GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
#include <cerrno> |
||
2 |
#include <cstdarg> |
||
3 |
|||
4 |
#include "debug_utils-inl.h" |
||
5 |
#include "node_errors.h" |
||
6 |
#include "node_external_reference.h" |
||
7 |
#include "node_internals.h" |
||
8 |
#include "node_process.h" |
||
9 |
#include "node_report.h" |
||
10 |
#include "node_v8_platform-inl.h" |
||
11 |
#include "util-inl.h" |
||
12 |
|||
13 |
namespace node { |
||
14 |
|||
15 |
using errors::TryCatchScope; |
||
16 |
using v8::Boolean; |
||
17 |
using v8::Context; |
||
18 |
using v8::Exception; |
||
19 |
using v8::Function; |
||
20 |
using v8::FunctionCallbackInfo; |
||
21 |
using v8::HandleScope; |
||
22 |
using v8::Int32; |
||
23 |
using v8::Isolate; |
||
24 |
using v8::Just; |
||
25 |
using v8::Local; |
||
26 |
using v8::Maybe; |
||
27 |
using v8::MaybeLocal; |
||
28 |
using v8::Message; |
||
29 |
using v8::Object; |
||
30 |
using v8::ScriptOrigin; |
||
31 |
using v8::StackFrame; |
||
32 |
using v8::StackTrace; |
||
33 |
using v8::String; |
||
34 |
using v8::Undefined; |
||
35 |
using v8::Value; |
||
36 |
|||
37 |
495 |
bool IsExceptionDecorated(Environment* env, Local<Value> er) { |
|
38 |
✓✗✓✓ ✓✓ |
990 |
if (!er.IsEmpty() && er->IsObject()) { |
39 |
484 |
Local<Object> err_obj = er.As<Object>(); |
|
40 |
auto maybe_value = |
||
41 |
968 |
err_obj->GetPrivate(env->context(), env->decorated_private_symbol()); |
|
42 |
Local<Value> decorated; |
||
43 |
✓✗✓✓ |
968 |
return maybe_value.ToLocal(&decorated) && decorated->IsTrue(); |
44 |
} |
||
45 |
11 |
return false; |
|
46 |
} |
||
47 |
|||
48 |
namespace per_process { |
||
49 |
4678 |
static Mutex tty_mutex; |
|
50 |
} // namespace per_process |
||
51 |
|||
52 |
904 |
static std::string GetErrorSource(Isolate* isolate, |
|
53 |
Local<Context> context, |
||
54 |
Local<Message> message, |
||
55 |
bool* added_exception_line) { |
||
56 |
904 |
MaybeLocal<String> source_line_maybe = message->GetSourceLine(context); |
|
57 |
1808 |
node::Utf8Value encoded_source(isolate, source_line_maybe.ToLocalChecked()); |
|
58 |
1808 |
std::string sourceline(*encoded_source, encoded_source.length()); |
|
59 |
904 |
*added_exception_line = false; |
|
60 |
|||
61 |
// If source maps have been enabled, the exception line will instead be |
||
62 |
// added in the JavaScript context: |
||
63 |
904 |
Environment* env = Environment::GetCurrent(isolate); |
|
64 |
const bool has_source_map_url = |
||
65 |
2712 |
!message->GetScriptOrigin().SourceMapUrl().IsEmpty(); |
|
66 |
✓✗✓✓ ✓✓ |
904 |
if (has_source_map_url && env->source_maps_enabled()) { |
67 |
12 |
return sourceline; |
|
68 |
} |
||
69 |
|||
70 |
✓✓ | 892 |
if (sourceline.find("node-do-not-add-exception-line") != std::string::npos) { |
71 |
8 |
return sourceline; |
|
72 |
} |
||
73 |
|||
74 |
// Because of how node modules work, all scripts are wrapped with a |
||
75 |
// "function (module, exports, __filename, ...) {" |
||
76 |
// to provide script local variables. |
||
77 |
// |
||
78 |
// When reporting errors on the first line of a script, this wrapper |
||
79 |
// function is leaked to the user. There used to be a hack here to |
||
80 |
// truncate off the first 62 characters, but it caused numerous other |
||
81 |
// problems when vm.runIn*Context() methods were used for non-module |
||
82 |
// code. |
||
83 |
// |
||
84 |
// If we ever decide to re-instate such a hack, the following steps |
||
85 |
// must be taken: |
||
86 |
// |
||
87 |
// 1. Pass a flag around to say "this code was wrapped" |
||
88 |
// 2. Update the stack frame output so that it is also correct. |
||
89 |
// |
||
90 |
// It would probably be simpler to add a line rather than add some |
||
91 |
// number of characters to the first line, since V8 truncates the |
||
92 |
// sourceline to 78 characters, and we end up not providing very much |
||
93 |
// useful debugging info to the user if we remove 62 characters. |
||
94 |
|||
95 |
// Print (filename):(line number): (message). |
||
96 |
884 |
ScriptOrigin origin = message->GetScriptOrigin(); |
|
97 |
1768 |
node::Utf8Value filename(isolate, message->GetScriptResourceName()); |
|
98 |
884 |
const char* filename_string = *filename; |
|
99 |
1768 |
int linenum = message->GetLineNumber(context).FromJust(); |
|
100 |
|||
101 |
3536 |
int script_start = (linenum - origin.ResourceLineOffset()->Value()) == 1 |
|
102 |
1210 |
? origin.ResourceColumnOffset()->Value() |
|
103 |
✓✓ | 1047 |
: 0; |
104 |
2652 |
int start = message->GetStartColumn(context).FromMaybe(0); |
|
105 |
2652 |
int end = message->GetEndColumn(context).FromMaybe(0); |
|
106 |
✓✓ | 884 |
if (start >= script_start) { |
107 |
✗✓ | 883 |
CHECK_GE(end, start); |
108 |
883 |
start -= script_start; |
|
109 |
883 |
end -= script_start; |
|
110 |
} |
||
111 |
|||
112 |
std::string buf = SPrintF("%s:%i\n%s\n", |
||
113 |
filename_string, |
||
114 |
linenum, |
||
115 |
1768 |
sourceline.c_str()); |
|
116 |
✗✓ | 884 |
CHECK_GT(buf.size(), 0); |
117 |
884 |
*added_exception_line = true; |
|
118 |
|||
119 |
✓✓✓✓ |
2652 |
if (start > end || |
120 |
✓✗✓✓ |
1767 |
start < 0 || |
121 |
883 |
static_cast<size_t>(end) > sourceline.size()) { |
|
122 |
18 |
return buf; |
|
123 |
} |
||
124 |
|||
125 |
866 |
constexpr int kUnderlineBufsize = 1020; |
|
126 |
char underline_buf[kUnderlineBufsize + 4]; |
||
127 |
866 |
int off = 0; |
|
128 |
// Print wavy underline (GetUnderline is deprecated). |
||
129 |
✓✓ | 5076 |
for (int i = 0; i < start; i++) { |
130 |
✓✗✗✓ ✗✓ |
4210 |
if (sourceline[i] == '\0' || off >= kUnderlineBufsize) { |
131 |
break; |
||
132 |
} |
||
133 |
✗✓ | 4210 |
CHECK_LT(off, kUnderlineBufsize); |
134 |
✓✓ | 4210 |
underline_buf[off++] = (sourceline[i] == '\t') ? '\t' : ' '; |
135 |
} |
||
136 |
✓✓ | 3445 |
for (int i = start; i < end; i++) { |
137 |
✓✗✗✓ ✗✓ |
2579 |
if (sourceline[i] == '\0' || off >= kUnderlineBufsize) { |
138 |
break; |
||
139 |
} |
||
140 |
✗✓ | 2579 |
CHECK_LT(off, kUnderlineBufsize); |
141 |
2579 |
underline_buf[off++] = '^'; |
|
142 |
} |
||
143 |
✗✓ | 866 |
CHECK_LE(off, kUnderlineBufsize); |
144 |
866 |
underline_buf[off++] = '\n'; |
|
145 |
|||
146 |
866 |
return buf + std::string(underline_buf, off); |
|
147 |
} |
||
148 |
|||
149 |
6 |
void PrintStackTrace(Isolate* isolate, Local<StackTrace> stack) { |
|
150 |
✓✓ | 96 |
for (int i = 0; i < stack->GetFrameCount(); i++) { |
151 |
84 |
Local<StackFrame> stack_frame = stack->GetFrame(isolate, i); |
|
152 |
126 |
node::Utf8Value fn_name_s(isolate, stack_frame->GetFunctionName()); |
|
153 |
✓✗ | 126 |
node::Utf8Value script_name(isolate, stack_frame->GetScriptName()); |
154 |
42 |
const int line_number = stack_frame->GetLineNumber(); |
|
155 |
42 |
const int column = stack_frame->GetColumn(); |
|
156 |
|||
157 |
✗✓ | 42 |
if (stack_frame->IsEval()) { |
158 |
if (stack_frame->GetScriptId() == Message::kNoScriptIdInfo) { |
||
159 |
FPrintF(stderr, " at [eval]:%i:%i\n", line_number, column); |
||
160 |
} else { |
||
161 |
FPrintF(stderr, |
||
162 |
" at [eval] (%s:%i:%i)\n", |
||
163 |
*script_name, |
||
164 |
line_number, |
||
165 |
column); |
||
166 |
} |
||
167 |
break; |
||
168 |
} |
||
169 |
|||
170 |
✓✓ | 42 |
if (fn_name_s.length() == 0) { |
171 |
11 |
FPrintF(stderr, " at %s:%i:%i\n", script_name, line_number, column); |
|
172 |
} else { |
||
173 |
FPrintF(stderr, |
||
174 |
" at %s (%s:%i:%i)\n", |
||
175 |
fn_name_s, |
||
176 |
script_name, |
||
177 |
line_number, |
||
178 |
✓✗ | 31 |
column); |
179 |
} |
||
180 |
} |
||
181 |
6 |
fflush(stderr); |
|
182 |
6 |
} |
|
183 |
|||
184 |
394 |
void PrintException(Isolate* isolate, |
|
185 |
Local<Context> context, |
||
186 |
Local<Value> err, |
||
187 |
Local<Message> message) { |
||
188 |
node::Utf8Value reason(isolate, |
||
189 |
788 |
err->ToDetailString(context) |
|
190 |
1182 |
.FromMaybe(Local<String>())); |
|
191 |
394 |
bool added_exception_line = false; |
|
192 |
std::string source = |
||
193 |
788 |
GetErrorSource(isolate, context, message, &added_exception_line); |
|
194 |
394 |
FPrintF(stderr, "%s\n", source); |
|
195 |
394 |
FPrintF(stderr, "%s\n", reason); |
|
196 |
|||
197 |
394 |
Local<v8::StackTrace> stack = message->GetStackTrace(); |
|
198 |
✗✓ | 394 |
if (!stack.IsEmpty()) PrintStackTrace(isolate, stack); |
199 |
394 |
} |
|
200 |
|||
201 |
394 |
void PrintCaughtException(Isolate* isolate, |
|
202 |
Local<Context> context, |
||
203 |
const v8::TryCatch& try_catch) { |
||
204 |
✗✓ | 394 |
CHECK(try_catch.HasCaught()); |
205 |
394 |
PrintException(isolate, context, try_catch.Exception(), try_catch.Message()); |
|
206 |
394 |
} |
|
207 |
|||
208 |
510 |
void AppendExceptionLine(Environment* env, |
|
209 |
Local<Value> er, |
||
210 |
Local<Message> message, |
||
211 |
enum ErrorHandlingMode mode) { |
||
212 |
✗✓ | 547 |
if (message.IsEmpty()) return; |
213 |
|||
214 |
983 |
HandleScope scope(env->isolate()); |
|
215 |
Local<Object> err_obj; |
||
216 |
✓✗✓✓ ✓✓ |
1020 |
if (!er.IsEmpty() && er->IsObject()) { |
217 |
499 |
err_obj = er.As<Object>(); |
|
218 |
} |
||
219 |
|||
220 |
510 |
bool added_exception_line = false; |
|
221 |
std::string source = GetErrorSource( |
||
222 |
✓✓ | 983 |
env->isolate(), env->context(), message, &added_exception_line); |
223 |
✓✓ | 510 |
if (!added_exception_line) { |
224 |
20 |
return; |
|
225 |
} |
||
226 |
490 |
MaybeLocal<Value> arrow_str = ToV8Value(env->context(), source); |
|
227 |
|||
228 |
✓✗✓✓ |
980 |
const bool can_set_arrow = !arrow_str.IsEmpty() && !err_obj.IsEmpty(); |
229 |
// If allocating arrow_str failed, print it out. There's not much else to do. |
||
230 |
// If it's not an error, but something needs to be printed out because |
||
231 |
// it's a fatal exception, also print it out from here. |
||
232 |
// Otherwise, the arrow property will be attached to the object and handled |
||
233 |
// by the caller. |
||
234 |
✓✓✓✓ ✓✓✓✓ |
666 |
if (!can_set_arrow || (mode == FATAL_ERROR && !err_obj->IsNativeError())) { |
235 |
✗✓ | 17 |
if (env->printed_error()) return; |
236 |
34 |
Mutex::ScopedLock lock(per_process::tty_mutex); |
|
237 |
17 |
env->set_printed_error(true); |
|
238 |
|||
239 |
17 |
ResetStdio(); |
|
240 |
17 |
FPrintF(stderr, "\n%s", source); |
|
241 |
17 |
return; |
|
242 |
} |
||
243 |
|||
244 |
✓✗✓✓ |
1892 |
CHECK(err_obj |
245 |
->SetPrivate(env->context(), |
||
246 |
env->arrow_message_private_symbol(), |
||
247 |
arrow_str.ToLocalChecked()) |
||
248 |
.FromMaybe(false)); |
||
249 |
} |
||
250 |
|||
251 |
[[noreturn]] void Abort() { |
||
252 |
DumpBacktrace(stderr); |
||
253 |
fflush(stderr); |
||
254 |
ABORT_NO_BACKTRACE(); |
||
255 |
} |
||
256 |
|||
257 |
[[noreturn]] void Assert(const AssertionInfo& info) { |
||
258 |
std::string name = GetHumanReadableProcessName(); |
||
259 |
|||
260 |
fprintf(stderr, |
||
261 |
"%s: %s:%s%s Assertion `%s' failed.\n", |
||
262 |
name.c_str(), |
||
263 |
info.file_line, |
||
264 |
info.function, |
||
265 |
*info.function ? ":" : "", |
||
266 |
info.message); |
||
267 |
fflush(stderr); |
||
268 |
|||
269 |
Abort(); |
||
270 |
} |
||
271 |
|||
272 |
enum class EnhanceFatalException { kEnhance, kDontEnhance }; |
||
273 |
|||
274 |
/** |
||
275 |
* Report the exception to the inspector, then print it to stderr. |
||
276 |
* This should only be used when the Node.js instance is about to exit |
||
277 |
* (i.e. this should be followed by a env->Exit() or an Abort()). |
||
278 |
* |
||
279 |
* Use enhance_stack = EnhanceFatalException::kDontEnhance |
||
280 |
* when it's unsafe to call into JavaScript. |
||
281 |
*/ |
||
282 |
207 |
static void ReportFatalException(Environment* env, |
|
283 |
Local<Value> error, |
||
284 |
Local<Message> message, |
||
285 |
EnhanceFatalException enhance_stack) { |
||
286 |
✗✓ | 207 |
if (!env->can_call_into_js()) |
287 |
enhance_stack = EnhanceFatalException::kDontEnhance; |
||
288 |
|||
289 |
207 |
Isolate* isolate = env->isolate(); |
|
290 |
✗✓ | 207 |
CHECK(!error.IsEmpty()); |
291 |
✗✓ | 207 |
CHECK(!message.IsEmpty()); |
292 |
414 |
HandleScope scope(isolate); |
|
293 |
|||
294 |
207 |
AppendExceptionLine(env, error, message, FATAL_ERROR); |
|
295 |
|||
296 |
207 |
auto report_to_inspector = [&]() { |
|
297 |
#if HAVE_INSPECTOR |
||
298 |
207 |
env->inspector_agent()->ReportUncaughtException(error, message); |
|
299 |
#endif |
||
300 |
414 |
}; |
|
301 |
|||
302 |
Local<Value> arrow; |
||
303 |
Local<Value> stack_trace; |
||
304 |
207 |
bool decorated = IsExceptionDecorated(env, error); |
|
305 |
|||
306 |
✓✓ | 207 |
if (!error->IsObject()) { // We can only enhance actual errors. |
307 |
11 |
report_to_inspector(); |
|
308 |
22 |
stack_trace = Undefined(isolate); |
|
309 |
// If error is not an object, AppendExceptionLine() has already print the |
||
310 |
// source line and the arrow to stderr. |
||
311 |
// TODO(joyeecheung): move that side effect out of AppendExceptionLine(). |
||
312 |
// It is done just to preserve the source line as soon as possible. |
||
313 |
} else { |
||
314 |
196 |
Local<Object> err_obj = error.As<Object>(); |
|
315 |
|||
316 |
392 |
auto enhance_with = [&](Local<Function> enhancer) { |
|
317 |
Local<Value> enhanced; |
||
318 |
784 |
Local<Value> argv[] = {err_obj}; |
|
319 |
✓✗✓✓ ✓✓ |
1176 |
if (!enhancer.IsEmpty() && |
320 |
enhancer |
||
321 |
1568 |
->Call(env->context(), Undefined(isolate), arraysize(argv), argv) |
|
322 |
392 |
.ToLocal(&enhanced)) { |
|
323 |
388 |
stack_trace = enhanced; |
|
324 |
} |
||
325 |
588 |
}; |
|
326 |
|||
327 |
✓✗✗ | 196 |
switch (enhance_stack) { |
328 |
case EnhanceFatalException::kEnhance: { |
||
329 |
196 |
enhance_with(env->enhance_fatal_stack_before_inspector()); |
|
330 |
196 |
report_to_inspector(); |
|
331 |
196 |
enhance_with(env->enhance_fatal_stack_after_inspector()); |
|
332 |
196 |
break; |
|
333 |
} |
||
334 |
case EnhanceFatalException::kDontEnhance: { |
||
335 |
USE(err_obj->Get(env->context(), env->stack_string()) |
||
336 |
.ToLocal(&stack_trace)); |
||
337 |
report_to_inspector(); |
||
338 |
break; |
||
339 |
} |
||
340 |
default: |
||
341 |
UNREACHABLE(); |
||
342 |
} |
||
343 |
|||
344 |
arrow = |
||
345 |
588 |
err_obj->GetPrivate(env->context(), env->arrow_message_private_symbol()) |
|
346 |
196 |
.ToLocalChecked(); |
|
347 |
} |
||
348 |
|||
349 |
414 |
node::Utf8Value trace(env->isolate(), stack_trace); |
|
350 |
|||
351 |
// range errors have a trace member set to undefined |
||
352 |
✓✓✓✓ ✓✓ |
617 |
if (trace.length() > 0 && !stack_trace->IsUndefined()) { |
353 |
✓✗✓✓ ✓✓✓✓ |
582 |
if (arrow.IsEmpty() || !arrow->IsString() || decorated) { |
354 |
75 |
FPrintF(stderr, "%s\n", trace); |
|
355 |
} else { |
||
356 |
238 |
node::Utf8Value arrow_string(env->isolate(), arrow); |
|
357 |
119 |
FPrintF(stderr, "%s\n%s\n", arrow_string, trace); |
|
358 |
} |
||
359 |
} else { |
||
360 |
// this really only happens for RangeErrors, since they're the only |
||
361 |
// kind that won't have all this info in the trace, or when non-Error |
||
362 |
// objects are thrown manually. |
||
363 |
MaybeLocal<Value> message; |
||
364 |
MaybeLocal<Value> name; |
||
365 |
|||
366 |
✓✓ | 13 |
if (error->IsObject()) { |
367 |
2 |
Local<Object> err_obj = error.As<Object>(); |
|
368 |
6 |
message = err_obj->Get(env->context(), env->message_string()); |
|
369 |
6 |
name = err_obj->Get(env->context(), env->name_string()); |
|
370 |
} |
||
371 |
|||
372 |
✗✓✗✗ ✓✗ |
45 |
if (message.IsEmpty() || message.ToLocalChecked()->IsUndefined() || |
373 |
✓✓✗✗ |
26 |
name.IsEmpty() || name.ToLocalChecked()->IsUndefined()) { |
374 |
// Not an error object. Just print as-is. |
||
375 |
26 |
node::Utf8Value message(env->isolate(), error); |
|
376 |
|||
377 |
FPrintF(stderr, "%s\n", |
||
378 |
✗✓✗✓ |
13 |
*message ? message.ToString() : "<toString() threw exception>"); |
379 |
} else { |
||
380 |
node::Utf8Value name_string(env->isolate(), name.ToLocalChecked()); |
||
381 |
node::Utf8Value message_string(env->isolate(), message.ToLocalChecked()); |
||
382 |
|||
383 |
if (arrow.IsEmpty() || !arrow->IsString() || decorated) { |
||
384 |
FPrintF(stderr, "%s: %s\n", name_string, message_string); |
||
385 |
} else { |
||
386 |
node::Utf8Value arrow_string(env->isolate(), arrow); |
||
387 |
FPrintF(stderr, |
||
388 |
"%s\n%s: %s\n", arrow_string, name_string, message_string); |
||
389 |
} |
||
390 |
} |
||
391 |
|||
392 |
✓✓ | 13 |
if (!env->options()->trace_uncaught) { |
393 |
20 |
std::string argv0; |
|
394 |
✓✗ | 10 |
if (!env->argv().empty()) argv0 = env->argv()[0]; |
395 |
✗✓ | 10 |
if (argv0.empty()) argv0 = "node"; |
396 |
FPrintF(stderr, |
||
397 |
"(Use `%s --trace-uncaught ...` to show where the exception " |
||
398 |
"was thrown)\n", |
||
399 |
10 |
fs::Basename(argv0, ".exe")); |
|
400 |
} |
||
401 |
} |
||
402 |
|||
403 |
✓✓ | 207 |
if (env->options()->trace_uncaught) { |
404 |
3 |
Local<StackTrace> trace = message->GetStackTrace(); |
|
405 |
✓✗ | 3 |
if (!trace.IsEmpty()) { |
406 |
3 |
FPrintF(stderr, "Thrown at:\n"); |
|
407 |
3 |
PrintStackTrace(env->isolate(), trace); |
|
408 |
} |
||
409 |
} |
||
410 |
|||
411 |
207 |
fflush(stderr); |
|
412 |
207 |
} |
|
413 |
|||
414 |
[[noreturn]] void FatalError(const char* location, const char* message) { |
||
415 |
OnFatalError(location, message); |
||
416 |
// to suppress compiler warning |
||
417 |
ABORT(); |
||
418 |
} |
||
419 |
|||
420 |
void OnFatalError(const char* location, const char* message) { |
||
421 |
if (location) { |
||
422 |
FPrintF(stderr, "FATAL ERROR: %s %s\n", location, message); |
||
423 |
} else { |
||
424 |
FPrintF(stderr, "FATAL ERROR: %s\n", message); |
||
425 |
} |
||
426 |
|||
427 |
Isolate* isolate = Isolate::GetCurrent(); |
||
428 |
Environment* env = Environment::GetCurrent(isolate); |
||
429 |
bool report_on_fatalerror; |
||
430 |
{ |
||
431 |
Mutex::ScopedLock lock(node::per_process::cli_options_mutex); |
||
432 |
report_on_fatalerror = per_process::cli_options->report_on_fatalerror; |
||
433 |
} |
||
434 |
|||
435 |
if (report_on_fatalerror) { |
||
436 |
report::TriggerNodeReport( |
||
437 |
isolate, env, message, "FatalError", "", Local<Object>()); |
||
438 |
} |
||
439 |
|||
440 |
fflush(stderr); |
||
441 |
ABORT(); |
||
442 |
} |
||
443 |
|||
444 |
namespace errors { |
||
445 |
|||
446 |
2789765 |
TryCatchScope::~TryCatchScope() { |
|
447 |
✓✓✓✓ ✓✓✓✓ |
1394880 |
if (HasCaught() && !HasTerminated() && mode_ == CatchMode::kFatal) { |
448 |
5 |
HandleScope scope(env_->isolate()); |
|
449 |
5 |
Local<v8::Value> exception = Exception(); |
|
450 |
5 |
Local<v8::Message> message = Message(); |
|
451 |
✓✗ | 5 |
EnhanceFatalException enhance = CanContinue() ? |
452 |
5 |
EnhanceFatalException::kEnhance : EnhanceFatalException::kDontEnhance; |
|
453 |
✗✓ | 5 |
if (message.IsEmpty()) |
454 |
message = Exception::CreateMessage(env_->isolate(), exception); |
||
455 |
5 |
ReportFatalException(env_, exception, message, enhance); |
|
456 |
5 |
env_->Exit(7); |
|
457 |
} |
||
458 |
1394853 |
} |
|
459 |
|||
460 |
4 |
const char* errno_string(int errorno) { |
|
461 |
#define ERRNO_CASE(e) \ |
||
462 |
case e: \ |
||
463 |
return #e; |
||
464 |
✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✗✗✗✗ ✗✗✓✗ ✗✗✗✗ ✗ |
4 |
switch (errorno) { |
465 |
#ifdef EACCES |
||
466 |
ERRNO_CASE(EACCES); |
||
467 |
#endif |
||
468 |
|||
469 |
#ifdef EADDRINUSE |
||
470 |
ERRNO_CASE(EADDRINUSE); |
||
471 |
#endif |
||
472 |
|||
473 |
#ifdef EADDRNOTAVAIL |
||
474 |
ERRNO_CASE(EADDRNOTAVAIL); |
||
475 |
#endif |
||
476 |
|||
477 |
#ifdef EAFNOSUPPORT |
||
478 |
ERRNO_CASE(EAFNOSUPPORT); |
||
479 |
#endif |
||
480 |
|||
481 |
#ifdef EAGAIN |
||
482 |
ERRNO_CASE(EAGAIN); |
||
483 |
#endif |
||
484 |
|||
485 |
#ifdef EWOULDBLOCK |
||
486 |
#if EAGAIN != EWOULDBLOCK |
||
487 |
ERRNO_CASE(EWOULDBLOCK); |
||
488 |
#endif |
||
489 |
#endif |
||
490 |
|||
491 |
#ifdef EALREADY |
||
492 |
ERRNO_CASE(EALREADY); |
||
493 |
#endif |
||
494 |
|||
495 |
#ifdef EBADF |
||
496 |
ERRNO_CASE(EBADF); |
||
497 |
#endif |
||
498 |
|||
499 |
#ifdef EBADMSG |
||
500 |
ERRNO_CASE(EBADMSG); |
||
501 |
#endif |
||
502 |
|||
503 |
#ifdef EBUSY |
||
504 |
ERRNO_CASE(EBUSY); |
||
505 |
#endif |
||
506 |
|||
507 |
#ifdef ECANCELED |
||
508 |
ERRNO_CASE(ECANCELED); |
||
509 |
#endif |
||
510 |
|||
511 |
#ifdef ECHILD |
||
512 |
1 |
ERRNO_CASE(ECHILD); |
|
513 |
#endif |
||
514 |
|||
515 |
#ifdef ECONNABORTED |
||
516 |
ERRNO_CASE(ECONNABORTED); |
||
517 |
#endif |
||
518 |
|||
519 |
#ifdef ECONNREFUSED |
||
520 |
ERRNO_CASE(ECONNREFUSED); |
||
521 |
#endif |
||
522 |
|||
523 |
#ifdef ECONNRESET |
||
524 |
ERRNO_CASE(ECONNRESET); |
||
525 |
#endif |
||
526 |
|||
527 |
#ifdef EDEADLK |
||
528 |
ERRNO_CASE(EDEADLK); |
||
529 |
#endif |
||
530 |
|||
531 |
#ifdef EDESTADDRREQ |
||
532 |
ERRNO_CASE(EDESTADDRREQ); |
||
533 |
#endif |
||
534 |
|||
535 |
#ifdef EDOM |
||
536 |
ERRNO_CASE(EDOM); |
||
537 |
#endif |
||
538 |
|||
539 |
#ifdef EDQUOT |
||
540 |
ERRNO_CASE(EDQUOT); |
||
541 |
#endif |
||
542 |
|||
543 |
#ifdef EEXIST |
||
544 |
ERRNO_CASE(EEXIST); |
||
545 |
#endif |
||
546 |
|||
547 |
#ifdef EFAULT |
||
548 |
ERRNO_CASE(EFAULT); |
||
549 |
#endif |
||
550 |
|||
551 |
#ifdef EFBIG |
||
552 |
ERRNO_CASE(EFBIG); |
||
553 |
#endif |
||
554 |
|||
555 |
#ifdef EHOSTUNREACH |
||
556 |
ERRNO_CASE(EHOSTUNREACH); |
||
557 |
#endif |
||
558 |
|||
559 |
#ifdef EIDRM |
||
560 |
ERRNO_CASE(EIDRM); |
||
561 |
#endif |
||
562 |
|||
563 |
#ifdef EILSEQ |
||
564 |
ERRNO_CASE(EILSEQ); |
||
565 |
#endif |
||
566 |
|||
567 |
#ifdef EINPROGRESS |
||
568 |
ERRNO_CASE(EINPROGRESS); |
||
569 |
#endif |
||
570 |
|||
571 |
#ifdef EINTR |
||
572 |
ERRNO_CASE(EINTR); |
||
573 |
#endif |
||
574 |
|||
575 |
#ifdef EINVAL |
||
576 |
ERRNO_CASE(EINVAL); |
||
577 |
#endif |
||
578 |
|||
579 |
#ifdef EIO |
||
580 |
ERRNO_CASE(EIO); |
||
581 |
#endif |
||
582 |
|||
583 |
#ifdef EISCONN |
||
584 |
ERRNO_CASE(EISCONN); |
||
585 |
#endif |
||
586 |
|||
587 |
#ifdef EISDIR |
||
588 |
ERRNO_CASE(EISDIR); |
||
589 |
#endif |
||
590 |
|||
591 |
#ifdef ELOOP |
||
592 |
ERRNO_CASE(ELOOP); |
||
593 |
#endif |
||
594 |
|||
595 |
#ifdef EMFILE |
||
596 |
ERRNO_CASE(EMFILE); |
||
597 |
#endif |
||
598 |
|||
599 |
#ifdef EMLINK |
||
600 |
ERRNO_CASE(EMLINK); |
||
601 |
#endif |
||
602 |
|||
603 |
#ifdef EMSGSIZE |
||
604 |
ERRNO_CASE(EMSGSIZE); |
||
605 |
#endif |
||
606 |
|||
607 |
#ifdef EMULTIHOP |
||
608 |
ERRNO_CASE(EMULTIHOP); |
||
609 |
#endif |
||
610 |
|||
611 |
#ifdef ENAMETOOLONG |
||
612 |
ERRNO_CASE(ENAMETOOLONG); |
||
613 |
#endif |
||
614 |
|||
615 |
#ifdef ENETDOWN |
||
616 |
ERRNO_CASE(ENETDOWN); |
||
617 |
#endif |
||
618 |
|||
619 |
#ifdef ENETRESET |
||
620 |
ERRNO_CASE(ENETRESET); |
||
621 |
#endif |
||
622 |
|||
623 |
#ifdef ENETUNREACH |
||
624 |
ERRNO_CASE(ENETUNREACH); |
||
625 |
#endif |
||
626 |
|||
627 |
#ifdef ENFILE |
||
628 |
ERRNO_CASE(ENFILE); |
||
629 |
#endif |
||
630 |
|||
631 |
#ifdef ENOBUFS |
||
632 |
ERRNO_CASE(ENOBUFS); |
||
633 |
#endif |
||
634 |
|||
635 |
#ifdef ENODATA |
||
636 |
ERRNO_CASE(ENODATA); |
||
637 |
#endif |
||
638 |
|||
639 |
#ifdef ENODEV |
||
640 |
ERRNO_CASE(ENODEV); |
||
641 |
#endif |
||
642 |
|||
643 |
#ifdef ENOENT |
||
644 |
ERRNO_CASE(ENOENT); |
||
645 |
#endif |
||
646 |
|||
647 |
#ifdef ENOEXEC |
||
648 |
ERRNO_CASE(ENOEXEC); |
||
649 |
#endif |
||
650 |
|||
651 |
#ifdef ENOLINK |
||
652 |
ERRNO_CASE(ENOLINK); |
||
653 |
#endif |
||
654 |
|||
655 |
#ifdef ENOLCK |
||
656 |
#if ENOLINK != ENOLCK |
||
657 |
ERRNO_CASE(ENOLCK); |
||
658 |
#endif |
||
659 |
#endif |
||
660 |
|||
661 |
#ifdef ENOMEM |
||
662 |
ERRNO_CASE(ENOMEM); |
||
663 |
#endif |
||
664 |
|||
665 |
#ifdef ENOMSG |
||
666 |
ERRNO_CASE(ENOMSG); |
||
667 |
#endif |
||
668 |
|||
669 |
#ifdef ENOPROTOOPT |
||
670 |
ERRNO_CASE(ENOPROTOOPT); |
||
671 |
#endif |
||
672 |
|||
673 |
#ifdef ENOSPC |
||
674 |
ERRNO_CASE(ENOSPC); |
||
675 |
#endif |
||
676 |
|||
677 |
#ifdef ENOSR |
||
678 |
ERRNO_CASE(ENOSR); |
||
679 |
#endif |
||
680 |
|||
681 |
#ifdef ENOSTR |
||
682 |
ERRNO_CASE(ENOSTR); |
||
683 |
#endif |
||
684 |
|||
685 |
#ifdef ENOSYS |
||
686 |
ERRNO_CASE(ENOSYS); |
||
687 |
#endif |
||
688 |
|||
689 |
#ifdef ENOTCONN |
||
690 |
ERRNO_CASE(ENOTCONN); |
||
691 |
#endif |
||
692 |
|||
693 |
#ifdef ENOTDIR |
||
694 |
ERRNO_CASE(ENOTDIR); |
||
695 |
#endif |
||
696 |
|||
697 |
#ifdef ENOTEMPTY |
||
698 |
#if ENOTEMPTY != EEXIST |
||
699 |
ERRNO_CASE(ENOTEMPTY); |
||
700 |
#endif |
||
701 |
#endif |
||
702 |
|||
703 |
#ifdef ENOTSOCK |
||
704 |
ERRNO_CASE(ENOTSOCK); |
||
705 |
#endif |
||
706 |
|||
707 |
#ifdef ENOTSUP |
||
708 |
ERRNO_CASE(ENOTSUP); |
||
709 |
#else |
||
710 |
#ifdef EOPNOTSUPP |
||
711 |
ERRNO_CASE(EOPNOTSUPP); |
||
712 |
#endif |
||
713 |
#endif |
||
714 |
|||
715 |
#ifdef ENOTTY |
||
716 |
ERRNO_CASE(ENOTTY); |
||
717 |
#endif |
||
718 |
|||
719 |
#ifdef ENXIO |
||
720 |
ERRNO_CASE(ENXIO); |
||
721 |
#endif |
||
722 |
|||
723 |
#ifdef EOVERFLOW |
||
724 |
ERRNO_CASE(EOVERFLOW); |
||
725 |
#endif |
||
726 |
|||
727 |
#ifdef EPERM |
||
728 |
2 |
ERRNO_CASE(EPERM); |
|
729 |
#endif |
||
730 |
|||
731 |
#ifdef EPIPE |
||
732 |
ERRNO_CASE(EPIPE); |
||
733 |
#endif |
||
734 |
|||
735 |
#ifdef EPROTO |
||
736 |
ERRNO_CASE(EPROTO); |
||
737 |
#endif |
||
738 |
|||
739 |
#ifdef EPROTONOSUPPORT |
||
740 |
ERRNO_CASE(EPROTONOSUPPORT); |
||
741 |
#endif |
||
742 |
|||
743 |
#ifdef EPROTOTYPE |
||
744 |
ERRNO_CASE(EPROTOTYPE); |
||
745 |
#endif |
||
746 |
|||
747 |
#ifdef ERANGE |
||
748 |
ERRNO_CASE(ERANGE); |
||
749 |
#endif |
||
750 |
|||
751 |
#ifdef EROFS |
||
752 |
ERRNO_CASE(EROFS); |
||
753 |
#endif |
||
754 |
|||
755 |
#ifdef ESPIPE |
||
756 |
ERRNO_CASE(ESPIPE); |
||
757 |
#endif |
||
758 |
|||
759 |
#ifdef ESRCH |
||
760 |
1 |
ERRNO_CASE(ESRCH); |
|
761 |
#endif |
||
762 |
|||
763 |
#ifdef ESTALE |
||
764 |
ERRNO_CASE(ESTALE); |
||
765 |
#endif |
||
766 |
|||
767 |
#ifdef ETIME |
||
768 |
ERRNO_CASE(ETIME); |
||
769 |
#endif |
||
770 |
|||
771 |
#ifdef ETIMEDOUT |
||
772 |
ERRNO_CASE(ETIMEDOUT); |
||
773 |
#endif |
||
774 |
|||
775 |
#ifdef ETXTBSY |
||
776 |
ERRNO_CASE(ETXTBSY); |
||
777 |
#endif |
||
778 |
|||
779 |
#ifdef EXDEV |
||
780 |
ERRNO_CASE(EXDEV); |
||
781 |
#endif |
||
782 |
|||
783 |
default: |
||
784 |
return ""; |
||
785 |
} |
||
786 |
} |
||
787 |
|||
788 |
1388 |
void PerIsolateMessageListener(Local<Message> message, Local<Value> error) { |
|
789 |
1388 |
Isolate* isolate = message->GetIsolate(); |
|
790 |
✓✓✗ | 1388 |
switch (message->ErrorLevel()) { |
791 |
case Isolate::MessageErrorLevel::kMessageWarning: { |
||
792 |
1 |
Environment* env = Environment::GetCurrent(isolate); |
|
793 |
✗✓ | 1 |
if (!env) { |
794 |
break; |
||
795 |
} |
||
796 |
3 |
Utf8Value filename(isolate, message->GetScriptOrigin().ResourceName()); |
|
797 |
// (filename):(line) (message) |
||
798 |
2 |
std::stringstream warning; |
|
799 |
1 |
warning << *filename; |
|
800 |
1 |
warning << ":"; |
|
801 |
3 |
warning << message->GetLineNumber(env->context()).FromMaybe(-1); |
|
802 |
1 |
warning << " "; |
|
803 |
3 |
v8::String::Utf8Value msg(isolate, message->Get()); |
|
804 |
1 |
warning << *msg; |
|
805 |
1 |
USE(ProcessEmitWarningGeneric(env, warning.str().c_str(), "V8")); |
|
806 |
1 |
break; |
|
807 |
} |
||
808 |
case Isolate::MessageErrorLevel::kMessageError: |
||
809 |
1387 |
TriggerUncaughtException(isolate, error, message); |
|
810 |
1215 |
break; |
|
811 |
} |
||
812 |
1216 |
} |
|
813 |
|||
814 |
453 |
void SetPrepareStackTraceCallback(const FunctionCallbackInfo<Value>& args) { |
|
815 |
453 |
Environment* env = Environment::GetCurrent(args); |
|
816 |
✗✓ | 906 |
CHECK(args[0]->IsFunction()); |
817 |
906 |
env->set_prepare_stack_trace_callback(args[0].As<Function>()); |
|
818 |
453 |
} |
|
819 |
|||
820 |
15 |
static void EnableSourceMaps(const FunctionCallbackInfo<Value>& args) { |
|
821 |
15 |
Environment* env = Environment::GetCurrent(args); |
|
822 |
15 |
env->set_source_maps_enabled(true); |
|
823 |
15 |
} |
|
824 |
|||
825 |
438 |
static void SetEnhanceStackForFatalException( |
|
826 |
const FunctionCallbackInfo<Value>& args) { |
||
827 |
438 |
Environment* env = Environment::GetCurrent(args); |
|
828 |
✗✓ | 876 |
CHECK(args[0]->IsFunction()); |
829 |
✗✓ | 876 |
CHECK(args[1]->IsFunction()); |
830 |
876 |
env->set_enhance_fatal_stack_before_inspector(args[0].As<Function>()); |
|
831 |
876 |
env->set_enhance_fatal_stack_after_inspector(args[1].As<Function>()); |
|
832 |
438 |
} |
|
833 |
|||
834 |
// Side effect-free stringification that will never throw exceptions. |
||
835 |
8 |
static void NoSideEffectsToString(const FunctionCallbackInfo<Value>& args) { |
|
836 |
8 |
Local<Context> context = args.GetIsolate()->GetCurrentContext(); |
|
837 |
Local<String> detail_string; |
||
838 |
✓✗ | 24 |
if (args[0]->ToDetailString(context).ToLocal(&detail_string)) |
839 |
16 |
args.GetReturnValue().Set(detail_string); |
|
840 |
8 |
} |
|
841 |
|||
842 |
43 |
static void TriggerUncaughtException(const FunctionCallbackInfo<Value>& args) { |
|
843 |
43 |
Isolate* isolate = args.GetIsolate(); |
|
844 |
43 |
Environment* env = Environment::GetCurrent(isolate); |
|
845 |
43 |
Local<Value> exception = args[0]; |
|
846 |
43 |
Local<Message> message = Exception::CreateMessage(isolate, exception); |
|
847 |
✓✗✗✓ ✗✓ |
43 |
if (env != nullptr && env->abort_on_uncaught_exception()) { |
848 |
ReportFatalException( |
||
849 |
env, exception, message, EnhanceFatalException::kEnhance); |
||
850 |
Abort(); |
||
851 |
} |
||
852 |
86 |
bool from_promise = args[1]->IsTrue(); |
|
853 |
43 |
errors::TriggerUncaughtException(isolate, exception, message, from_promise); |
|
854 |
11 |
} |
|
855 |
|||
856 |
4601 |
void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
|
857 |
4601 |
registry->Register(SetPrepareStackTraceCallback); |
|
858 |
4601 |
registry->Register(EnableSourceMaps); |
|
859 |
4601 |
registry->Register(SetEnhanceStackForFatalException); |
|
860 |
4601 |
registry->Register(NoSideEffectsToString); |
|
861 |
4601 |
registry->Register(TriggerUncaughtException); |
|
862 |
4601 |
} |
|
863 |
|||
864 |
438 |
void Initialize(Local<Object> target, |
|
865 |
Local<Value> unused, |
||
866 |
Local<Context> context, |
||
867 |
void* priv) { |
||
868 |
438 |
Environment* env = Environment::GetCurrent(context); |
|
869 |
env->SetMethod( |
||
870 |
438 |
target, "setPrepareStackTraceCallback", SetPrepareStackTraceCallback); |
|
871 |
438 |
env->SetMethod(target, "enableSourceMaps", EnableSourceMaps); |
|
872 |
env->SetMethod(target, |
||
873 |
"setEnhanceStackForFatalException", |
||
874 |
438 |
SetEnhanceStackForFatalException); |
|
875 |
env->SetMethodNoSideEffect( |
||
876 |
438 |
target, "noSideEffectsToString", NoSideEffectsToString); |
|
877 |
438 |
env->SetMethod(target, "triggerUncaughtException", TriggerUncaughtException); |
|
878 |
438 |
} |
|
879 |
|||
880 |
294 |
void DecorateErrorStack(Environment* env, |
|
881 |
const errors::TryCatchScope& try_catch) { |
||
882 |
294 |
Local<Value> exception = try_catch.Exception(); |
|
883 |
|||
884 |
✓✓ | 305 |
if (!exception->IsObject()) return; |
885 |
|||
886 |
288 |
Local<Object> err_obj = exception.As<Object>(); |
|
887 |
|||
888 |
✓✓ | 288 |
if (IsExceptionDecorated(env, err_obj)) return; |
889 |
|||
890 |
285 |
AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR); |
|
891 |
568 |
TryCatchScope try_catch_scope(env); // Ignore exceptions below. |
|
892 |
855 |
MaybeLocal<Value> stack = err_obj->Get(env->context(), env->stack_string()); |
|
893 |
MaybeLocal<Value> maybe_value = |
||
894 |
570 |
err_obj->GetPrivate(env->context(), env->arrow_message_private_symbol()); |
|
895 |
|||
896 |
Local<Value> arrow; |
||
897 |
✓✗✗✓ ✗✓ |
855 |
if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) { |
898 |
return; |
||
899 |
} |
||
900 |
|||
901 |
✓✗✓✓ ✓✓ |
1140 |
if (stack.IsEmpty() || !stack.ToLocalChecked()->IsString()) { |
902 |
2 |
return; |
|
903 |
} |
||
904 |
|||
905 |
Local<String> decorated_stack = String::Concat( |
||
906 |
env->isolate(), |
||
907 |
String::Concat(env->isolate(), |
||
908 |
arrow.As<String>(), |
||
909 |
FIXED_ONE_BYTE_STRING(env->isolate(), "\n")), |
||
910 |
849 |
stack.ToLocalChecked().As<String>()); |
|
911 |
849 |
USE(err_obj->Set(env->context(), env->stack_string(), decorated_stack)); |
|
912 |
err_obj->SetPrivate( |
||
913 |
✓✓ | 849 |
env->context(), env->decorated_private_symbol(), True(env->isolate())); |
914 |
} |
||
915 |
|||
916 |
1439 |
void TriggerUncaughtException(Isolate* isolate, |
|
917 |
Local<Value> error, |
||
918 |
Local<Message> message, |
||
919 |
bool from_promise) { |
||
920 |
✗✓ | 1439 |
CHECK(!error.IsEmpty()); |
921 |
1440 |
HandleScope scope(isolate); |
|
922 |
|||
923 |
✓✓ | 1439 |
if (message.IsEmpty()) message = Exception::CreateMessage(isolate, error); |
924 |
|||
925 |
✗✓ | 1439 |
CHECK(isolate->InContext()); |
926 |
1439 |
Local<Context> context = isolate->GetCurrentContext(); |
|
927 |
1439 |
Environment* env = Environment::GetCurrent(context); |
|
928 |
✗✓ | 1439 |
if (env == nullptr) { |
929 |
// This means that the exception happens before Environment is assigned |
||
930 |
// to the context e.g. when there is a SyntaxError in a per-context |
||
931 |
// script - which usually indicates that there is a bug because no JS |
||
932 |
// error is supposed to be thrown at this point. |
||
933 |
// Since we don't have access to Environment here, there is not |
||
934 |
// much we can do, so we just print whatever is useful and crash. |
||
935 |
PrintException(isolate, context, error, message); |
||
936 |
Abort(); |
||
937 |
} |
||
938 |
|||
939 |
// Invoke process._fatalException() to give user a chance to handle it. |
||
940 |
// We have to grab it from the process object since this has been |
||
941 |
// monkey-patchable. |
||
942 |
1439 |
Local<Object> process_object = env->process_object(); |
|
943 |
1439 |
Local<String> fatal_exception_string = env->fatal_exception_string(); |
|
944 |
Local<Value> fatal_exception_function = |
||
945 |
2878 |
process_object->Get(env->context(), |
|
946 |
5756 |
fatal_exception_string).ToLocalChecked(); |
|
947 |
// If the exception happens before process._fatalException is attached |
||
948 |
// during bootstrap, or if the user has patched it incorrectly, exit |
||
949 |
// the current Node.js instance. |
||
950 |
✗✓ | 1439 |
if (!fatal_exception_function->IsFunction()) { |
951 |
ReportFatalException( |
||
952 |
env, error, message, EnhanceFatalException::kDontEnhance); |
||
953 |
env->Exit(6); |
||
954 |
return; |
||
955 |
} |
||
956 |
|||
957 |
MaybeLocal<Value> handled; |
||
958 |
✓✓ | 1439 |
if (env->can_call_into_js()) { |
959 |
// We do not expect the global uncaught exception itself to throw any more |
||
960 |
// exceptions. If it does, exit the current Node.js instance. |
||
961 |
errors::TryCatchScope try_catch(env, |
||
962 |
2864 |
errors::TryCatchScope::CatchMode::kFatal); |
|
963 |
// Explicitly disable verbose exception reporting - |
||
964 |
// if process._fatalException() throws an error, we don't want it to |
||
965 |
// trigger the per-isolate message listener which will call this |
||
966 |
// function and recurse. |
||
967 |
1433 |
try_catch.SetVerbose(false); |
|
968 |
Local<Value> argv[2] = { error, |
||
969 |
2866 |
Boolean::New(env->isolate(), from_promise) }; |
|
970 |
|||
971 |
2864 |
handled = fatal_exception_function.As<Function>()->Call( |
|
972 |
5730 |
env->context(), process_object, arraysize(argv), argv); |
|
973 |
} |
||
974 |
|||
975 |
// If process._fatalException() throws, we are now exiting the Node.js |
||
976 |
// instance so return to continue the exit routine. |
||
977 |
// TODO(joyeecheung): return a Maybe here to prevent the caller from |
||
978 |
// stepping on the exit. |
||
979 |
✓✓ | 1432 |
if (handled.IsEmpty()) { |
980 |
7 |
return; |
|
981 |
} |
||
982 |
|||
983 |
// The global uncaught exception handler returns true if the user handles it |
||
984 |
// by e.g. listening to `uncaughtException`. In that case, continue program |
||
985 |
// execution. |
||
986 |
// TODO(joyeecheung): This has been only checking that the return value is |
||
987 |
// exactly false. Investigate whether this can be turned to an "if true" |
||
988 |
// similar to how the worker global uncaught exception handler handles it. |
||
989 |
✓✓ | 2850 |
if (!handled.ToLocalChecked()->IsFalse()) { |
990 |
1223 |
return; |
|
991 |
} |
||
992 |
|||
993 |
// Now we are certain that the exception is fatal. |
||
994 |
202 |
ReportFatalException(env, error, message, EnhanceFatalException::kEnhance); |
|
995 |
202 |
RunAtExit(env); |
|
996 |
|||
997 |
// If the global uncaught exception handler sets process.exitCode, |
||
998 |
// exit with that code. Otherwise, exit with 1. |
||
999 |
202 |
Local<String> exit_code = env->exit_code_string(); |
|
1000 |
Local<Value> code; |
||
1001 |
✓✗✓✓ ✓✓ |
808 |
if (process_object->Get(env->context(), exit_code).ToLocal(&code) && |
1002 |
202 |
code->IsInt32()) { |
|
1003 |
402 |
env->Exit(code.As<Int32>()->Value()); |
|
1004 |
} else { |
||
1005 |
✓✓ | 1 |
env->Exit(1); |
1006 |
} |
||
1007 |
} |
||
1008 |
|||
1009 |
6 |
void TriggerUncaughtException(Isolate* isolate, const v8::TryCatch& try_catch) { |
|
1010 |
// If the try_catch is verbose, the per-isolate message listener is going to |
||
1011 |
// handle it (which is going to call into another overload of |
||
1012 |
// TriggerUncaughtException()). |
||
1013 |
✗✓ | 6 |
if (try_catch.IsVerbose()) { |
1014 |
return; |
||
1015 |
} |
||
1016 |
|||
1017 |
// If the user calls TryCatch::TerminateExecution() on this TryCatch |
||
1018 |
// they must call CancelTerminateExecution() again before invoking |
||
1019 |
// TriggerUncaughtException() because it will invoke |
||
1020 |
// process._fatalException() in the JS land. |
||
1021 |
✗✓ | 6 |
CHECK(!try_catch.HasTerminated()); |
1022 |
✗✓ | 6 |
CHECK(try_catch.HasCaught()); |
1023 |
9 |
HandleScope scope(isolate); |
|
1024 |
6 |
TriggerUncaughtException(isolate, |
|
1025 |
try_catch.Exception(), |
||
1026 |
try_catch.Message(), |
||
1027 |
6 |
false /* from_promise */); |
|
1028 |
} |
||
1029 |
|||
1030 |
} // namespace errors |
||
1031 |
|||
1032 |
} // namespace node |
||
1033 |
|||
1034 |
4670 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(errors, node::errors::Initialize) |
|
1035 |
✓✗✓✗ |
18635 |
NODE_MODULE_EXTERNAL_REFERENCE(errors, node::errors::RegisterExternalReferences) |
Generated by: GCOVR (Version 3.4) |