diff --git a/ops/op2/dispatch_slow.rs b/ops/op2/dispatch_slow.rs index a3642b15e..9126e6b02 100644 --- a/ops/op2/dispatch_slow.rs +++ b/ops/op2/dispatch_slow.rs @@ -72,6 +72,12 @@ pub(crate) fn generate_dispatch_slow( |s| V8SignatureMappingError::NoRetValMapping(s, signature.ret_val.clone()), )?); + let with_stack_trace = if generator_state.needs_stack_trace { + with_stack_trace(generator_state) + } else { + quote!() + }; + // We only generate the isolate if we need it but don't need a scope. We call it `scope`. let with_isolate = if generator_state.needs_isolate && !generator_state.needs_scope { @@ -125,12 +131,6 @@ pub(crate) fn generate_dispatch_slow( quote!() }; - let with_stack_trace = if generator_state.needs_stack_trace { - with_stack_trace(generator_state) - } else { - quote!() - }; - Ok( gs_quote!(generator_state(opctx, info, slow_function, slow_function_metrics) => { #[inline(always)] @@ -196,12 +196,12 @@ pub(crate) fn with_stack_trace( generator_state: &mut GeneratorState, ) -> TokenStream { generator_state.needs_opctx = true; + generator_state.needs_scope = true; gs_quote!(generator_state(stack_trace, opctx, scope) => (let #stack_trace = if #opctx.enable_stack_trace_arg { - let hs = &mut deno_core::v8::HandleScope::new(&mut #scope); - let stack_trace_msg = deno_core::v8::String::empty(hs); - let stack_trace_error = deno_core::v8::Exception::error(hs, stack_trace_msg.into()); - let js_error = deno_core::error::JsError::from_v8_exception(hs, stack_trace_error); + let stack_trace_msg = deno_core::v8::String::empty(&mut #scope); + let stack_trace_error = deno_core::v8::Exception::error(&mut #scope, stack_trace_msg.into()); + let js_error = deno_core::error::JsError::from_v8_exception(&mut #scope, stack_trace_error); Some(js_error.frames) } else { None };) ) diff --git a/ops/op2/test_cases/async/async_stack_trace.out b/ops/op2/test_cases/async/async_stack_trace.out index fecf76bc5..776aa4cda 100644 --- a/ops/op2/test_cases/async/async_stack_trace.out +++ b/ops/op2/test_cases/async/async_stack_trace.out @@ -45,14 +45,13 @@ pub const fn op_async_stack_trace() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let stack_trace = if opctx.enable_stack_trace_arg { - let hs = &mut deno_core::v8::HandleScope::new(&mut scope); - let stack_trace_msg = deno_core::v8::String::empty(hs); + let stack_trace_msg = deno_core::v8::String::empty(&mut scope); let stack_trace_error = deno_core::v8::Exception::error( - hs, + &mut scope, stack_trace_msg.into(), ); let js_error = deno_core::error::JsError::from_v8_exception( - hs, + &mut scope, stack_trace_error, ); Some(js_error.frames) diff --git a/ops/op2/test_cases/compiler_pass/sync.rs b/ops/op2/test_cases/compiler_pass/sync.rs index 0bb50f9c7..562aea581 100644 --- a/ops/op2/test_cases/compiler_pass/sync.rs +++ b/ops/op2/test_cases/compiler_pass/sync.rs @@ -3,6 +3,7 @@ deno_ops_compile_test_runner::prelude!(); use deno_core::v8; +use deno_core::error::JsStackFrame; // Collect a few examples that we'll smoke test when not running on the CI. @@ -58,3 +59,9 @@ fn op_smi_signed_return( ) -> Int32 { a as Int32 + b as Int32 + c as Int32 + d as Int32 } + +#[op2] +fn op_stack_trace( + #[string] _: String, + #[stack_trace] _: Option>, +) {} diff --git a/ops/op2/test_cases/sync/stack_trace.out b/ops/op2/test_cases/sync/stack_trace.out index a824276f6..3ae095c2d 100644 --- a/ops/op2/test_cases/sync/stack_trace.out +++ b/ops/op2/test_cases/sync/stack_trace.out @@ -45,14 +45,13 @@ const fn op_stack_trace() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let stack_trace = if opctx.enable_stack_trace_arg { - let hs = &mut deno_core::v8::HandleScope::new(&mut scope); - let stack_trace_msg = deno_core::v8::String::empty(hs); + let stack_trace_msg = deno_core::v8::String::empty(&mut scope); let stack_trace_error = deno_core::v8::Exception::error( - hs, + &mut scope, stack_trace_msg.into(), ); let js_error = deno_core::error::JsError::from_v8_exception( - hs, + &mut scope, stack_trace_error, ); Some(js_error.frames) diff --git a/ops/op2/test_cases/sync/stack_trace_scope.out b/ops/op2/test_cases/sync/stack_trace_scope.out new file mode 100644 index 000000000..201863252 --- /dev/null +++ b/ops/op2/test_cases/sync/stack_trace_scope.out @@ -0,0 +1,110 @@ +#[allow(non_camel_case_types)] +const fn op_stack_trace() -> ::deno_core::_ops::OpDecl { + #[allow(non_camel_case_types)] + struct op_stack_trace { + _unconstructable: ::std::marker::PhantomData<()>, + } + impl ::deno_core::_ops::Op for op_stack_trace { + const NAME: &'static str = stringify!(op_stack_trace); + const DECL: ::deno_core::_ops::OpDecl = ::deno_core::_ops::OpDecl::new_internal_op2( + ::deno_core::__op_name_fast!(op_stack_trace), + false, + false, + 2usize as u8, + false, + Self::v8_fn_ptr as _, + Self::v8_fn_ptr_metrics as _, + None, + None, + ::deno_core::OpMetadata { + ..::deno_core::OpMetadata::default() + }, + ); + } + impl op_stack_trace { + pub const fn name() -> &'static str { + ::NAME + } + #[inline(always)] + fn slow_function_impl<'s>( + info: &'s deno_core::v8::FunctionCallbackInfo, + ) -> usize { + #[cfg(debug_assertions)] + let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( + &::DECL, + ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; + let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); + let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( + info, + ); + let opctx: &'s _ = unsafe { + &*(deno_core::v8::Local::< + deno_core::v8::External, + >::cast_unchecked(args.data()) + .value() as *const deno_core::_ops::OpCtx) + }; + let stack_trace = if opctx.enable_stack_trace_arg { + let stack_trace_msg = deno_core::v8::String::empty(&mut scope); + let stack_trace_error = deno_core::v8::Exception::error( + &mut scope, + stack_trace_msg.into(), + ); + let js_error = deno_core::error::JsError::from_v8_exception( + &mut scope, + stack_trace_error, + ); + Some(js_error.frames) + } else { + None + }; + let result = { + let arg0 = args.get(0usize as i32); + let arg0 = deno_core::_ops::to_string(&mut scope, &arg0); + let arg1 = stack_trace; + Self::call(arg0, arg1) + }; + deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv); + return 0; + } + extern "C" fn v8_fn_ptr<'s>(info: *const deno_core::v8::FunctionCallbackInfo) { + let info: &'s _ = unsafe { &*info }; + Self::slow_function_impl(info); + } + extern "C" fn v8_fn_ptr_metrics<'s>( + info: *const deno_core::v8::FunctionCallbackInfo, + ) { + let info: &'s _ = unsafe { &*info }; + let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( + info, + ); + let opctx: &'s _ = unsafe { + &*(deno_core::v8::Local::< + deno_core::v8::External, + >::cast_unchecked(args.data()) + .value() as *const deno_core::_ops::OpCtx) + }; + deno_core::_ops::dispatch_metrics_slow( + opctx, + deno_core::_ops::OpMetricsEvent::Dispatched, + ); + let res = Self::slow_function_impl(info); + if res == 0 { + deno_core::_ops::dispatch_metrics_slow( + opctx, + deno_core::_ops::OpMetricsEvent::Completed, + ); + } else { + deno_core::_ops::dispatch_metrics_slow( + opctx, + deno_core::_ops::OpMetricsEvent::Error, + ); + } + } + } + impl op_stack_trace { + #[inline(always)] + fn call(_: String, _: Option>) {} + } + ::DECL +} diff --git a/ops/op2/test_cases/sync/stack_trace_scope.rs b/ops/op2/test_cases/sync/stack_trace_scope.rs new file mode 100644 index 000000000..1b2934293 --- /dev/null +++ b/ops/op2/test_cases/sync/stack_trace_scope.rs @@ -0,0 +1,11 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +#![deny(warnings)] +deno_ops_compile_test_runner::prelude!(); + +use deno_core::error::JsStackFrame; + +#[op2] +fn op_stack_trace( + #[string] _: String, + #[stack_trace] _: Option>, +) {}