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