From 8492b3030a9442200b5511d9ef6ea10bbb252857 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 1 Aug 2024 20:47:16 +0200 Subject: [PATCH] fix: Error.pST callsite methods should be on prototype (#858) - chore: report cause when JsStackFrame fails to deserialize - fix: Error.pST callsite methods should be on prototype --------- Co-authored-by: Marvin Hagemeister Co-authored-by: Nathan Whitaker --- core/error.rs | 373 +++++++++++------- core/runtime/bindings.rs | 56 ++- core/runtime/jsruntime.rs | 10 +- core/runtime/v8_static_strings.rs | 2 +- .../error_prepare_stack_trace.out | 40 ++ .../error_prepare_stack_trace.ts | 29 ++ testing/lib.rs | 1 + 7 files changed, 354 insertions(+), 157 deletions(-) create mode 100644 testing/integration/error_prepare_stack_trace/error_prepare_stack_trace.out create mode 100644 testing/integration/error_prepare_stack_trace/error_prepare_stack_trace.ts diff --git a/core/error.rs b/core/error.rs index fd83744c87d892..0c9a1277a9a3ba 100644 --- a/core/error.rs +++ b/core/error.rs @@ -9,6 +9,7 @@ use std::fmt::Formatter; use std::fmt::Write as _; use anyhow::Error; +use v8::Object; use crate::runtime::v8_static_strings; use crate::runtime::JsRealm; @@ -328,7 +329,19 @@ impl JsStackFrame { macro_rules! call { ($key: ident : $t: ty) => {{ let res = call_method(scope, callsite, $key, &[])?; - let res: $t = serde_v8::from_v8(scope, res).ok()?; + let res: $t = match serde_v8::from_v8(scope, res) { + Ok(res) => res, + Err(err) => { + let message = format!( + "Failed to deserialize return value from callsite property '{}' to correct type: {err:?}.", + $key + ); + let message = v8::String::new(scope, &message).unwrap(); + let exception = v8::Exception::type_error(scope, message); + scope.throw_exception(exception); + return None; + } + }; res }}; ($key: ident) => { call!($key : _) }; @@ -430,13 +443,37 @@ fn call_method<'a, T>( args: &[v8::Local<'a, v8::Value>], ) -> Option> where - v8::Local<'a, T>: TryFrom>, + v8::Local<'a, T>: TryFrom, Error: Debug>, { - get_property(scope, object, key)? - .try_cast::() - .ok()? - .call(scope, object.into(), args) - .and_then(|v| v8::Local::try_from(v).ok()) + let func = match get_property(scope, object, key)?.try_cast::() + { + Ok(func) => func, + Err(err) => { + let message = + format!("Callsite property '{key}' is not a function: {err}"); + let message = v8::String::new(scope, &message).unwrap(); + let exception = v8::Exception::type_error(scope, message); + scope.throw_exception(exception); + return None; + } + }; + + let res = func.call(scope, object.into(), args)?; + + let result = match v8::Local::try_from(res) { + Ok(result) => result, + Err(err) => { + let message = format!( + "Failed to cast callsite method '{key}' return value to correct value: {err:?}." + ); + let message = v8::String::new(scope, &message).unwrap(); + let exception = v8::Exception::type_error(scope, message); + scope.throw_exception(exception); + return None; + } + }; + + Some(result) } #[derive(Default, serde::Deserialize)] @@ -606,9 +643,21 @@ impl JsError { let mut buf = Vec::with_capacity(frames_v8.length() as usize); for i in 0..frames_v8.length() { let callsite = frames_v8.get_index(scope, i).unwrap().cast(); - buf.push( - JsStackFrame::from_callsite_object(scope, callsite).unwrap(), - ); + let tc_scope = &mut v8::TryCatch::new(scope); + let Some(stack_frame) = + JsStackFrame::from_callsite_object(tc_scope, callsite) + else { + let message = tc_scope + .exception() + .expect( + "JsStackFrame::from_callsite_object raised an exception", + ) + .to_rust_string_lossy(tc_scope); + panic!( + "Failed to create JsStackFrame from callsite object: {message}" + ); + }; + buf.push(stack_frame); } buf } @@ -951,9 +1000,10 @@ v8_static_strings::v8_static_strings! { IS_ASYNC = "isAsync", IS_PROMISE_ALL = "isPromiseAll", GET_PROMISE_INDEX = "getPromiseIndex", - PREPARE_STACK_TRACE = "prepareStackTrace", - ORIGINAL = "_orig", TO_STRING = "toString", + PREPARE_STACK_TRACE = "prepareStackTrace", + ORIGINAL = "deno_core::original_call_site", + ERROR_RECEIVER_IS_NOT_VALID_CALLSITE_OBJECT = "The receiver is not a valid callsite object.", } #[inline(always)] @@ -967,39 +1017,58 @@ pub(crate) fn original_call_site_key<'a>( fn make_patched_callsite<'s>( scope: &mut v8::HandleScope<'s>, callsite: v8::Local<'s, v8::Object>, - template: v8::Local<'s, v8::ObjectTemplate>, + prototype: v8::Local<'s, v8::Object>, ) -> v8::Local<'s, v8::Object> { - let out_obj = template.new_instance(scope).unwrap(); + let out_obj = Object::new(scope); + out_obj.set_prototype(scope, prototype.into()); let orig_key = original_call_site_key(scope); out_obj.set_private(scope, orig_key, callsite.into()); out_obj } -fn make_fn_template<'a, F>( +fn original_call_site<'a>( scope: &mut v8::HandleScope<'a>, - f: F, -) -> v8::Local<'a, v8::FunctionTemplate> -where - F: for<'s, 't> FnOnce( - &mut v8::HandleScope<'s>, - v8::FunctionCallbackArguments<'t>, - v8::ReturnValue<'t>, - ) + Copy, -{ - v8::FunctionBuilder::::new( - move |scope: &mut v8::HandleScope<'_>, - args: v8::FunctionCallbackArguments<'_>, - rv: v8::ReturnValue<'_>| { - f(scope, args, rv); - }, - ) - .build(scope) + this: v8::Local<'_, v8::Object>, +) -> Option> { + let orig_key = original_call_site_key(scope); + let Some(orig) = this + .get_private(scope, orig_key) + .and_then(|v| v8::Local::::try_from(v).ok()) + else { + let message = ERROR_RECEIVER_IS_NOT_VALID_CALLSITE_OBJECT.v8_string(scope); + let exception = v8::Exception::type_error(scope, message); + scope.throw_exception(exception); + return None; + }; + Some(orig) } -fn maybe_to_path_str(s: &str) -> Option { - if s.starts_with("file://") { +macro_rules! make_callsite_fn { + ($fn:ident, $field:ident) => { + pub fn $fn( + scope: &mut v8::HandleScope<'_>, + args: v8::FunctionCallbackArguments<'_>, + mut rv: v8::ReturnValue<'_>, + ) { + let Some(orig) = original_call_site(scope, args.this()) else { + return; + }; + let key = $field.v8_string(scope).into(); + let orig_ret = orig + .cast::() + .get(scope, key) + .unwrap() + .cast::() + .call(scope, orig.into(), &[]); + rv.set(orig_ret.unwrap_or_else(|| v8::undefined(scope).into())); + } + }; +} + +fn maybe_to_path_str(string: &str) -> Option { + if string.starts_with("file://") { Some( - Url::parse(s) + Url::parse(string) .unwrap() .to_file_path() .unwrap() @@ -1011,13 +1080,80 @@ fn maybe_to_path_str(s: &str) -> Option { } } -fn original_call_site<'s>( - scope: &mut v8::HandleScope<'s>, - object: v8::Local, -) -> v8::Local<'s, v8::Object> { - // lookup the original call site object - let orig_key = original_call_site_key(scope); - object.get_private(scope, orig_key).unwrap().cast() +pub mod callsite_fns { + use super::*; + + make_callsite_fn!(get_this, GET_THIS); + make_callsite_fn!(get_type_name, GET_TYPE_NAME); + make_callsite_fn!(get_function, GET_FUNCTION); + make_callsite_fn!(get_function_name, GET_FUNCTION_NAME); + make_callsite_fn!(get_method_name, GET_METHOD_NAME); + + pub fn get_file_name( + scope: &mut v8::HandleScope<'_>, + args: v8::FunctionCallbackArguments<'_>, + mut rv: v8::ReturnValue<'_>, + ) { + let Some(orig) = original_call_site(scope, args.this()) else { + return; + }; + // call getFileName + let orig_ret = + call_method::(scope, orig, super::GET_FILE_NAME, &[]); + if let Some(ret_val) = orig_ret { + // strip off `file://` + let string = ret_val.to_rust_string_lossy(scope); + if let Some(file_name) = maybe_to_path_str(&string) { + let v8_str = crate::FastString::from(file_name).v8_string(scope).into(); + rv.set(v8_str); + } else { + rv.set(ret_val.into()); + } + } + } + + make_callsite_fn!(get_line_number, GET_LINE_NUMBER); + make_callsite_fn!(get_column_number, GET_COLUMN_NUMBER); + make_callsite_fn!(get_eval_origin, GET_EVAL_ORIGIN); + make_callsite_fn!(is_toplevel, IS_TOPLEVEL); + make_callsite_fn!(is_eval, IS_EVAL); + make_callsite_fn!(is_native, IS_NATIVE); + make_callsite_fn!(is_constructor, IS_CONSTRUCTOR); + make_callsite_fn!(is_async, IS_ASYNC); + make_callsite_fn!(is_promise_all, IS_PROMISE_ALL); + make_callsite_fn!(get_promise_index, GET_PROMISE_INDEX); + + pub fn to_string( + scope: &mut v8::HandleScope<'_>, + args: v8::FunctionCallbackArguments<'_>, + mut rv: v8::ReturnValue<'_>, + ) { + let Some(orig) = original_call_site(scope, args.this()) else { + return; + }; + // `this[kOriginalCallsite].toString()` + let Some(orig_to_string_v8) = + call_method::(scope, orig, TO_STRING, &[]) + else { + return; + }; + let orig_to_string = serde_v8::to_utf8(orig_to_string_v8, scope); + // `this[kOriginalCallsite].getFileName()` + let Some(orig_file_name) = + call_method::(scope, orig, GET_FILE_NAME, &[]) + else { + return; + }; + // replace file URL with file path in original `toString` + let orig_file_name = serde_v8::to_utf8(orig_file_name, scope); + if let Some(file_name) = maybe_to_path_str(&orig_file_name) { + let to_string = orig_to_string.replace(&orig_file_name, &file_name); + let v8_str = crate::FastString::from(to_string).v8_string(scope).into(); + rv.set(v8_str); + } else { + rv.set(orig_to_string_v8.into()); + } + } } /// Creates a template for a `Callsite`-like object, with @@ -1037,116 +1173,45 @@ fn original_call_site<'s>( /// } /// } /// ``` -pub(crate) fn make_callsite_template<'s>( +pub(crate) fn make_callsite_prototype<'s>( scope: &mut v8::HandleScope<'s>, -) -> v8::Local<'s, v8::ObjectTemplate> { +) -> v8::Local<'s, v8::Object> { let template = v8::ObjectTemplate::new(scope); - // effectively - // `$field() { return this[kOriginalCallsite].$field() }` - macro_rules! make_delegate { - ($($field: ident),+ $(,)?) => { - $( - { - let key = $field.v8_string(scope).into(); - template.set_with_attr( - key, - make_fn_template( - scope, - |scope, args, mut rv| { - let orig = original_call_site(scope, args.this()); - let key = $field.v8_string(scope).into(); - let orig_ret = orig - .get(scope, key) - .unwrap() - .cast::() - .call(scope, orig.into(), &[]); - rv.set(orig_ret.unwrap_or_else(|| v8::undefined(scope).into())); - }, - ) - .into(), - v8::PropertyAttribute::DONT_DELETE - | v8::PropertyAttribute::DONT_ENUM - | v8::PropertyAttribute::READ_ONLY, - ); - } - )+ + macro_rules! set_attr { + ($scope:ident, $template:ident, $fn:ident, $field:ident) => { + let key = $field.v8_string($scope).into(); + $template.set_with_attr( + key, + v8::FunctionBuilder::::new(callsite_fns::$fn) + .build($scope) + .into(), + v8::PropertyAttribute::DONT_DELETE + | v8::PropertyAttribute::DONT_ENUM + | v8::PropertyAttribute::READ_ONLY, + ); }; } - // delegate to the original callsite object - make_delegate!( - // excludes getFileName and toString, which we'll override below - GET_THIS, - GET_TYPE_NAME, - GET_FUNCTION, - GET_FUNCTION_NAME, - GET_METHOD_NAME, - GET_LINE_NUMBER, - GET_COLUMN_NUMBER, - GET_EVAL_ORIGIN, - IS_TOPLEVEL, - IS_EVAL, - IS_NATIVE, - IS_CONSTRUCTOR, - IS_ASYNC, - IS_PROMISE_ALL, - GET_PROMISE_INDEX, - ); - - let get_file_name_key = GET_FILE_NAME.v8_string(scope).into(); - template.set_with_attr( - get_file_name_key, - make_fn_template(scope, |scope, args, mut rv| { - // lookup the original call site object - let orig = original_call_site(scope, args.this()); - // call getFileName - let orig_ret = call_method::(scope, orig, GET_FILE_NAME, &[]); - if let Some(ret_val) = orig_ret { - // strip off `file://` - let string = serde_v8::to_utf8(ret_val, scope); - let file_name = maybe_to_path_str(&string).unwrap_or(string); - let v8_str = crate::FastString::from(file_name).v8_string(scope).into(); - rv.set(v8_str); - } - }) - .into(), - v8::PropertyAttribute::DONT_DELETE - | v8::PropertyAttribute::DONT_ENUM - | v8::PropertyAttribute::READ_ONLY, - ); - - let to_string_key = TO_STRING.v8_string(scope).into(); - template.set_with_attr( - to_string_key, - make_fn_template(scope, |scope, args, mut rv| { - let orig = original_call_site(scope, args.this()); - // `this[kOriginalCallsite].toString()` - let orig_to_string_v8 = - call_method::(scope, orig, TO_STRING, &[]).unwrap(); - let orig_to_string = serde_v8::to_utf8(orig_to_string_v8, scope); - // `this[kOriginalCallsite].getFileName()` - if let Some(orig_file_name) = - call_method::(scope, orig, GET_FILE_NAME, &[]) - { - // replace file URL with file path in original `toString` - let orig_file_name = serde_v8::to_utf8(orig_file_name, scope); - if let Some(file_name) = maybe_to_path_str(&orig_file_name) { - let to_string = orig_to_string.replace(&orig_file_name, &file_name); - let v8_str = - crate::FastString::from(to_string).v8_string(scope).into(); - rv.set(v8_str); - return; - } - } - rv.set(orig_to_string_v8.into()); - }) - .into(), - v8::PropertyAttribute::DONT_DELETE - | v8::PropertyAttribute::DONT_ENUM - | v8::PropertyAttribute::READ_ONLY, - ); - template + set_attr!(scope, template, get_this, GET_THIS); + set_attr!(scope, template, get_type_name, GET_TYPE_NAME); + set_attr!(scope, template, get_function, GET_FUNCTION); + set_attr!(scope, template, get_function_name, GET_FUNCTION_NAME); + set_attr!(scope, template, get_method_name, GET_METHOD_NAME); + set_attr!(scope, template, get_file_name, GET_FILE_NAME); + set_attr!(scope, template, get_line_number, GET_LINE_NUMBER); + set_attr!(scope, template, get_column_number, GET_COLUMN_NUMBER); + set_attr!(scope, template, get_eval_origin, GET_EVAL_ORIGIN); + set_attr!(scope, template, is_toplevel, IS_TOPLEVEL); + set_attr!(scope, template, is_eval, IS_EVAL); + set_attr!(scope, template, is_native, IS_NATIVE); + set_attr!(scope, template, is_constructor, IS_CONSTRUCTOR); + set_attr!(scope, template, is_async, IS_ASYNC); + set_attr!(scope, template, is_promise_all, IS_PROMISE_ALL); + set_attr!(scope, template, get_promise_index, GET_PROMISE_INDEX); + set_attr!(scope, template, to_string, TO_STRING); + + template.new_instance(scope).unwrap() } // called by V8 whenever a stack trace is created. @@ -1177,15 +1242,15 @@ pub fn prepare_stack_trace_callback<'s>( let len = callsites.length(); let mut patched = Vec::with_capacity(len as usize); let template = JsRuntime::state_from(scope) - .callsite_template + .callsite_prototype .borrow() .clone() .unwrap(); - let template = v8::Local::new(scope, template); + let prototype = v8::Local::new(scope, template); for i in 0..len { let callsite = callsites.get_index(scope, i).unwrap().cast::(); - patched.push(make_patched_callsite(scope, callsite, template).into()); + patched.push(make_patched_callsite(scope, callsite, prototype).into()); } let patched_callsites = v8::Array::new_with_elements(scope, &patched); @@ -1230,7 +1295,15 @@ fn format_stack_trace<'s>( // format each stack frame for i in 0..callsites.length() { let callsite = callsites.get_index(scope, i).unwrap().cast::(); - let frame = JsStackFrame::from_callsite_object(scope, callsite).unwrap(); + let tc_scope = &mut v8::TryCatch::new(scope); + let Some(frame) = JsStackFrame::from_callsite_object(tc_scope, callsite) + else { + let message = tc_scope + .exception() + .expect("JsStackFrame::from_callsite_object raised an exception") + .to_rust_string_lossy(tc_scope); + panic!("Failed to create JsStackFrame from callsite object: {message}"); + }; write!(result, "\n at {}", format_frame::(&frame)) .unwrap(); } diff --git a/core/runtime/bindings.rs b/core/runtime/bindings.rs index 9f6a406f138fb3..264ab281d27c59 100644 --- a/core/runtime/bindings.rs +++ b/core/runtime/bindings.rs @@ -10,6 +10,7 @@ use super::jsruntime::BUILTIN_SOURCES; use super::jsruntime::CONTEXT_SETUP_SOURCES; use super::v8_static_strings::*; use crate::cppgc::cppgc_template_constructor; +use crate::error::callsite_fns; use crate::error::has_call_site; use crate::error::is_instance_of_error; use crate::error::throw_type_error; @@ -42,7 +43,8 @@ pub(crate) fn create_external_references( + BUILTIN_SOURCES.len() + (ops.len() * 4) + additional_references.len() - + sources.len(), + + sources.len() + + 18, // for callsite_fns ); references.push(v8::ExternalReference { @@ -103,6 +105,58 @@ pub(crate) fn create_external_references( }) } + references.push(v8::ExternalReference { + function: callsite_fns::get_this.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::get_type_name.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::get_function.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::get_function_name.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::get_method_name.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::get_file_name.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::get_line_number.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::get_column_number.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::get_eval_origin.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::is_toplevel.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::is_eval.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::is_native.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::is_constructor.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::is_async.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::is_promise_all.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::get_promise_index.map_fn_to(), + }); + references.push(v8::ExternalReference { + function: callsite_fns::to_string.map_fn_to(), + }); + v8::ExternalReferences::new(&references) } diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs index a0cf375f6b987a..d139cb158665f8 100644 --- a/core/runtime/jsruntime.rs +++ b/core/runtime/jsruntime.rs @@ -434,7 +434,7 @@ pub struct JsRuntimeState { pub(crate) eval_context_code_cache_ready_cb: Option, pub(crate) cppgc_template: RefCell>>, - pub(crate) callsite_template: RefCell>>, + pub(crate) callsite_prototype: RefCell>>, waker: Arc, /// Accessed through [`JsRuntimeState::with_inspector`]. inspector: RefCell>>>, @@ -846,7 +846,7 @@ impl JsRuntime { inspector: None.into(), has_inspector: false.into(), cppgc_template: None.into(), - callsite_template: None.into(), + callsite_prototype: None.into(), import_assertions_support: options.import_assertions_support, }); @@ -1012,11 +1012,11 @@ impl JsRuntime { let scope = &mut context_scope; let context = v8::Local::new(scope, &main_context); - let callsite_template = crate::error::make_callsite_template(scope); + let callsite_prototype = crate::error::make_callsite_prototype(scope); state_rc - .callsite_template + .callsite_prototype .borrow_mut() - .replace(v8::Global::new(scope, callsite_template)); + .replace(v8::Global::new(scope, callsite_prototype)); // ...followed by creation of `Deno.core` namespace, as well as internal // infrastructure to provide JavaScript bindings for ops... diff --git a/core/runtime/v8_static_strings.rs b/core/runtime/v8_static_strings.rs index 0fad47fdbdae1a..b106d9c767a799 100644 --- a/core/runtime/v8_static_strings.rs +++ b/core/runtime/v8_static_strings.rs @@ -12,7 +12,7 @@ pub(crate) use v8_static_strings; v8_static_strings!( BUILD_CUSTOM_ERROR = "buildCustomError", CALL_CONSOLE = "callConsole", - CALL_SITE_EVALS = "callSiteEvals", + CALL_SITE_EVALS = "deno_core::call_site_evals", CAUSE = "cause", CODE = "code", CONSOLE = "console", diff --git a/testing/integration/error_prepare_stack_trace/error_prepare_stack_trace.out b/testing/integration/error_prepare_stack_trace/error_prepare_stack_trace.out new file mode 100644 index 00000000000000..d9eba840f74a1f --- /dev/null +++ b/testing/integration/error_prepare_stack_trace/error_prepare_stack_trace.out @@ -0,0 +1,40 @@ +[ + "getThis", + "getTypeName", + "getFunction", + "getFunctionName", + "getMethodName", + "getFileName", + "getLineNumber", + "getColumnNumber", + "getEvalOrigin", + "isToplevel", + "isEval", + "isNative", + "isConstructor", + "isAsync", + "isPromiseAll", + "getPromiseIndex", + "toString" +] +[] +[ + "test:///integration/error_prepare_stack_trace/error_prepare_stack_trace.ts:12:13" +] +getThis() threw an error: The receiver is not a valid callsite object. +getTypeName() threw an error: The receiver is not a valid callsite object. +getFunction() threw an error: The receiver is not a valid callsite object. +getFunctionName() threw an error: The receiver is not a valid callsite object. +getMethodName() threw an error: The receiver is not a valid callsite object. +getFileName() threw an error: The receiver is not a valid callsite object. +getLineNumber() threw an error: The receiver is not a valid callsite object. +getColumnNumber() threw an error: The receiver is not a valid callsite object. +getEvalOrigin() threw an error: The receiver is not a valid callsite object. +isToplevel() threw an error: The receiver is not a valid callsite object. +isEval() threw an error: The receiver is not a valid callsite object. +isNative() threw an error: The receiver is not a valid callsite object. +isConstructor() threw an error: The receiver is not a valid callsite object. +isAsync() threw an error: The receiver is not a valid callsite object. +isPromiseAll() threw an error: The receiver is not a valid callsite object. +getPromiseIndex() threw an error: The receiver is not a valid callsite object. +toString() threw an error: The receiver is not a valid callsite object. diff --git a/testing/integration/error_prepare_stack_trace/error_prepare_stack_trace.ts b/testing/integration/error_prepare_stack_trace/error_prepare_stack_trace.ts new file mode 100644 index 00000000000000..c8c9348b061ee3 --- /dev/null +++ b/testing/integration/error_prepare_stack_trace/error_prepare_stack_trace.ts @@ -0,0 +1,29 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +let errorCallsitePrototype; +// deno-lint-ignore no-explicit-any +(Error as any).prepareStackTrace = (_err, frames) => { + return frames.map((frame) => { + errorCallsitePrototype = Object.getPrototypeOf(frame); + console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(frame))); + console.log(Object.getOwnPropertyNames(frame)); + return frame.toString(); + }); +}; + +console.log(new Error("fail").stack); + +for (const prop of Object.getOwnPropertyNames(errorCallsitePrototype)) { + if (typeof errorCallsitePrototype[prop] === "function") { + let error; + try { + errorCallsitePrototype[prop](); + } catch (e) { + error = e; + } + if (error) { + console.log(`${prop}() threw an error: ${error.message}`); + } else { + console.log(`${prop}() did not throw an error`); + } + } +} diff --git a/testing/lib.rs b/testing/lib.rs index e97df85e62f0fd..00494ac058ebd1 100644 --- a/testing/lib.rs +++ b/testing/lib.rs @@ -63,6 +63,7 @@ integration_test!( error_rejection_order, error_eval_stack, error_ext_stack, + error_prepare_stack_trace, error_with_stack, error_without_stack, main_module_handler,