Skip to content

Commit e6fb17b

Browse files
authored
feat: Support #[stack_trace] op2 arg (#920)
Needed for denoland/deno#25502
1 parent c907543 commit e6fb17b

15 files changed

+427
-83
lines changed

core/extension_set.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ pub fn create_op_ctxs(
126126
op_state: Rc<RefCell<OpState>>,
127127
runtime_state: Rc<JsRuntimeState>,
128128
get_error_class_fn: GetErrorClassFn,
129+
enable_stack_trace_arg_in_ops: bool,
129130
) -> Box<[OpCtx]> {
130131
let op_count = op_decls.len();
131132
let mut op_ctxs = Vec::with_capacity(op_count);
@@ -145,6 +146,7 @@ pub fn create_op_ctxs(
145146
runtime_state_ptr,
146147
get_error_class_fn,
147148
metrics_fn,
149+
enable_stack_trace_arg_in_ops,
148150
);
149151

150152
op_ctxs.push(op_ctx);

core/ops.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ pub struct OpCtx {
8686
pub state: Rc<RefCell<OpState>>,
8787
#[doc(hidden)]
8888
pub get_error_class_fn: GetErrorClassFn,
89+
#[doc(hidden)]
90+
pub enable_stack_trace_arg: bool,
8991

9092
pub(crate) decl: OpDecl,
9193
pub(crate) fast_fn_info: Option<CFunction>,
@@ -106,6 +108,7 @@ impl OpCtx {
106108
runtime_state: *const JsRuntimeState,
107109
get_error_class_fn: GetErrorClassFn,
108110
metrics_fn: Option<OpMetricsFn>,
111+
enable_stack_trace_arg: bool,
109112
) -> Self {
110113
// If we want metrics for this function, create the fastcall `CFunctionInfo` from the metrics
111114
// `CFunction`. For some extremely fast ops, the parameter list may change for the metrics
@@ -127,6 +130,7 @@ impl OpCtx {
127130
fast_fn_info,
128131
isolate,
129132
metrics_fn,
133+
enable_stack_trace_arg,
130134
}
131135
}
132136

core/runtime/jsruntime.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,10 @@ pub struct RuntimeOptions {
553553
Option<(EvalContextGetCodeCacheCb, EvalContextCodeCacheReadyCb)>,
554554

555555
pub import_assertions_support: ImportAssertionsSupport,
556+
557+
/// Whether `#[stack_trace]` argument in ops should return `Some(frames)`. Use wisely,
558+
/// as it's very expensive to collect stack traces on each op invocation.
559+
pub enable_stack_trace_arg_in_ops: bool,
556560
}
557561

558562
pub struct ImportAssertionsSupportCustomCallbackArgs {
@@ -873,6 +877,7 @@ impl JsRuntime {
873877
op_state.clone(),
874878
state_rc.clone(),
875879
get_error_class_fn,
880+
options.enable_stack_trace_arg_in_ops,
876881
);
877882

878883
// ...ops are now almost fully set up; let's create a V8 isolate...

ops/op2/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,20 @@ Only usable in `deno_core`.
921921
</td><td>
922922
⚠️ Extremely dangerous, may crash if you don't use `nofast` depending on what you do.
923923
</td></tr>
924+
<tr>
925+
<td>
926+
927+
```text
928+
#[stack_trace] Option<Vec<JsStackFrame>>
929+
```
930+
931+
</td><td>
932+
933+
</td><td>
934+
935+
</td><td>
936+
⚠️ This argument is very slow as it needs to create an error instance and collect a whole stack frame. It only returns `Some(frames)` if `RuntimeOptions::enable_stack_trace_arg_in_ops` is set to true.
937+
</td></tr>
924938
</table>
925939

926940
<!-- END ARGS -->

ops/op2/dispatch_async.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use super::dispatch_slow::with_opstate;
1111
use super::dispatch_slow::with_retval;
1212
use super::dispatch_slow::with_scope;
1313
use super::dispatch_slow::with_self;
14+
use super::dispatch_slow::with_stack_trace;
1415
use super::generator_state::gs_quote;
1516
use super::generator_state::GeneratorState;
1617
use super::signature::ParsedSignature;
@@ -120,11 +121,12 @@ pub(crate) fn generate_dispatch_async(
120121
quote!()
121122
};
122123

123-
let with_opctx = if generator_state.needs_opctx {
124-
with_opctx(generator_state)
125-
} else {
126-
quote!()
127-
};
124+
let with_opctx =
125+
if generator_state.needs_opctx | generator_state.needs_stack_trace {
126+
with_opctx(generator_state)
127+
} else {
128+
quote!()
129+
};
128130

129131
let with_retval = if generator_state.needs_retval {
130132
with_retval(generator_state)
@@ -138,8 +140,15 @@ pub(crate) fn generate_dispatch_async(
138140
quote!()
139141
};
140142

141-
let with_scope = if generator_state.needs_scope {
142-
with_scope(generator_state)
143+
let with_scope =
144+
if generator_state.needs_scope | generator_state.needs_stack_trace {
145+
with_scope(generator_state)
146+
} else {
147+
quote!()
148+
};
149+
150+
let with_stack_trace = if generator_state.needs_stack_trace {
151+
with_stack_trace(generator_state)
143152
} else {
144153
quote!()
145154
};
@@ -157,6 +166,7 @@ pub(crate) fn generate_dispatch_async(
157166
#with_opctx
158167
#with_opstate
159168
#with_self
169+
#with_stack_trace
160170

161171
#output
162172
}

ops/op2/dispatch_fast.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,8 @@ fn map_arg_to_v8_fastcall_type(
845845
| Arg::OptionBuffer(..)
846846
| Arg::SerdeV8(_)
847847
| Arg::FromV8(_)
848-
| Arg::Ref(..) => return Ok(None),
848+
| Arg::Ref(..)
849+
| Arg::Special(Special::StackTrace) => return Ok(None),
849850
// We don't support v8 global arguments
850851
Arg::V8Global(_) | Arg::OptionV8Global(_) => return Ok(None),
851852
// We do support v8 type arguments (including Option<...>)

ops/op2/dispatch_slow.rs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,12 @@ pub(crate) fn generate_dispatch_slow(
9292
quote!()
9393
};
9494

95-
let with_opctx = if generator_state.needs_opctx {
96-
with_opctx(generator_state)
97-
} else {
98-
quote!()
99-
};
95+
let with_opctx =
96+
if generator_state.needs_opctx | generator_state.needs_stack_trace {
97+
with_opctx(generator_state)
98+
} else {
99+
quote!()
100+
};
100101

101102
let with_retval = if generator_state.needs_retval {
102103
with_retval(generator_state)
@@ -117,8 +118,15 @@ pub(crate) fn generate_dispatch_slow(
117118
quote!()
118119
};
119120

120-
let with_scope = if generator_state.needs_scope {
121-
with_scope(generator_state)
121+
let with_scope =
122+
if generator_state.needs_scope | generator_state.needs_stack_trace {
123+
with_scope(generator_state)
124+
} else {
125+
quote!()
126+
};
127+
128+
let with_stack_trace = if generator_state.needs_stack_trace {
129+
with_stack_trace(generator_state)
122130
} else {
123131
quote!()
124132
};
@@ -136,6 +144,7 @@ pub(crate) fn generate_dispatch_slow(
136144
#with_opctx
137145
#with_isolate
138146
#with_opstate
147+
#with_stack_trace
139148
#with_js_runtime_state
140149
#with_self
141150

@@ -183,6 +192,21 @@ pub(crate) fn with_scope(generator_state: &mut GeneratorState) -> TokenStream {
183192
)
184193
}
185194

195+
pub(crate) fn with_stack_trace(
196+
generator_state: &mut GeneratorState,
197+
) -> TokenStream {
198+
generator_state.needs_opctx = true;
199+
gs_quote!(generator_state(stack_trace, opctx, scope) =>
200+
(let #stack_trace = if #opctx.enable_stack_trace_arg {
201+
let hs = &mut deno_core::v8::HandleScope::new(&mut #scope);
202+
let stack_trace_msg = deno_core::v8::String::empty(hs);
203+
let stack_trace_error = deno_core::v8::Exception::error(hs, stack_trace_msg.into());
204+
let js_error = deno_core::error::JsError::from_v8_exception(hs, stack_trace_error);
205+
Some(js_error.frames)
206+
} else { None };)
207+
)
208+
}
209+
186210
pub(crate) fn with_retval(generator_state: &mut GeneratorState) -> TokenStream {
187211
gs_quote!(generator_state(retval, info) =>
188212
(let mut #retval = deno_core::v8::ReturnValue::from_function_callback_info(#info);)
@@ -284,11 +308,13 @@ pub fn from_arg(
284308
scope,
285309
opstate,
286310
opctx,
311+
stack_trace,
287312
js_runtime_state,
288313
needs_scope,
289314
needs_isolate,
290315
needs_opstate,
291316
needs_opctx,
317+
needs_stack_trace,
292318
needs_js_runtime_state,
293319
..
294320
} = &mut generator_state;
@@ -439,6 +465,10 @@ pub fn from_arg(
439465
*needs_opctx = true;
440466
quote!(let #arg_ident = #opctx.isolate;)
441467
}
468+
Arg::Special(Special::StackTrace) => {
469+
*needs_stack_trace = true;
470+
quote!(let #arg_ident = #stack_trace;)
471+
}
442472
Arg::Ref(_, Special::HandleScope) => {
443473
*needs_scope = true;
444474
quote!(let #arg_ident = &mut #scope;)

ops/op2/generator_state.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2-
use proc_macro2::{Ident, TokenStream};
2+
use proc_macro2::Ident;
3+
use proc_macro2::TokenStream;
34

45
#[derive(Clone)]
56
pub struct GeneratorState {
@@ -19,6 +20,8 @@ pub struct GeneratorState {
1920
pub opctx: Ident,
2021
/// The `OpState` used for storing op state.
2122
pub opstate: Ident,
23+
// The stack trace used for storing a stack trace.
24+
pub stack_trace: Ident,
2225
/// The `JsRuntimeState` used for storing the `Rc<JsRuntimeState>``.
2326
pub js_runtime_state: Ident,
2427
/// The `FastApiCallbackOptions` used in fast calls for fallback returns.
@@ -46,6 +49,7 @@ pub struct GeneratorState {
4649
pub needs_isolate: bool,
4750
pub needs_opstate: bool,
4851
pub needs_opctx: bool,
52+
pub needs_stack_trace: bool,
4953
pub needs_js_runtime_state: bool,
5054
pub needs_fast_api_callback_options: bool,
5155
pub needs_self: bool,

ops/op2/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ fn generate_op2(
140140
let info = Ident::new("info", Span::call_site());
141141
let opctx = Ident::new("opctx", Span::call_site());
142142
let opstate = Ident::new("opstate", Span::call_site());
143+
let stack_trace = Ident::new("stack_trace", Span::call_site());
143144
let js_runtime_state = Ident::new("js_runtime_state", Span::call_site());
144145
let promise_id = Ident::new("promise_id", Span::call_site());
145146
let slow_function = Ident::new("v8_fn_ptr", Span::call_site());
@@ -165,6 +166,7 @@ fn generate_op2(
165166
opctx,
166167
opstate,
167168
js_runtime_state,
169+
stack_trace,
168170
fast_api_callback_options,
169171
result,
170172
retval,
@@ -181,6 +183,7 @@ fn generate_op2(
181183
needs_isolate: false,
182184
needs_opctx: false,
183185
needs_opstate: false,
186+
needs_stack_trace: false,
184187
needs_js_runtime_state: false,
185188
needs_fast_api_callback_options: false,
186189
needs_self: config.method.is_some(),

ops/op2/signature.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ pub enum Special {
146146
JsRuntimeState,
147147
FastApiCallbackOptions,
148148
Isolate,
149+
StackTrace,
149150
}
150151

151152
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -335,7 +336,8 @@ impl Arg {
335336
| Special::OpState
336337
| Special::JsRuntimeState
337338
| Special::HandleScope
338-
| Special::Isolate,
339+
| Special::Isolate
340+
| Special::StackTrace,
339341
) => true,
340342
Self::Ref(
341343
_,
@@ -755,6 +757,8 @@ pub enum AttributeModifier {
755757
Number,
756758
/// #[cppgc], for a resource backed managed by cppgc.
757759
CppGcResource,
760+
/// #[stack_trace], for a stack trace of the op call site
761+
StackTrace,
758762
}
759763

760764
impl AttributeModifier {
@@ -771,6 +775,7 @@ impl AttributeModifier {
771775
AttributeModifier::State => "state",
772776
AttributeModifier::Global => "global",
773777
AttributeModifier::CppGcResource => "cppgc",
778+
AttributeModifier::StackTrace => "stack_trace",
774779
}
775780
}
776781
}
@@ -1239,6 +1244,7 @@ fn parse_attribute(
12391244
(#[cppgc]) => Some(AttributeModifier::CppGcResource),
12401245
(#[to_v8]) => Some(AttributeModifier::ToV8),
12411246
(#[from_v8]) => Some(AttributeModifier::FromV8),
1247+
(#[stack_trace]) => Some(AttributeModifier::StackTrace),
12421248
(#[allow ($_rule:path)]) => None,
12431249
(#[doc = $_attr:literal]) => None,
12441250
(#[cfg $_cfg:tt]) => None,
@@ -1513,6 +1519,16 @@ pub(crate) fn parse_type(
15131519
"argument",
15141520
))
15151521
}
1522+
AttributeModifier::StackTrace => {
1523+
if position == Position::RetVal {
1524+
return Err(ArgError::InvalidAttributePosition(
1525+
primary.name(),
1526+
"argument",
1527+
));
1528+
}
1529+
1530+
return Ok(Arg::Special(Special::StackTrace));
1531+
}
15161532
AttributeModifier::ToV8 if position == Position::Arg => {
15171533
return Err(ArgError::InvalidAttributePosition(
15181534
primary.name(),

0 commit comments

Comments
 (0)