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