GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../src/node_errors.cc Lines: 154 186 82.8 %
Date: 2019-01-07 12:15:22 Branches: 133 182 73.1 %

Line Branch Exec Source
1
#include <stdarg.h>
2
#include "node_errors.h"
3
#include "node_internals.h"
4
5
namespace node {
6
7
using v8::Context;
8
using v8::Exception;
9
using v8::Function;
10
using v8::FunctionCallbackInfo;
11
using v8::HandleScope;
12
using v8::Int32;
13
using v8::Isolate;
14
using v8::Just;
15
using v8::Local;
16
using v8::Maybe;
17
using v8::MaybeLocal;
18
using v8::Message;
19
using v8::NewStringType;
20
using v8::Number;
21
using v8::Object;
22
using v8::ScriptOrigin;
23
using v8::String;
24
using v8::Undefined;
25
using v8::Value;
26
27
496
bool IsExceptionDecorated(Environment* env, Local<Value> er) {
28

992
  if (!er.IsEmpty() && er->IsObject()) {
29
490
    Local<Object> err_obj = er.As<Object>();
30
    auto maybe_value =
31
980
        err_obj->GetPrivate(env->context(), env->decorated_private_symbol());
32
    Local<Value> decorated;
33

980
    return maybe_value.ToLocal(&decorated) && decorated->IsTrue();
34
  }
35
6
  return false;
36
}
37
38
498
void AppendExceptionLine(Environment* env,
39
                         Local<Value> er,
40
                         Local<Message> message,
41
                         enum ErrorHandlingMode mode) {
42
508
  if (message.IsEmpty()) return;
43
44
498
  HandleScope scope(env->isolate());
45
  Local<Object> err_obj;
46

996
  if (!er.IsEmpty() && er->IsObject()) {
47
492
    err_obj = er.As<Object>();
48
  }
49
50
  // Print (filename):(line number): (message).
51
498
  ScriptOrigin origin = message->GetScriptOrigin();
52
986
  node::Utf8Value filename(env->isolate(), message->GetScriptResourceName());
53
498
  const char* filename_string = *filename;
54
1494
  int linenum = message->GetLineNumber(env->context()).FromJust();
55
  // Print line of source code.
56
996
  MaybeLocal<String> source_line_maybe = message->GetSourceLine(env->context());
57
  node::Utf8Value sourceline(env->isolate(),
58
986
                             source_line_maybe.ToLocalChecked());
59
498
  const char* sourceline_string = *sourceline;
60
498
  if (strstr(sourceline_string, "node-do-not-add-exception-line") != nullptr)
61
    return;
62
63
  // Because of how node modules work, all scripts are wrapped with a
64
  // "function (module, exports, __filename, ...) {"
65
  // to provide script local variables.
66
  //
67
  // When reporting errors on the first line of a script, this wrapper
68
  // function is leaked to the user. There used to be a hack here to
69
  // truncate off the first 62 characters, but it caused numerous other
70
  // problems when vm.runIn*Context() methods were used for non-module
71
  // code.
72
  //
73
  // If we ever decide to re-instate such a hack, the following steps
74
  // must be taken:
75
  //
76
  // 1. Pass a flag around to say "this code was wrapped"
77
  // 2. Update the stack frame output so that it is also correct.
78
  //
79
  // It would probably be simpler to add a line rather than add some
80
  // number of characters to the first line, since V8 truncates the
81
  // sourceline to 78 characters, and we end up not providing very much
82
  // useful debugging info to the user if we remove 62 characters.
83
84
1992
  int script_start = (linenum - origin.ResourceLineOffset()->Value()) == 1
85
736
                         ? origin.ResourceColumnOffset()->Value()
86
736
                         : 0;
87
1494
  int start = message->GetStartColumn(env->context()).FromMaybe(0);
88
1494
  int end = message->GetEndColumn(env->context()).FromMaybe(0);
89
498
  if (start >= script_start) {
90
498
    CHECK_GE(end, start);
91
498
    start -= script_start;
92
498
    end -= script_start;
93
  }
94
95
  char arrow[1024];
96
498
  int max_off = sizeof(arrow) - 2;
97
98
  int off = snprintf(arrow,
99
                     sizeof(arrow),
100
                     "%s:%i\n%s\n",
101
                     filename_string,
102
                     linenum,
103
498
                     sourceline_string);
104
498
  CHECK_GE(off, 0);
105
498
  if (off > max_off) {
106
1
    off = max_off;
107
  }
108
109
  // Print wavy underline (GetUnderline is deprecated).
110
3651
  for (int i = 0; i < start; i++) {
111

3154
    if (sourceline_string[i] == '\0' || off >= max_off) {
112
      break;
113
    }
114
3153
    CHECK_LT(off, max_off);
115
3153
    arrow[off++] = (sourceline_string[i] == '\t') ? '\t' : ' ';
116
  }
117
1382
  for (int i = start; i < end; i++) {
118

906
    if (sourceline_string[i] == '\0' || off >= max_off) {
119
      break;
120
    }
121
884
    CHECK_LT(off, max_off);
122
884
    arrow[off++] = '^';
123
  }
124
498
  CHECK_LE(off, max_off);
125
498
  arrow[off] = '\n';
126
498
  arrow[off + 1] = '\0';
127
128
  Local<String> arrow_str =
129
498
      String::NewFromUtf8(env->isolate(), arrow, NewStringType::kNormal)
130
996
          .ToLocalChecked();
131
132

996
  const bool can_set_arrow = !arrow_str.IsEmpty() && !err_obj.IsEmpty();
133
  // If allocating arrow_str failed, print it out. There's not much else to do.
134
  // If it's not an error, but something needs to be printed out because
135
  // it's a fatal exception, also print it out from here.
136
  // Otherwise, the arrow property will be attached to the object and handled
137
  // by the caller.
138


616
  if (!can_set_arrow || (mode == FATAL_ERROR && !err_obj->IsNativeError())) {
139
10
    if (env->printed_error()) return;
140
10
    Mutex::ScopedLock lock(process_mutex);
141
10
    env->set_printed_error(true);
142
143
10
    uv_tty_reset_mode();
144
10
    PrintErrorString("\n%s", arrow);
145
10
    return;
146
  }
147
148

1952
  CHECK(err_obj
149
            ->SetPrivate(
150
                env->context(), env->arrow_message_private_symbol(), arrow_str)
151
488
            .FromMaybe(false));
152
}
153
154
[[noreturn]] void Abort() {
155
  DumpBacktrace(stderr);
156
  fflush(stderr);
157
  ABORT_NO_BACKTRACE();
158
}
159
160
[[noreturn]] void Assert(const char* const (*args)[4]) {
161
  auto filename = (*args)[0];
162
  auto linenum = (*args)[1];
163
  auto message = (*args)[2];
164
  auto function = (*args)[3];
165
166
  char name[1024];
167
  GetHumanReadableProcessName(&name);
168
169
  fprintf(stderr,
170
          "%s: %s:%s:%s%s Assertion `%s' failed.\n",
171
          name,
172
          filename,
173
          linenum,
174
          function,
175
          *function ? ":" : "",
176
          message);
177
  fflush(stderr);
178
179
  Abort();
180
}
181
182
124
void ReportException(Environment* env,
183
                     Local<Value> er,
184
                     Local<Message> message) {
185
124
  CHECK(!er.IsEmpty());
186
124
  HandleScope scope(env->isolate());
187
188
124
  if (message.IsEmpty()) message = Exception::CreateMessage(env->isolate(), er);
189
190
124
  AppendExceptionLine(env, er, message, FATAL_ERROR);
191
192
  Local<Value> trace_value;
193
  Local<Value> arrow;
194
124
  const bool decorated = IsExceptionDecorated(env, er);
195
196

494
  if (er->IsUndefined() || er->IsNull()) {
197
4
    trace_value = Undefined(env->isolate());
198
  } else {
199
366
    Local<Object> err_obj = er->ToObject(env->context()).ToLocalChecked();
200
201
    trace_value = err_obj->Get(env->context(),
202
488
                               env->stack_string()).ToLocalChecked();
203
    arrow =
204
244
        err_obj->GetPrivate(env->context(), env->arrow_message_private_symbol())
205
244
            .ToLocalChecked();
206
  }
207
208
248
  node::Utf8Value trace(env->isolate(), trace_value);
209
210
  // range errors have a trace member set to undefined
211

372
  if (trace.length() > 0 && !trace_value->IsUndefined()) {
212


342
    if (arrow.IsEmpty() || !arrow->IsString() || decorated) {
213
47
      PrintErrorString("%s\n", *trace);
214
    } else {
215
67
      node::Utf8Value arrow_string(env->isolate(), arrow);
216
67
      PrintErrorString("%s\n%s\n", *arrow_string, *trace);
217
    }
218
  } else {
219
    // this really only happens for RangeErrors, since they're the only
220
    // kind that won't have all this info in the trace, or when non-Error
221
    // objects are thrown manually.
222
    Local<Value> message;
223
    Local<Value> name;
224
225
10
    if (er->IsObject()) {
226
4
      Local<Object> err_obj = er.As<Object>();
227
      message = err_obj->Get(env->context(),
228
16
                             env->message_string()).ToLocalChecked();
229
      name = err_obj->Get(env->context(),
230
16
          FIXED_ONE_BYTE_STRING(env->isolate(), "name")).ToLocalChecked();
231
    }
232
233


20
    if (message.IsEmpty() || message->IsUndefined() || name.IsEmpty() ||
234
2
        name->IsUndefined()) {
235
      // Not an error object. Just print as-is.
236
9
      String::Utf8Value message(env->isolate(), er);
237
238
      PrintErrorString("%s\n",
239
9
                       *message ? *message : "<toString() threw exception>");
240
    } else {
241
1
      node::Utf8Value name_string(env->isolate(), name);
242
2
      node::Utf8Value message_string(env->isolate(), message);
243
244


3
      if (arrow.IsEmpty() || !arrow->IsString() || decorated) {
245
1
        PrintErrorString("%s: %s\n", *name_string, *message_string);
246
      } else {
247
        node::Utf8Value arrow_string(env->isolate(), arrow);
248
        PrintErrorString(
249
            "%s\n%s: %s\n", *arrow_string, *name_string, *message_string);
250
1
      }
251
    }
252
  }
253
254
124
  fflush(stderr);
255
256
#if HAVE_INSPECTOR
257
248
  env->inspector_agent()->FatalException(er, message);
258
#endif
259
124
}
260
261
4
void ReportException(Environment* env, const v8::TryCatch& try_catch) {
262
4
  ReportException(env, try_catch.Exception(), try_catch.Message());
263
4
}
264
265
143
void PrintErrorString(const char* format, ...) {
266
  va_list ap;
267
143
  va_start(ap, format);
268
#ifdef _WIN32
269
  HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
270
271
  // Check if stderr is something other than a tty/console
272
  if (stderr_handle == INVALID_HANDLE_VALUE || stderr_handle == nullptr ||
273
      uv_guess_handle(_fileno(stderr)) != UV_TTY) {
274
    vfprintf(stderr, format, ap);
275
    va_end(ap);
276
    return;
277
  }
278
279
  // Fill in any placeholders
280
  int n = _vscprintf(format, ap);
281
  std::vector<char> out(n + 1);
282
  vsprintf(out.data(), format, ap);
283
284
  // Get required wide buffer size
285
  n = MultiByteToWideChar(CP_UTF8, 0, out.data(), -1, nullptr, 0);
286
287
  std::vector<wchar_t> wbuf(n);
288
  MultiByteToWideChar(CP_UTF8, 0, out.data(), -1, wbuf.data(), n);
289
290
  // Don't include the null character in the output
291
  CHECK_GT(n, 0);
292
  WriteConsoleW(stderr_handle, wbuf.data(), n - 1, nullptr, nullptr);
293
#else
294
143
  vfprintf(stderr, format, ap);
295
#endif
296
143
  va_end(ap);
297
143
}
298
299
[[noreturn]] void FatalError(const char* location, const char* message) {
300
  OnFatalError(location, message);
301
  // to suppress compiler warning
302
  ABORT();
303
}
304
305
void OnFatalError(const char* location, const char* message) {
306
  if (location) {
307
    PrintErrorString("FATAL ERROR: %s %s\n", location, message);
308
  } else {
309
    PrintErrorString("FATAL ERROR: %s\n", message);
310
  }
311
  fflush(stderr);
312
  ABORT();
313
}
314
315
namespace errors {
316
317
380466
TryCatchScope::~TryCatchScope() {
318


190233
  if (HasCaught() && !HasTerminated() && mode_ == CatchMode::kFatal) {
319
    HandleScope scope(env_->isolate());
320
    ReportException(env_, Exception(), Message());
321
    exit(7);
322
  }
323
190232
}
324
325
}  // namespace errors
326
327
378
void DecorateErrorStack(Environment* env,
328
                        const errors::TryCatchScope& try_catch) {
329
378
  Local<Value> exception = try_catch.Exception();
330
331
388
  if (!exception->IsObject()) return;
332
333
372
  Local<Object> err_obj = exception.As<Object>();
334
335
372
  if (IsExceptionDecorated(env, err_obj)) return;
336
337
369
  AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR);
338
  Local<Value> stack =
339
1476
      err_obj->Get(env->context(), env->stack_string()).ToLocalChecked();
340
  MaybeLocal<Value> maybe_value =
341
738
      err_obj->GetPrivate(env->context(), env->arrow_message_private_symbol());
342
343
  Local<Value> arrow;
344

1107
  if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) {
345
    return;
346
  }
