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