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 |
574 |
bool IsExceptionDecorated(Environment* env, Local<Value> er) { |
|
38 |
✓✗✓✓ ✓✓ |
1148 |
if (!er.IsEmpty() && er->IsObject()) { |
39 |
559 |
Local<Object> err_obj = er.As<Object>(); |
|
40 |
auto maybe_value = |
||
41 |
559 |
err_obj->GetPrivate(env->context(), env->decorated_private_symbol()); |
|
42 |
Local<Value> decorated; |
||
43 |
✓✗✓✓ |
1118 |
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 |
948 |
static std::string GetErrorSource(Isolate* isolate, |
|
87 |
Local<Context> context, |
||
88 |
Local<Message> message, |
||
89 |
bool* added_exception_line) { |
||
90 |
948 |
MaybeLocal<String> source_line_maybe = message->GetSourceLine(context); |
|
91 |
1896 |
node::Utf8Value encoded_source(isolate, source_line_maybe.ToLocalChecked()); |
|
92 |
1896 |
std::string sourceline(*encoded_source, encoded_source.length()); |
|
93 |
948 |
*added_exception_line = false; |
|
94 |
|||
95 |
✗✓ | 948 |
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 |
948 |
Environment* env = Environment::GetCurrent(isolate); |
|
102 |
const bool has_source_map_url = |
||
103 |
✓✗ | 2844 |
!message->GetScriptOrigin().SourceMapUrl().IsEmpty() && |
104 |
✓✓ | 2844 |
!message->GetScriptOrigin().SourceMapUrl()->IsUndefined(); |
105 |
✓✓✓✗ ✓✓✓✓ |
948 |
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 |
932 |
ScriptOrigin origin = message->GetScriptOrigin(); |
|
136 |
1864 |
node::Utf8Value filename(isolate, message->GetScriptResourceName()); |
|
137 |
932 |
const char* filename_string = *filename; |
|
138 |
932 |
int linenum = message->GetLineNumber(context).FromJust(); |
|
139 |
|||
140 |
932 |
int script_start = (linenum - origin.LineOffset()) == 1 |
|
141 |
✓✓ | 1097 |
? origin.ColumnOffset() |
142 |
932 |
: 0; |
|
143 |
✓✗ | 1864 |
int start = message->GetStartColumn(context).FromMaybe(0); |
144 |
✓✗ | 932 |
int end = message->GetEndColumn(context).FromMaybe(0); |
145 |
✓✓ | 932 |
if (start >= script_start) { |
146 |
✗✓ | 931 |
CHECK_GE(end, start); |
147 |
931 |
start -= script_start; |
|
148 |
931 |
end -= script_start; |
|
149 |
} |
||
150 |
|||
151 |
std::string buf = SPrintF("%s:%i\n%s\n", |
||
152 |
filename_string, |
||
153 |
linenum, |
||
154 |
1864 |
sourceline.c_str()); |
|
155 |
✗✓ | 932 |
CHECK_GT(buf.size(), 0); |
156 |
932 |
*added_exception_line = true; |
|
157 |
|||
158 |
2796 |
if (start > end || |
|
159 |
✓✗✓✓ ✓✓✓✓ |
1863 |
start < 0 || |
160 |
931 |
static_cast<size_t>(end) > sourceline.size()) { |
|
161 |
18 |
return buf; |
|
162 |
} |
||
163 |
|||
164 |
914 |
constexpr int kUnderlineBufsize = 1020; |
|
165 |
char underline_buf[kUnderlineBufsize + 4]; |
||
166 |
914 |
int off = 0; |
|
167 |
// Print wavy underline (GetUnderline is deprecated). |
||
168 |
✓✓ | 5344 |
for (int i = 0; i < start; i++) { |
169 |
✓✗✗✓ ✗✓ |
4430 |
if (sourceline[i] == '\0' || off >= kUnderlineBufsize) { |
170 |
break; |
||
171 |
} |
||
172 |
✗✓ | 4430 |
CHECK_LT(off, kUnderlineBufsize); |
173 |
✓✓ | 4430 |
underline_buf[off++] = (sourceline[i] == '\t') ? '\t' : ' '; |
174 |
} |
||
175 |
✓✓ | 3515 |
for (int i = start; i < end; i++) { |
176 |
✓✗✗✓ ✗✓ |
2601 |
if (sourceline[i] == '\0' || off >= kUnderlineBufsize) { |
177 |
break; |
||
178 |
} |
||
179 |
✗✓ | 2601 |
CHECK_LT(off, kUnderlineBufsize); |
180 |
2601 |
underline_buf[off++] = '^'; |
|
181 |
} |
||
182 |
✗✓ | 914 |
CHECK_LE(off, kUnderlineBufsize); |
183 |
914 |
underline_buf[off++] = '\n'; |
|
184 |
|||
185 |
914 |
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 |
425 |
void PrintException(Isolate* isolate, |
|
224 |
Local<Context> context, |
||
225 |
Local<Value> err, |
||
226 |
Local<Message> message) { |
||
227 |
node::Utf8Value reason(isolate, |
||
228 |
425 |
err->ToDetailString(context) |
|
229 |
850 |
.FromMaybe(Local<String>())); |
|
230 |
425 |
bool added_exception_line = false; |
|
231 |
std::string source = |
||
232 |
850 |
GetErrorSource(isolate, context, message, &added_exception_line); |
|
233 |
425 |
FPrintF(stderr, "%s\n", source); |
|
234 |
425 |
FPrintF(stderr, "%s\n", reason); |
|
235 |
|||
236 |
425 |
Local<v8::StackTrace> stack = message->GetStackTrace(); |
|
237 |
✓✓ | 425 |
if (!stack.IsEmpty()) PrintStackTrace(isolate, stack); |
238 |
425 |
} |
|
239 |
|||
240 |
425 |
void PrintCaughtException(Isolate* isolate, |
|
241 |
Local<Context> context, |
||
242 |
const v8::TryCatch& try_catch) { |
||
243 |
✗✓ | 425 |
CHECK(try_catch.HasCaught()); |
244 |
425 |
PrintException(isolate, context, try_catch.Exception(), try_catch.Message()); |
|
245 |
425 |
} |
|
246 |
|||
247 |
592 |
void AppendExceptionLine(Environment* env, |
|
248 |
Local<Value> er, |
||
249 |
Local<Message> message, |
||
250 |
enum ErrorHandlingMode mode) { |
||
251 |
✗✓ | 683 |
if (message.IsEmpty()) return; |
252 |
|||
253 |
592 |
HandleScope scope(env->isolate()); |
|
254 |
Local<Object> err_obj; |
||
255 |
✓✗✓✓ ✓✓ |
1184 |
if (!er.IsEmpty() && er->IsObject()) { |
256 |
577 |
err_obj = er.As<Object>(); |
|
257 |
// If arrow_message is already set, skip. |
||
258 |
auto maybe_value = err_obj->GetPrivate(env->context(), |
||
259 |
577 |
env->arrow_message_private_symbol()); |
|
260 |
Local<Value> lvalue; |
||
261 |
✓✗✓✓ ✓✓ |
1731 |
if (!maybe_value.ToLocal(&lvalue) || lvalue->IsString()) |
262 |
69 |
return; |
|
263 |
} |
||
264 |
|||
265 |
523 |
bool added_exception_line = false; |
|
266 |
std::string source = GetErrorSource( |
||
267 |
523 |
env->isolate(), env->context(), message, &added_exception_line); |
|
268 |
✗✓ | 523 |
if (!added_exception_line) { |
269 |
return; |
||
270 |
} |
||
271 |
523 |
MaybeLocal<Value> arrow_str = ToV8Value(env->context(), source); |
|
272 |
|||
273 |
✓✗✓✓ |
1046 |
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 |
✓✓✓✓ ✓✓✓✓ |
713 |
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 |
✗✓✓✗ ✗✓ |
1503 |
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 |
274 |
static void ReportFatalException(Environment* env, |
|
328 |
Local<Value> error, |
||
329 |
Local<Message> message, |
||
330 |
EnhanceFatalException enhance_stack) { |
||
331 |
✗✓ | 274 |
if (!env->can_call_into_js()) |
332 |
enhance_stack = EnhanceFatalException::kDontEnhance; |
||
333 |
|||
334 |
274 |
Isolate* isolate = env->isolate(); |
|
335 |
✗✓ | 274 |
CHECK(!error.IsEmpty()); |
336 |
✗✓ | 274 |
CHECK(!message.IsEmpty()); |
337 |
548 |
HandleScope scope(isolate); |
|
338 |
|||
339 |
274 |
AppendExceptionLine(env, error, message, FATAL_ERROR); |
|
340 |
|||
341 |
274 |
auto report_to_inspector = [&]() { |
|
342 |
#if HAVE_INSPECTOR |
||
343 |
274 |
env->inspector_agent()->ReportUncaughtException(error, message); |
|
344 |
#endif |
||
345 |
274 |
}; |
|
346 |
|||
347 |
Local<Value> arrow; |
||
348 |
Local<Value> stack_trace; |
||
349 |
274 |
bool decorated = IsExceptionDecorated(env, error); |
|
350 |
|||
351 |
✓✓ | 274 |
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 |
259 |
Local<Object> err_obj = error.As<Object>(); |
|
360 |
|||
361 |
514 |
auto enhance_with = [&](Local<Function> enhancer) { |
|
362 |
Local<Value> enhanced; |
||
363 |
514 |
Local<Value> argv[] = {err_obj}; |
|
364 |
✓✗ | 1028 |
if (!enhancer.IsEmpty() && |
365 |
enhancer |
||
366 |
✓✓ | 1542 |
->Call(env->context(), Undefined(isolate), arraysize(argv), argv) |
367 |
✓✓ | 514 |
.ToLocal(&enhanced)) { |
368 |
510 |
stack_trace = enhanced; |
|
369 |
} |
||
370 |
773 |
}; |
|
371 |
|||
372 |
✓✓✗ | 259 |
switch (enhance_stack) { |
373 |
257 |
case EnhanceFatalException::kEnhance: { |
|
374 |
257 |
enhance_with(env->enhance_fatal_stack_before_inspector()); |
|
375 |
257 |
report_to_inspector(); |
|
376 |
257 |
enhance_with(env->enhance_fatal_stack_after_inspector()); |
|
377 |
257 |
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 |
259 |
err_obj->GetPrivate(env->context(), env->arrow_message_private_symbol()) |
|
391 |
259 |
.ToLocalChecked(); |
|
392 |
} |
||
393 |
|||
394 |
548 |
node::Utf8Value trace(env->isolate(), stack_trace); |
|
395 |
548 |
std::string report_message = "Exception"; |
|
396 |
|||
397 |
// range errors have a trace member set to undefined |
||
398 |
✓✓✓✓ ✓✓ |
818 |
if (trace.length() > 0 && !stack_trace->IsUndefined()) { |
399 |
✓✗✓✓ ✓✓✓✓ |
771 |
if (arrow.IsEmpty() || !arrow->IsString() || decorated) { |
400 |
67 |
FPrintF(stderr, "%s\n", trace); |
|
401 |
} else { |
||
402 |
380 |
node::Utf8Value arrow_string(env->isolate(), arrow); |
|
403 |
190 |
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 |
✓✓ | 274 |
if (env->isolate_data()->options()->report_uncaught_exception) { |
452 |
4 |
TriggerNodeReport(env, report_message.c_str(), "Exception", "", error); |
|
453 |
} |
||
454 |
|||
455 |
✓✓ | 274 |
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 |
✓✓ | 274 |
if (env->options()->extra_info_on_fatal_exception) { |
464 |
273 |
FPrintF(stderr, "\nNode.js %s\n", NODE_VERSION); |
|
465 |
} |
||
466 |
|||
467 |
274 |
fflush(stderr); |
|
468 |
274 |
} |
|
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 |
// Trigger report with the isolate. Environment::GetCurrent may return |
||
517 |
// nullptr here: |
||
518 |
// - If the OOM is reported by a young generation space allocation, |
||
519 |
// Isolate::GetCurrentContext returns an empty handle. |
||
520 |
// - Otherwise, Isolate::GetCurrentContext returns a non-empty handle. |
||
521 |
TriggerNodeReport(isolate, message, "OOMError", "", Local<Object>()); |
||
522 |
} |
||
523 |
|||
524 |
fflush(stderr); |
||
525 |
ABORT(); |
||
526 |
} |
||
527 |
|||
528 |
1379 |
v8::ModifyCodeGenerationFromStringsResult ModifyCodeGenerationFromStrings( |
|
529 |
v8::Local<v8::Context> context, |
||
530 |
v8::Local<v8::Value> source, |
||
531 |
bool is_code_like) { |
||
532 |
2758 |
HandleScope scope(context->GetIsolate()); |
|
533 |
|||
534 |
1379 |
Environment* env = Environment::GetCurrent(context); |
|
535 |
✓✓ | 1379 |
if (env->source_maps_enabled()) { |
536 |
// We do not expect the maybe_cache_generated_source_map to throw any more |
||
537 |
// exceptions. If it does, just ignore it. |
||
538 |
4 |
errors::TryCatchScope try_catch(env); |
|
539 |
Local<Function> maybe_cache_source_map = |
||
540 |
2 |
env->maybe_cache_generated_source_map(); |
|
541 |
2 |
Local<Value> argv[1] = {source}; |
|
542 |
|||
543 |
MaybeLocal<Value> maybe_cached = maybe_cache_source_map->Call( |
||
544 |
4 |
context, context->Global(), arraysize(argv), argv); |
|
545 |
2 |
if (maybe_cached.IsEmpty()) { |
|
546 |
DCHECK(try_catch.HasCaught()); |
||
547 |
} |
||
548 |
} |
||
549 |
|||
550 |
Local<Value> allow_code_gen = context->GetEmbedderData( |
||
551 |
2758 |
ContextEmbedderIndex::kAllowCodeGenerationFromStrings); |
|
552 |
bool codegen_allowed = |
||
553 |
✓✗✓✓ |
4137 |
allow_code_gen->IsUndefined() || allow_code_gen->IsTrue(); |
554 |
return { |
||
555 |
codegen_allowed, |
||
556 |
{}, |
||
557 |
1379 |
}; |
|
558 |
} |
||
559 |
|||
560 |
namespace errors { |
||
561 |
|||
562 |
1124456 |
TryCatchScope::~TryCatchScope() { |
|
563 |
✓✓✓✓ ✓✓✓✓ |
1124462 |
if (HasCaught() && !HasTerminated() && mode_ == CatchMode::kFatal) { |
564 |
6 |
HandleScope scope(env_->isolate()); |
|
565 |
6 |
Local<v8::Value> exception = Exception(); |
|
566 |
6 |
Local<v8::Message> message = Message(); |
|
567 |
✓✗ | 6 |
EnhanceFatalException enhance = CanContinue() ? |
568 |
6 |
EnhanceFatalException::kEnhance : EnhanceFatalException::kDontEnhance; |
|
569 |
✗✓ | 6 |
if (message.IsEmpty()) |
570 |
message = Exception::CreateMessage(env_->isolate(), exception); |
||
571 |
6 |
ReportFatalException(env_, exception, message, enhance); |
|
572 |
6 |
env_->Exit(7); |
|
573 |
} |
||
574 |
1124456 |
} |
|
575 |
|||
576 |
8 |
const char* errno_string(int errorno) { |
|
577 |
#define ERRNO_CASE(e) \ |
||
578 |
case e: \ |
||
579 |
return #e; |
||
580 |
✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✗✗✗✗ ✗✗✓✗ ✗✗✗✗ ✗ |
8 |
switch (errorno) { |
581 |
#ifdef EACCES |
||
582 |
ERRNO_CASE(EACCES); |
||
583 |
#endif |
||
584 |
|||
585 |
#ifdef EADDRINUSE |
||
586 |
ERRNO_CASE(EADDRINUSE); |
||
587 |
#endif |
||
588 |
|||
589 |
#ifdef EADDRNOTAVAIL |
||
590 |
ERRNO_CASE(EADDRNOTAVAIL); |
||
591 |
#endif |
||
592 |
|||
593 |
#ifdef EAFNOSUPPORT |
||
594 |
ERRNO_CASE(EAFNOSUPPORT); |
||
595 |
#endif |
||
596 |
|||
597 |
#ifdef EAGAIN |
||
598 |
ERRNO_CASE(EAGAIN); |
||
599 |
#endif |
||
600 |
|||
601 |
#ifdef EWOULDBLOCK |
||
602 |
#if EAGAIN != EWOULDBLOCK |
||
603 |
ERRNO_CASE(EWOULDBLOCK); |
||
604 |
#endif |
||
605 |
#endif |
||
606 |
|||
607 |
#ifdef EALREADY |
||
608 |
ERRNO_CASE(EALREADY); |
||
609 |
#endif |
||
610 |
|||
611 |
#ifdef EBADF |
||
612 |
ERRNO_CASE(EBADF); |
||
613 |
#endif |
||
614 |
|||
615 |
#ifdef EBADMSG |
||
616 |
ERRNO_CASE(EBADMSG); |
||
617 |
#endif |
||
618 |
|||
619 |
#ifdef EBUSY |
||
620 |
ERRNO_CASE(EBUSY); |
||
621 |
#endif |
||
622 |
|||
623 |
#ifdef ECANCELED |
||
624 |
ERRNO_CASE(ECANCELED); |
||
625 |
#endif |
||
626 |
|||
627 |
#ifdef ECHILD |
||
628 |
1 |
ERRNO_CASE(ECHILD); |
|
629 |
#endif |
||
630 |
|||
631 |
#ifdef ECONNABORTED |
||
632 |
ERRNO_CASE(ECONNABORTED); |
||
633 |
#endif |
||
634 |
|||
635 |
#ifdef ECONNREFUSED |
||
636 |
ERRNO_CASE(ECONNREFUSED); |
||
637 |
#endif |
||
638 |
|||
639 |
#ifdef ECONNRESET |
||
640 |
ERRNO_CASE(ECONNRESET); |
||
641 |
#endif |
||
642 |
|||
643 |
#ifdef EDEADLK |
||
644 |
ERRNO_CASE(EDEADLK); |
||
645 |
#endif |
||
646 |
|||
647 |
#ifdef EDESTADDRREQ |
||
648 |
ERRNO_CASE(EDESTADDRREQ); |
||
649 |
#endif |
||
650 |
|||
651 |
#ifdef EDOM |
||
652 |
ERRNO_CASE(EDOM); |
||
653 |
#endif |
||
654 |
|||
655 |
#ifdef EDQUOT |
||
656 |
ERRNO_CASE(EDQUOT); |
||
657 |
#endif |
||
658 |
|||
659 |
#ifdef EEXIST |
||
660 |
ERRNO_CASE(EEXIST); |
||
661 |
#endif |
||
662 |
|||
663 |
#ifdef EFAULT |
||
664 |
ERRNO_CASE(EFAULT); |
||
665 |
#endif |
||
666 |
|||
667 |
#ifdef EFBIG |
||
668 |
ERRNO_CASE(EFBIG); |
||
669 |
#endif |
||
670 |
|||
671 |
#ifdef EHOSTUNREACH |
||
672 |
ERRNO_CASE(EHOSTUNREACH); |
||
673 |
#endif |
||
674 |
|||
675 |
#ifdef EIDRM |
||
676 |
ERRNO_CASE(EIDRM); |
||
677 |
#endif |
||
678 |
|||
679 |
#ifdef EILSEQ |
||
680 |
ERRNO_CASE(EILSEQ); |
||
681 |
#endif |
||
682 |
|||
683 |
#ifdef EINPROGRESS |
||
684 |
ERRNO_CASE(EINPROGRESS); |
||
685 |
#endif |
||
686 |
|||
687 |
#ifdef EINTR |
||
688 |
ERRNO_CASE(EINTR); |
||
689 |
#endif |
||
690 |
|||
691 |
#ifdef EINVAL |
||
692 |
ERRNO_CASE(EINVAL); |
||
693 |
#endif |
||
694 |
|||
695 |
#ifdef EIO |
||
696 |
ERRNO_CASE(EIO); |
||
697 |
#endif |
||
698 |
|||
699 |
#ifdef EISCONN |
||
700 |
ERRNO_CASE(EISCONN); |
||
701 |
#endif |
||
702 |
|||
703 |
#ifdef EISDIR |
||
704 |
ERRNO_CASE(EISDIR); |
||
705 |
#endif |
||
706 |
|||
707 |
#ifdef ELOOP |
||
708 |
ERRNO_CASE(ELOOP); |
||
709 |
#endif |
||
710 |
|||
711 |
#ifdef EMFILE |
||
712 |
ERRNO_CASE(EMFILE); |
||
713 |
#endif |
||
714 |
|||
715 |
#ifdef EMLINK |
||
716 |
ERRNO_CASE(EMLINK); |
||
717 |
#endif |
||
718 |
|||
719 |
#ifdef EMSGSIZE |
||
720 |
ERRNO_CASE(EMSGSIZE); |
||
721 |
#endif |
||
722 |
|||
723 |
#ifdef EMULTIHOP |
||
724 |
ERRNO_CASE(EMULTIHOP); |
||
725 |
#endif |
||
726 |
|||
727 |
#ifdef ENAMETOOLONG |
||
728 |
ERRNO_CASE(ENAMETOOLONG); |
||
729 |
#endif |
||
730 |
|||
731 |
#ifdef ENETDOWN |
||
732 |
ERRNO_CASE(ENETDOWN); |
||
733 |
#endif |
||
734 |
|||
735 |
#ifdef ENETRESET |
||
736 |
ERRNO_CASE(ENETRESET); |
||
737 |
#endif |
||
738 |
|||
739 |
#ifdef ENETUNREACH |
||
740 |
ERRNO_CASE(ENETUNREACH); |
||
741 |
#endif |
||
742 |
|||
743 |
#ifdef ENFILE |
||
744 |
ERRNO_CASE(ENFILE); |
||
745 |
#endif |
||
746 |
|||
747 |
#ifdef ENOBUFS |
||
748 |
ERRNO_CASE(ENOBUFS); |
||
749 |
#endif |
||
750 |
|||
751 |
#ifdef ENODATA |
||
752 |
ERRNO_CASE(ENODATA); |
||
753 |
#endif |
||
754 |
|||
755 |
#ifdef ENODEV |
||
756 |
ERRNO_CASE(ENODEV); |
||
757 |
#endif |
||
758 |
|||
759 |
#ifdef ENOENT |
||
760 |
ERRNO_CASE(ENOENT); |
||
761 |
#endif |
||
762 |
|||
763 |
#ifdef ENOEXEC |
||
764 |
ERRNO_CASE(ENOEXEC); |
||
765 |
#endif |
||
766 |
|||
767 |
#ifdef ENOLINK |
||
768 |
ERRNO_CASE(ENOLINK); |
||
769 |
#endif |
||
770 |
|||
771 |
#ifdef ENOLCK |
||
772 |
#if ENOLINK != ENOLCK |
||
773 |
ERRNO_CASE(ENOLCK); |
||
774 |
#endif |
||
775 |
#endif |
||
776 |
|||
777 |
#ifdef ENOMEM |
||
778 |
ERRNO_CASE(ENOMEM); |
||
779 |
#endif |
||
780 |
|||
781 |
#ifdef ENOMSG |
||
782 |
ERRNO_CASE(ENOMSG); |
||
783 |
#endif |
||
784 |
|||
785 |
#ifdef ENOPROTOOPT |
||
786 |
ERRNO_CASE(ENOPROTOOPT); |
||
787 |
#endif |
||
788 |
|||
789 |
#ifdef ENOSPC |
||
790 |
ERRNO_CASE(ENOSPC); |
||
791 |
#endif |
||
792 |
|||
793 |
#ifdef ENOSR |
||
794 |
ERRNO_CASE(ENOSR); |
||
795 |
#endif |
||
796 |
|||
797 |
#ifdef ENOSTR |
||
798 |
ERRNO_CASE(ENOSTR); |
||
799 |
#endif |
||
800 |
|||
801 |
#ifdef ENOSYS |
||
802 |
ERRNO_CASE(ENOSYS); |
||
803 |
#endif |
||
804 |
|||
805 |
#ifdef ENOTCONN |
||
806 |
ERRNO_CASE(ENOTCONN); |
||
807 |
#endif |
||
808 |
|||
809 |
#ifdef ENOTDIR |
||
810 |
ERRNO_CASE(ENOTDIR); |
||
811 |
#endif |
||
812 |
|||
813 |
#ifdef ENOTEMPTY |
||
814 |
#if ENOTEMPTY != EEXIST |
||
815 |
ERRNO_CASE(ENOTEMPTY); |
||
816 |
#endif |
||
817 |
#endif |
||
818 |
|||
819 |
#ifdef ENOTSOCK |
||
820 |
ERRNO_CASE(ENOTSOCK); |
||
821 |
#endif |
||
822 |
|||
823 |
#ifdef ENOTSUP |
||
824 |
ERRNO_CASE(ENOTSUP); |
||
825 |
#else |
||
826 |
#ifdef EOPNOTSUPP |
||
827 |
ERRNO_CASE(EOPNOTSUPP); |
||
828 |
#endif |
||
829 |
#endif |
||
830 |
|||
831 |
#ifdef ENOTTY |
||
832 |
ERRNO_CASE(ENOTTY); |
||
833 |
#endif |
||
834 |
|||
835 |
#ifdef ENXIO |
||
836 |
ERRNO_CASE(ENXIO); |
||
837 |
#endif |
||
838 |
|||
839 |
#ifdef EOVERFLOW |
||
840 |
ERRNO_CASE(EOVERFLOW); |
||
841 |
#endif |
||
842 |
|||
843 |
#ifdef EPERM |
||
844 |
6 |
ERRNO_CASE(EPERM); |
|
845 |
#endif |
||
846 |
|||
847 |
#ifdef EPIPE |
||
848 |
ERRNO_CASE(EPIPE); |
||
849 |
#endif |
||
850 |
|||
851 |
#ifdef EPROTO |
||
852 |
ERRNO_CASE(EPROTO); |
||
853 |
#endif |
||
854 |
|||
855 |
#ifdef EPROTONOSUPPORT |
||
856 |
ERRNO_CASE(EPROTONOSUPPORT); |
||
857 |
#endif |
||
858 |
|||
859 |
#ifdef EPROTOTYPE |
||
860 |
ERRNO_CASE(EPROTOTYPE); |
||
861 |
#endif |
||
862 |
|||
863 |
#ifdef ERANGE |
||
864 |
ERRNO_CASE(ERANGE); |
||
865 |
#endif |
||
866 |
|||
867 |
#ifdef EROFS |
||
868 |
ERRNO_CASE(EROFS); |
||
869 |
#endif |
||
870 |
|||
871 |
#ifdef ESPIPE |
||
872 |
ERRNO_CASE(ESPIPE); |
||
873 |
#endif |
||
874 |
|||
875 |
#ifdef ESRCH |
||
876 |
1 |
ERRNO_CASE(ESRCH); |
|
877 |
#endif |
||
878 |
|||
879 |
#ifdef ESTALE |
||
880 |
ERRNO_CASE(ESTALE); |
||
881 |
#endif |
||
882 |
|||
883 |
#ifdef ETIME |
||
884 |
ERRNO_CASE(ETIME); |
||
885 |
#endif |
||
886 |
|||
887 |
#ifdef ETIMEDOUT |
||
888 |
ERRNO_CASE(ETIMEDOUT); |
||
889 |
#endif |
||
890 |
|||
891 |
#ifdef ETXTBSY |
||
892 |
ERRNO_CASE(ETXTBSY); |
||
893 |
#endif |
||
894 |
|||
895 |
#ifdef EXDEV |
||
896 |
ERRNO_CASE(EXDEV); |
||
897 |
#endif |
||
898 |
|||
899 |
default: |
||
900 |
return ""; |
||
901 |
} |
||
902 |
} |
||
903 |
|||
904 |
1447 |
void PerIsolateMessageListener(Local<Message> message, Local<Value> error) { |
|
905 |
1447 |
Isolate* isolate = message->GetIsolate(); |
|
906 |
✓✓✗ | 1447 |
switch (message->ErrorLevel()) { |
907 |
1 |
case Isolate::MessageErrorLevel::kMessageWarning: { |
|
908 |
1 |
Environment* env = Environment::GetCurrent(isolate); |
|
909 |
✗✓ | 1 |
if (!env) { |
910 |
break; |
||
911 |
} |
||
912 |
3 |
Utf8Value filename(isolate, message->GetScriptOrigin().ResourceName()); |
|
913 |
// (filename):(line) (message) |
||
914 |
2 |
std::stringstream warning; |
|
915 |
1 |
warning << *filename; |
|
916 |
1 |
warning << ":"; |
|
917 |
✓✗ | 2 |
warning << message->GetLineNumber(env->context()).FromMaybe(-1); |
918 |
1 |
warning << " "; |
|
919 |
3 |
v8::String::Utf8Value msg(isolate, message->Get()); |
|
920 |
1 |
warning << *msg; |
|
921 |
1 |
USE(ProcessEmitWarningGeneric(env, warning.str().c_str(), "V8")); |
|
922 |
1 |
break; |
|
923 |
} |
||
924 |
1446 |
case Isolate::MessageErrorLevel::kMessageError: |
|
925 |
1446 |
TriggerUncaughtException(isolate, error, message); |
|
926 |
1245 |
break; |
|
927 |
} |
||
928 |
1246 |
} |
|
929 |
|||
930 |
811 |
void SetPrepareStackTraceCallback(const FunctionCallbackInfo<Value>& args) { |
|
931 |
811 |
Environment* env = Environment::GetCurrent(args); |
|
932 |
✗✓ | 811 |
CHECK(args[0]->IsFunction()); |
933 |
1622 |
env->set_prepare_stack_trace_callback(args[0].As<Function>()); |
|
934 |
811 |
} |
|
935 |
|||
936 |
6088 |
static void SetSourceMapsEnabled(const FunctionCallbackInfo<Value>& args) { |
|
937 |
6088 |
Environment* env = Environment::GetCurrent(args); |
|
938 |
✗✓ | 6088 |
CHECK(args[0]->IsBoolean()); |
939 |
12176 |
env->set_source_maps_enabled(args[0].As<Boolean>()->Value()); |
|
940 |
6088 |
} |
|
941 |
|||
942 |
25 |
static void SetGetSourceMapErrorSource( |
|
943 |
const FunctionCallbackInfo<Value>& args) { |
||
944 |
25 |
Environment* env = Environment::GetCurrent(args); |
|
945 |
✗✓ | 25 |
CHECK(args[0]->IsFunction()); |
946 |
50 |
env->set_get_source_map_error_source(args[0].As<Function>()); |
|
947 |
25 |
} |
|
948 |
|||
949 |
6270 |
static void SetMaybeCacheGeneratedSourceMap( |
|
950 |
const FunctionCallbackInfo<Value>& args) { |
||
951 |
6270 |
Environment* env = Environment::GetCurrent(args); |
|
952 |
✗✓ | 6270 |
CHECK(args[0]->IsFunction()); |
953 |
12540 |
env->set_maybe_cache_generated_source_map(args[0].As<Function>()); |
|
954 |
6270 |
} |
|
955 |
|||
956 |
784 |
static void SetEnhanceStackForFatalException( |
|
957 |
const FunctionCallbackInfo<Value>& args) { |
||
958 |
784 |
Environment* env = Environment::GetCurrent(args); |
|
959 |
✗✓ | 784 |
CHECK(args[0]->IsFunction()); |
960 |
✗✓ | 784 |
CHECK(args[1]->IsFunction()); |
961 |
✓✗ | 1568 |
env->set_enhance_fatal_stack_before_inspector(args[0].As<Function>()); |
962 |
1568 |
env->set_enhance_fatal_stack_after_inspector(args[1].As<Function>()); |
|
963 |
784 |
} |
|
964 |
|||
965 |
// Side effect-free stringification that will never throw exceptions. |
||
966 |
9 |
static void NoSideEffectsToString(const FunctionCallbackInfo<Value>& args) { |
|
967 |
9 |
Local<Context> context = args.GetIsolate()->GetCurrentContext(); |
|
968 |
Local<String> detail_string; |
||
969 |
✓✗ | 18 |
if (args[0]->ToDetailString(context).ToLocal(&detail_string)) |
970 |
18 |
args.GetReturnValue().Set(detail_string); |
|
971 |
9 |
} |
|
972 |
|||
973 |
88 |
static void TriggerUncaughtException(const FunctionCallbackInfo<Value>& args) { |
|
974 |
88 |
Isolate* isolate = args.GetIsolate(); |
|
975 |
88 |
Environment* env = Environment::GetCurrent(isolate); |
|
976 |
88 |
Local<Value> exception = args[0]; |
|
977 |
88 |
Local<Message> message = Exception::CreateMessage(isolate, exception); |
|
978 |
✓✗✗✓ ✗✓ |
88 |
if (env != nullptr && env->abort_on_uncaught_exception()) { |
979 |
ReportFatalException( |
||
980 |
env, exception, message, EnhanceFatalException::kEnhance); |
||
981 |
Abort(); |
||
982 |
} |
||
983 |
88 |
bool from_promise = args[1]->IsTrue(); |
|
984 |
88 |
errors::TriggerUncaughtException(isolate, exception, message, from_promise); |
|
985 |
13 |
} |
|
986 |
|||
987 |
5527 |
void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
|
988 |
5527 |
registry->Register(SetPrepareStackTraceCallback); |
|
989 |
5527 |
registry->Register(SetGetSourceMapErrorSource); |
|
990 |
5527 |
registry->Register(SetSourceMapsEnabled); |
|
991 |
5527 |
registry->Register(SetMaybeCacheGeneratedSourceMap); |
|
992 |
5527 |
registry->Register(SetEnhanceStackForFatalException); |
|
993 |
5527 |
registry->Register(NoSideEffectsToString); |
|
994 |
5527 |
registry->Register(TriggerUncaughtException); |
|
995 |
5527 |
} |
|
996 |
|||
997 |
784 |
void Initialize(Local<Object> target, |
|
998 |
Local<Value> unused, |
||
999 |
Local<Context> context, |
||
1000 |
void* priv) { |
||
1001 |
784 |
SetMethod(context, |
|
1002 |
target, |
||
1003 |
"setPrepareStackTraceCallback", |
||
1004 |
SetPrepareStackTraceCallback); |
||
1005 |
784 |
SetMethod(context, |
|
1006 |
target, |
||
1007 |
"setGetSourceMapErrorSource", |
||
1008 |
SetGetSourceMapErrorSource); |
||
1009 |
784 |
SetMethod(context, target, "setSourceMapsEnabled", SetSourceMapsEnabled); |
|
1010 |
784 |
SetMethod(context, |
|
1011 |
target, |
||
1012 |
"setMaybeCacheGeneratedSourceMap", |
||
1013 |
SetMaybeCacheGeneratedSourceMap); |
||
1014 |
784 |
SetMethod(context, |
|
1015 |
target, |
||
1016 |
"setEnhanceStackForFatalException", |
||
1017 |
SetEnhanceStackForFatalException); |
||
1018 |
784 |
SetMethodNoSideEffect( |
|
1019 |
context, target, "noSideEffectsToString", NoSideEffectsToString); |
||
1020 |
784 |
SetMethod( |
|
1021 |
context, target, "triggerUncaughtException", TriggerUncaughtException); |
||
1022 |
784 |
} |
|
1023 |
|||
1024 |
309 |
void DecorateErrorStack(Environment* env, |
|
1025 |
const errors::TryCatchScope& try_catch) { |
||
1026 |
309 |
Local<Value> exception = try_catch.Exception(); |
|
1027 |
|||
1028 |
✓✓ | 315 |
if (!exception->IsObject()) return; |
1029 |
|||
1030 |
300 |
Local<Object> err_obj = exception.As<Object>(); |
|
1031 |
|||
1032 |
✓✓ | 300 |
if (IsExceptionDecorated(env, err_obj)) return; |
1033 |
|||
1034 |
297 |
AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR); |
|
1035 |
297 |
TryCatchScope try_catch_scope(env); // Ignore exceptions below. |
|
1036 |
594 |
MaybeLocal<Value> stack = err_obj->Get(env->context(), env->stack_string()); |
|
1037 |
MaybeLocal<Value> maybe_value = |
||
1038 |
297 |
err_obj->GetPrivate(env->context(), env->arrow_message_private_symbol()); |
|
1039 |
|||
1040 |
Local<Value> arrow; |
||
1041 |
✓✗✗✓ ✗✓ |
891 |
if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) { |
1042 |
return; |
||
1043 |
} |
||
1044 |
|||
1045 |
✓✓✓✓ ✓✓ |
889 |
if (stack.IsEmpty() || !stack.ToLocalChecked()->IsString()) { |
1046 |
3 |
return; |
|
1047 |
} |
||
1048 |
|||
1049 |
Local<String> decorated_stack = String::Concat( |
||
1050 |
env->isolate(), |
||
1051 |
String::Concat(env->isolate(), |
||
1052 |
arrow.As<String>(), |
||
1053 |
FIXED_ONE_BYTE_STRING(env->isolate(), "\n")), |
||
1054 |
882 |
stack.ToLocalChecked().As<String>()); |
|
1055 |
588 |
USE(err_obj->Set(env->context(), env->stack_string(), decorated_stack)); |
|
1056 |
err_obj->SetPrivate( |
||
1057 |
588 |
env->context(), env->decorated_private_symbol(), True(env->isolate())); |
|
1058 |
} |
||
1059 |
|||
1060 |
1548 |
void TriggerUncaughtException(Isolate* isolate, |
|
1061 |
Local<Value> error, |
||
1062 |
Local<Message> message, |
||
1063 |
bool from_promise) { |
||
1064 |
✗✓ | 1548 |
CHECK(!error.IsEmpty()); |
1065 |
1548 |
HandleScope scope(isolate); |
|
1066 |
|||
1067 |
✓✓ | 1548 |
if (message.IsEmpty()) message = Exception::CreateMessage(isolate, error); |
1068 |
|||
1069 |
✗✓ | 1548 |
CHECK(isolate->InContext()); |
1070 |
1548 |
Local<Context> context = isolate->GetCurrentContext(); |
|
1071 |
1548 |
Environment* env = Environment::GetCurrent(context); |
|
1072 |
✗✓ | 1548 |
if (env == nullptr) { |
1073 |
// This means that the exception happens before Environment is assigned |
||
1074 |
// to the context e.g. when there is a SyntaxError in a per-context |
||
1075 |
// script - which usually indicates that there is a bug because no JS |
||
1076 |
// error is supposed to be thrown at this point. |
||
1077 |
// Since we don't have access to Environment here, there is not |
||
1078 |
// much we can do, so we just print whatever is useful and crash. |
||
1079 |
PrintException(isolate, context, error, message); |
||
1080 |
Abort(); |
||
1081 |
} |
||
1082 |
|||
1083 |
// Invoke process._fatalException() to give user a chance to handle it. |
||
1084 |
// We have to grab it from the process object since this has been |
||
1085 |
// monkey-patchable. |
||
1086 |
1548 |
Local<Object> process_object = env->process_object(); |
|
1087 |
1548 |
Local<String> fatal_exception_string = env->fatal_exception_string(); |
|
1088 |
Local<Value> fatal_exception_function = |
||
1089 |
1548 |
process_object->Get(env->context(), |
|
1090 |
3096 |
fatal_exception_string).ToLocalChecked(); |
|
1091 |
// If the exception happens before process._fatalException is attached |
||
1092 |
// during bootstrap, or if the user has patched it incorrectly, exit |
||
1093 |
// the current Node.js instance. |
||
1094 |
✓✓ | 1548 |
if (!fatal_exception_function->IsFunction()) { |
1095 |
2 |
ReportFatalException( |
|
1096 |
env, error, message, EnhanceFatalException::kDontEnhance); |
||
1097 |
2 |
env->Exit(6); |
|
1098 |
1 |
return; |
|
1099 |
} |
||
1100 |
|||
1101 |
MaybeLocal<Value> maybe_handled; |
||
1102 |
✓✓ | 1546 |
if (env->can_call_into_js()) { |
1103 |
// We do not expect the global uncaught exception itself to throw any more |
||
1104 |
// exceptions. If it does, exit the current Node.js instance. |
||
1105 |
errors::TryCatchScope try_catch(env, |
||
1106 |
1540 |
errors::TryCatchScope::CatchMode::kFatal); |
|
1107 |
// Explicitly disable verbose exception reporting - |
||
1108 |
// if process._fatalException() throws an error, we don't want it to |
||
1109 |
// trigger the per-isolate message listener which will call this |
||
1110 |
// function and recurse. |
||
1111 |
1540 |
try_catch.SetVerbose(false); |
|
1112 |
Local<Value> argv[2] = { error, |
||
1113 |
3080 |
Boolean::New(env->isolate(), from_promise) }; |
|
1114 |
|||
1115 |
1540 |
maybe_handled = fatal_exception_function.As<Function>()->Call( |
|
1116 |
1540 |
env->context(), process_object, arraysize(argv), argv); |
|
1117 |
} |
||
1118 |
|||
1119 |
// If process._fatalException() throws, we are now exiting the Node.js |
||
1120 |
// instance so return to continue the exit routine. |
||
1121 |
// TODO(joyeecheung): return a Maybe here to prevent the caller from |
||
1122 |
// stepping on the exit. |
||
1123 |
Local<Value> handled; |
||
1124 |
✓✓ | 1531 |
if (!maybe_handled.ToLocal(&handled)) { |
1125 |
9 |
return; |
|
1126 |
} |
||
1127 |
|||
1128 |
// The global uncaught exception handler returns true if the user handles it |
||
1129 |
// by e.g. listening to `uncaughtException`. In that case, continue program |
||
1130 |
// execution. |
||
1131 |
// TODO(joyeecheung): This has been only checking that the return value is |
||
1132 |
// exactly false. Investigate whether this can be turned to an "if true" |
||
1133 |
// similar to how the worker global uncaught exception handler handles it. |
||
1134 |
✓✓ | 1522 |
if (!handled->IsFalse()) { |
1135 |
1256 |
return; |
|
1136 |
} |
||
1137 |
|||
1138 |
// Now we are certain that the exception is fatal. |
||
1139 |
266 |
ReportFatalException(env, error, message, EnhanceFatalException::kEnhance); |
|
1140 |
266 |
RunAtExit(env); |
|
1141 |
|||
1142 |
// If the global uncaught exception handler sets process.exitCode, |
||
1143 |
// exit with that code. Otherwise, exit with 1. |
||
1144 |
266 |
Local<String> exit_code = env->exit_code_string(); |
|
1145 |
Local<Value> code; |
||
1146 |
✓✗✓✓ ✓✓ |
798 |
if (process_object->Get(env->context(), exit_code).ToLocal(&code) && |
1147 |
266 |
code->IsInt32()) { |
|
1148 |
265 |
env->Exit(code.As<Int32>()->Value()); |
|
1149 |
} else { |
||
1150 |
1 |
env->Exit(1); |
|
1151 |
} |
||
1152 |
} |
||
1153 |
|||
1154 |
6 |
void TriggerUncaughtException(Isolate* isolate, const v8::TryCatch& try_catch) { |
|
1155 |
// If the try_catch is verbose, the per-isolate message listener is going to |
||
1156 |
// handle it (which is going to call into another overload of |
||
1157 |
// TriggerUncaughtException()). |
||
1158 |
✗✓ | 6 |
if (try_catch.IsVerbose()) { |
1159 |
return; |
||
1160 |
} |
||
1161 |
|||
1162 |
// If the user calls TryCatch::TerminateExecution() on this TryCatch |
||
1163 |
// they must call CancelTerminateExecution() again before invoking |
||
1164 |
// TriggerUncaughtException() because it will invoke |
||
1165 |
// process._fatalException() in the JS land. |
||
1166 |
✗✓ | 6 |
CHECK(!try_catch.HasTerminated()); |
1167 |
✗✓ | 6 |
CHECK(try_catch.HasCaught()); |
1168 |
9 |
HandleScope scope(isolate); |
|
1169 |
6 |
TriggerUncaughtException(isolate, |
|
1170 |
try_catch.Exception(), |
||
1171 |
try_catch.Message(), |
||
1172 |
false /* from_promise */); |
||
1173 |
} |
||
1174 |
|||
1175 |
} // namespace errors |
||
1176 |
|||
1177 |
} // namespace node |
||
1178 |
|||
1179 |
5597 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(errors, node::errors::Initialize) |
|
1180 |
5527 |
NODE_MODULE_EXTERNAL_REFERENCE(errors, node::errors::RegisterExternalReferences) |
Generated by: GCOVR (Version 4.2) |