Skip to content

Commit 6f70054

Browse files
devsnekBridgeAR
authored andcommitted
src, lib: take control of prepareStackTrace
Refs https://crbug.com/v8/7848 PR-URL: #23926 Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent aed74cc commit 6f70054

7 files changed

Lines changed: 94 additions & 1 deletion

File tree

‎lib/internal/bootstrap/node.js‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
// passed by node::RunBootstrapping()
3636
/* global process, require, internalBinding, isMainThread, ownsProcessState */
3737

38+
setupPrepareStackTrace();
39+
3840
const{JSON, Object, Symbol }=primordials;
3941
constconfig=internalBinding('config');
4042
const{ deprecate }=require('internal/util');
@@ -290,6 +292,12 @@ process.emitWarning = emitWarning;
290292
// Note: only after this point are the timers effective
291293
}
292294

295+
functionsetupPrepareStackTrace(){
296+
const{ setPrepareStackTraceCallback }=internalBinding('errors');
297+
const{ prepareStackTrace }=require('internal/errors');
298+
setPrepareStackTraceCallback(prepareStackTrace);
299+
}
300+
293301
functionsetupProcessObject(){
294302
constEventEmitter=require('events');
295303
constorigProcProto=Object.getPrototypeOf(process);

‎lib/internal/errors.js‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,31 @@ const codes = {};
1919

2020
const{ kMaxLength }=internalBinding('buffer');
2121

22+
constMainContextError=Error;
23+
constErrorToString=Error.prototype.toString;
24+
// Polyfill of V8's Error.prepareStackTrace API.
25+
// https://crbug.com/v8/7848
26+
constprepareStackTrace=(globalThis,error,trace)=>{
27+
// `globalThis` is the global that contains the constructor which
28+
// created `error`.
29+
if(typeofglobalThis.Error.prepareStackTrace==='function'){
30+
returnglobalThis.Error.prepareStackTrace(error,trace);
31+
}
32+
// We still have legacy usage that depends on the main context's `Error`
33+
// being used, even when the error is from a different context.
34+
// TODO(devsnek): evaluate if this can be eventually deprecated/removed.
35+
if(typeofMainContextError.prepareStackTrace==='function'){
36+
returnMainContextError.prepareStackTrace(error,trace);
37+
}
38+
39+
consterrorString=ErrorToString.call(error);
40+
if(trace.length===0){
41+
returnerrorString;
42+
}
43+
return`${errorString}\n at ${trace.join('\n at ')}`;
44+
};
45+
46+
2247
letexcludedStackFn;
2348

2449
// Lazily loaded
@@ -598,7 +623,8 @@ module.exports = {
598623
uvExceptionWithHostPort,
599624
SystemError,
600625
// This is exported only to facilitate testing.
601-
E
626+
E,
627+
prepareStackTrace,
602628
};
603629

604630
// To declare an error message, use the E(sym, val, def) function above. The sym

‎src/api/environment.cc‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#endif
1414

1515
namespacenode {
16+
using errors::TryCatchScope;
17+
using v8::Array;
1618
using v8::Context;
1719
using v8::EscapableHandleScope;
1820
using v8::Function;
@@ -45,6 +47,41 @@ static bool ShouldAbortOnUncaughtException(Isolate* isolate) {
4547
!env->inside_should_not_abort_on_uncaught_scope();
4648
}
4749

50+
static MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
51+
Local<Value> exception,
52+
Local<Array> trace) {
53+
Environment* env = Environment::GetCurrent(context);
54+
if (env == nullptr) {
55+
MaybeLocal<String> s = exception->ToString(context);
56+
return s.IsEmpty() ?
57+
MaybeLocal<Value>() :
58+
MaybeLocal<Value>(s.ToLocalChecked());
59+
}
60+
Local<Function> prepare = env->prepare_stack_trace_callback();
61+
if (prepare.IsEmpty()) {
62+
MaybeLocal<String> s = exception->ToString(context);
63+
return s.IsEmpty() ?
64+
MaybeLocal<Value>() :
65+
MaybeLocal<Value>(s.ToLocalChecked());
66+
}
67+
Local<Value> args[] = {
68+
context->Global(),
69+
exception,
70+
trace,
71+
};
72+
// This TryCatch + Rethrow is required by V8 due to details around exception
73+
// handling there. For C++ callbacks, V8 expects a scheduled exception (which
74+
// is what ReThrow gives us). Just returning the empty MaybeLocal would leave
75+
// us with a pending exception.
76+
TryCatchScope try_catch(env);
77+
MaybeLocal<Value> result = prepare->Call(
78+
context, Undefined(env->isolate()), arraysize(args), args);
79+
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
80+
try_catch.ReThrow();
81+
}
82+
return result;
83+
}
84+
4885
void* NodeArrayBufferAllocator::Allocate(size_t size) {
4986
if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
5087
returnUncheckedCalloc(size);
@@ -166,6 +203,7 @@ void SetIsolateUpForNode(v8::Isolate* isolate, IsolateSettingCategories cat) {
166203
isolate->SetAbortOnUncaughtExceptionCallback(
167204
ShouldAbortOnUncaughtException);
168205
isolate->SetFatalErrorHandler(OnFatalError);
206+
isolate->SetPrepareStackTraceCallback(PrepareStackTraceCallback);
169207
break;
170208
case IsolateSettingCategories::kMisc:
171209
isolate->SetMicrotasksPolicy(MicrotasksPolicy::kExplicit);

‎src/env.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
402402
V(native_module_require, v8::Function) \
403403
V(performance_entry_callback, v8::Function) \
404404
V(performance_entry_template, v8::Function) \
405+
V(prepare_stack_trace_callback, v8::Function) \
405406
V(process_object, v8::Object) \
406407
V(primordials, v8::Object) \
407408
V(promise_reject_callback, v8::Function) \

‎src/node_binding.cc‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
V(contextify) \
4949
V(credentials) \
5050
V(domain) \
51+
V(errors) \
5152
V(fs) \
5253
V(fs_event_wrap) \
5354
V(heap_utils) \

‎src/node_errors.cc‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ using v8::Boolean;
1717
using v8::Context;
1818
using v8::Exception;
1919
using v8::Function;
20+
using v8::FunctionCallbackInfo;
2021
using v8::HandleScope;
2122
using v8::Int32;
2223
using v8::Isolate;
@@ -767,6 +768,21 @@ void PerIsolateMessageListener(Local<Message> message, Local<Value> error) {
767768
}
768769
}
769770

771+
voidSetPrepareStackTraceCallback(const FunctionCallbackInfo<Value>& args) {
772+
Environment* env = Environment::GetCurrent(args);
773+
CHECK(args[0]->IsFunction());
774+
env->set_prepare_stack_trace_callback(args[0].As<Function>());
775+
}
776+
777+
voidInitialize(Local<Object> target,
778+
Local<Value> unused,
779+
Local<Context> context,
780+
void* priv) {
781+
Environment* env = Environment::GetCurrent(context);
782+
env->SetMethod(
783+
target, "setPrepareStackTraceCallback", SetPrepareStackTraceCallback);
784+
}
785+
770786
} // namespace errors
771787

772788
voidDecorateErrorStack(Environment* env,
@@ -880,3 +896,5 @@ void FatalException(Isolate* isolate,
880896
}
881897

882898
} // namespace node
899+
900+
NODE_MODULE_CONTEXT_AWARE_INTERNAL(errors, node::errors::Initialize)

‎test/parallel/test-bootstrap-modules.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const common = require('../common');
99
constassert=require('assert');
1010

1111
constexpectedModules=newSet([
12+
'Internal Binding errors',
1213
'Internal Binding async_wrap',
1314
'Internal Binding buffer',
1415
'Internal Binding config',

0 commit comments

Comments
 (0)