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