347
348

1107
  if (stack.IsEmpty() || !stack->IsString()) {
349
1
    return;
350
  }
351
352
  Local<String> decorated_stack = String::Concat(
353
      env->isolate(),
354
      String::Concat(env->isolate(),
355
                     arrow.As<String>(),
356
                     FIXED_ONE_BYTE_STRING(env->isolate(), "\n")),
357
736
      stack.As<String>());
358
1472
  err_obj->Set(env->context(), env->stack_string(), decorated_stack).FromJust();
359
  err_obj->SetPrivate(
360
1104
      env->context(), env->decorated_private_symbol(), True(env->isolate()));
361
}
362
363
1301
void FatalException(Isolate* isolate,
364
                    Local<Value> error,
365
                    Local<Message> message) {
366
1301
  HandleScope scope(isolate);
367
368
1301
  Environment* env = Environment::GetCurrent(isolate);
369
1301
  CHECK_NOT_NULL(env);  // TODO(addaleax): Handle nullptr here.
370
1301
  Local<Object> process_object = env->process_object();
371
1301
  Local<String> fatal_exception_string = env->fatal_exception_string();
372
  Local<Value> fatal_exception_function =
373
      process_object->Get(env->context(),
374
3903
                          fatal_exception_string).ToLocalChecked();
375
376
1301
  if (!fatal_exception_function->IsFunction()) {
377
    // Failed before the process._fatalException function was added!
378
    // this is probably pretty bad.  Nothing to do but report and exit.
379
    ReportException(env, error, message);
380
    exit(6);
381
  } else {
382
1301
    errors::TryCatchScope fatal_try_catch(env);
383
384
    // Do not call FatalException when _fatalException handler throws
385
1301
    fatal_try_catch.SetVerbose(false);
386
387
    // This will return true if the JS layer handled it, false otherwise
388
    MaybeLocal<Value> caught = fatal_exception_function.As<Function>()->Call(
389
3903
        env->context(), process_object, 1, &error);
390
391
2474
    if (fatal_try_catch.HasTerminated()) return;
392
393
1299
    if (fatal_try_catch.HasCaught()) {
394
      // The fatal exception function threw, so we must exit
395
4
      ReportException(env, fatal_try_catch);
396
4
      exit(7);
397
398
2590
    } else if (caught.ToLocalChecked()->IsFalse()) {
399
120
      ReportException(env, error, message);
400
401
      // fatal_exception_function call before may have set a new exit code ->
402
      // read it again, otherwise use default for uncaughtException 1
403
120
      Local<String> exit_code = env->exit_code_string();
404
      Local<Value> code;
405


600
      if (!process_object->Get(env->context(), exit_code).ToLocal(&code) ||
406
120
          !code->IsInt32()) {
407
1
        exit(1);
408
      }
409
238
      exit(code.As<Int32>()->Value());
410
1175
    }
411
1175
  }
412
}
413
414
3
void FatalException(Isolate* isolate, const v8::TryCatch& try_catch) {
415
  // If we try to print out a termination exception, we'd just get 'null',
416
  // so just crashing here with that information seems like a better idea,
417
  // and in particular it seems like we should handle terminations at the call
418
  // site for this function rather than by printing them out somewhere.
419
3
  CHECK(!try_catch.HasTerminated());
420
421
3
  HandleScope scope(isolate);
422
3
  if (!try_catch.IsVerbose()) {
423
3
    FatalException(isolate, try_catch.Exception(), try_catch.Message());
424
2
  }
425
2
}
426
427
2
void FatalException(const FunctionCallbackInfo<Value>& args) {
428
2
  Isolate* isolate = args.GetIsolate();
429
2
  Environment* env = Environment::GetCurrent(isolate);
430

2
  if (env != nullptr && env->abort_on_uncaught_exception()) {
431
    Abort();
432
  }
433
2
  Local<Value> exception = args[0];
434
2
  Local<Message> message = Exception::CreateMessage(isolate, exception);
435
2
  FatalException(isolate, exception, message);
436
2
}
437
438
}  // namespace node