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