xref: /third_party/node/src/node_errors.cc (revision 1cb0ef41)
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