0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-11-21 15:04:33 -05:00

Add Isolate::set_prepare_stack_trace_callback() (#594)

Refs: https://github.com/denoland/deno/issues/7644
This commit is contained in:
Ben Noordhuis 2021-02-08 13:11:48 +01:00 committed by GitHub
parent 8809068f01
commit 64aa11e34d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 162 additions and 1 deletions

View file

@ -206,6 +206,11 @@ void v8__Isolate__RequestInterrupt(v8::Isolate* isolate,
isolate->RequestInterrupt(callback, data);
}
void v8__Isolate__SetPrepareStackTraceCallback(
v8::Isolate* isolate, v8::PrepareStackTraceCallback callback) {
isolate->SetPrepareStackTraceCallback(callback);
}
void v8__Isolate__SetPromiseHook(v8::Isolate* isolate, v8::PromiseHook hook) {
isolate->SetPromiseHook(hook);
}

View file

@ -4,15 +4,20 @@ use crate::isolate_create_params::raw;
use crate::isolate_create_params::CreateParams;
use crate::promise::PromiseRejectMessage;
use crate::scope::data::ScopeData;
use crate::scope::HandleScope;
use crate::support::BuildTypeIdHasher;
use crate::support::MapFnFrom;
use crate::support::MapFnTo;
use crate::support::Opaque;
use crate::support::ToCFn;
use crate::support::UnitType;
use crate::wasm::trampoline;
use crate::wasm::WasmStreaming;
use crate::Array;
use crate::CallbackScope;
use crate::Context;
use crate::FixedArray;
use crate::Function;
use crate::HandleScope;
use crate::Local;
use crate::Message;
use crate::Module;
@ -138,6 +143,23 @@ pub type OOMErrorCallback =
#[derive(Debug)]
pub struct HeapStatistics([usize; 16]);
// Windows x64 ABI: MaybeLocal<Value> returned on the stack.
#[cfg(target_os = "windows")]
pub type PrepareStackTraceCallback<'s> = extern "C" fn(
*mut *const Value,
Local<'s, Context>,
Local<'s, Value>,
Local<'s, Array>,
) -> *mut *const Value;
// System V ABI: MaybeLocal<Value> returned in a register.
#[cfg(not(target_os = "windows"))]
pub type PrepareStackTraceCallback<'s> = extern "C" fn(
Local<'s, Context>,
Local<'s, Value>,
Local<'s, Array>,
) -> *const Value;
extern "C" {
fn v8__Isolate__New(params: *const raw::CreateParams) -> *mut Isolate;
fn v8__Isolate__Dispose(this: *mut Isolate);
@ -172,6 +194,10 @@ extern "C" {
isolate: *mut Isolate,
callback: OOMErrorCallback,
);
fn v8__Isolate__SetPrepareStackTraceCallback(
isolate: *mut Isolate,
callback: PrepareStackTraceCallback,
);
fn v8__Isolate__SetPromiseHook(isolate: *mut Isolate, hook: PromiseHook);
fn v8__Isolate__SetPromiseRejectCallback(
isolate: *mut Isolate,
@ -481,6 +507,26 @@ impl Isolate {
unsafe { v8__Isolate__AddMessageListener(self, callback) }
}
/// This specifies the callback called when the stack property of Error
/// is accessed.
///
/// PrepareStackTraceCallback is called when the stack property of an error is
/// first accessed. The return value will be used as the stack value. If this
/// callback is registed, the |Error.prepareStackTrace| API will be disabled.
/// |sites| is an array of call sites, specified in
/// https://v8.dev/docs/stack-trace-api
pub fn set_prepare_stack_trace_callback<'s>(
&mut self,
callback: impl MapFnTo<PrepareStackTraceCallback<'s>>,
) {
// Note: the C++ API returns a MaybeLocal but V8 asserts at runtime when
// it's empty. That is, you can't return None and that's why the Rust API
// expects Local<Value> instead of Option<Local<Value>>.
unsafe {
v8__Isolate__SetPrepareStackTraceCallback(self, callback.map_fn_to())
};
}
/// Set the PromiseHook callback for various promise lifecycle
/// events.
pub fn set_promise_hook(&mut self, hook: PromiseHook) {
@ -900,3 +946,36 @@ impl Default for HeapStatistics {
}
}
}
impl<'s, F> MapFnFrom<F> for PrepareStackTraceCallback<'s>
where
F: UnitType
+ Fn(
&mut HandleScope<'s>,
Local<'s, Value>,
Local<'s, Array>,
) -> Local<'s, Value>,
{
// Windows x64 ABI: MaybeLocal<Value> returned on the stack.
#[cfg(target_os = "windows")]
fn mapping() -> Self {
let f = |ret_ptr, context, error, sites| {
let mut scope: CallbackScope = unsafe { CallbackScope::new(context) };
let r = (F::get())(&mut scope, error, sites);
unsafe { std::ptr::write(ret_ptr, &*r as *const _) };
ret_ptr
};
f.to_c_fn()
}
// System V ABI: MaybeLocal<Value> returned in a register.
#[cfg(not(target_os = "windows"))]
fn mapping() -> Self {
let f = |context, error, sites| {
let mut scope: CallbackScope = unsafe { CallbackScope::new(context) };
let r = (F::get())(&mut scope, error, sites);
&*r as *const _
};
f.to_c_fn()
}
}

View file

@ -4638,3 +4638,80 @@ fn oom_callback() {
// Don't attempt to trigger the OOM callback since we don't have a safe way to
// recover from it.
}
#[test]
fn prepare_stack_trace_callback() {
thread_local! {
static SITES: RefCell<Option<v8::Global<v8::Array>>> = RefCell::new(None);
}
let script = r#"
function g() { throw new Error("boom") }
function f() { g() }
try {
f()
} catch (e) {
e.stack
}
"#;
let _setup_guard = setup();
let isolate = &mut v8::Isolate::new(Default::default());
isolate.set_prepare_stack_trace_callback(callback);
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
let scope = &mut v8::TryCatch::new(scope);
let result = eval(scope, script).unwrap();
assert_eq!(Some(42), result.uint32_value(scope));
let sites = SITES.with(|slot| slot.borrow_mut().take()).unwrap();
let sites = v8::Local::new(scope, sites);
assert_eq!(3, sites.length());
let scripts = [
r#"
if ("g" !== site.getFunctionName()) throw "fail";
if (2 !== site.getLineNumber()) throw "fail";
"#,
r#"
if ("f" !== site.getFunctionName()) throw "fail";
if (3 !== site.getLineNumber()) throw "fail";
"#,
r#"
if (null !== site.getFunctionName()) throw "fail";
if (5 !== site.getLineNumber()) throw "fail";
"#,
];
let global = context.global(scope);
let name = v8::String::new(scope, "site").unwrap().into();
for i in 0..3 {
let site = sites.get_index(scope, i).unwrap();
global.set(scope, name, site).unwrap();
let script = scripts[i as usize];
let result = eval(scope, script);
assert!(result.is_some());
}
fn callback<'s>(
scope: &mut v8::HandleScope<'s>,
error: v8::Local<v8::Value>,
sites: v8::Local<v8::Array>,
) -> v8::Local<'s, v8::Value> {
let message = v8::Exception::create_message(scope, error);
let actual = message.get(scope).to_rust_string_lossy(scope);
assert_eq!(actual, "Uncaught Error: boom");
SITES.with(|slot| {
let mut slot = slot.borrow_mut();
assert!(slot.is_none());
*slot = Some(v8::Global::new(scope, sites));
});
v8::Integer::new(scope, 42).into()
}
}