Coverage Report

Created: 2025-07-04 09:33

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