Coverage Report

Created: 2025-12-10 07:58

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