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