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 13namespace node { 14 15using errors::TryCatchScope; 16using v8::Boolean; 17using v8::Context; 18using v8::Exception; 19using v8::Function; 20using v8::FunctionCallbackInfo; 21using v8::HandleScope; 22using v8::Int32; 23using v8::Isolate; 24using v8::Just; 25using v8::Local; 26using v8::Maybe; 27using v8::MaybeLocal; 28using v8::Message; 29using v8::Object; 30using v8::ScriptOrigin; 31using v8::StackFrame; 32using v8::StackTrace; 33using v8::String; 34using v8::Undefined; 35using v8::Value; 36 37bool 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 48namespace per_process { 49static Mutex tty_mutex; 50} // namespace per_process 51 52static 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 86static 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 188static 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 237static void PrintToStderrAndFlush(const std::string& str) { 238 FPrintF(stderr, "%s\n", str); 239 fflush(stderr); 240} 241 242void PrintStackTrace(Isolate* isolate, Local<StackTrace> stack) { 243 PrintToStderrAndFlush(FormatStackTrace(isolate, stack)); 244} 245 246std::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 263std::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 271void PrintCaughtException(Isolate* isolate, 272 Local<Context> context, 273 const v8::TryCatch& try_catch) { 274 PrintToStderrAndFlush(FormatCaughtException(isolate, context, try_catch)); 275} 276 277void 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 326[[noreturn]] void Abort() { 327 DumpBacktrace(stderr); 328 fflush(stderr); 329 ABORT_NO_BACKTRACE(); 330} 331 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 347enum 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 */ 357static 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 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 524[[noreturn]] void OOMErrorHandler(const char* location, const v8::OOMDetails& details) { 525 const char* message = 526 details.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 554v8::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 586namespace errors { 587 588TryCatchScope::~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 602const 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 930void 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 956void SetPrepareStackTraceCallback(const FunctionCallbackInfo<Value>& args) { 957 Realm* realm = Realm::GetCurrent(args); 958 CHECK(args[0]->IsFunction()); 959 realm->set_prepare_stack_trace_callback(args[0].As<Function>()); 960} 961 962static 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 968static 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 975static 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 982static void SetEnhanceStackForFatalException( 983 const FunctionCallbackInfo<Value>& args) { 984 Realm* realm = Realm::GetCurrent(args); 985 CHECK(args[0]->IsFunction()); 986 CHECK(args[1]->IsFunction()); 987 realm->set_enhance_fatal_stack_before_inspector(args[0].As<Function>()); 988 realm->set_enhance_fatal_stack_after_inspector(args[1].As<Function>()); 989} 990 991// Side effect-free stringification that will never throw exceptions. 992static 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 999static 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 1013void 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 1023void 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 1050void 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 1086void 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 1181void 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 1206NODE_BINDING_CONTEXT_AWARE_INTERNAL(errors, node::errors::Initialize) 1207NODE_BINDING_EXTERNAL_REFERENCE(errors, 1208 node::errors::RegisterExternalReferences) 1209