Repository: denoland/deno_core
Pull request #858 (2024-08-01 16:37:09)

fix: Error.pST callsite methods should be on prototype
- **chore: report cause when JsStackFrame fails to deserialize**
- **fix: Error.pST callsite methods should be on prototype**


CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/denoland/deno_core?pullRequest=858) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our [Contributor License Agreement](https://cla-assistant.io/denoland/deno_core?pullRequest=858) before we can accept your contribution.<br/>**1** out of **2** committers have signed the CLA.<br/><br/>:white_check_mark: lucacasonato<br/>:x: marvinhagemeister<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/denoland/deno_core?pullRequest=858) it.</sub>

Patch:
diff --git a/core/error.rs b/core/error.rs
index fd83744..0c9a127 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<v8::Local<'a, T>>
 where
-  v8::Local<'a, T>: TryFrom<v8::Local<'a, v8::Value>>,
+  v8::Local<'a, T>: TryFrom<v8::Local<'a, v8::Value>, Error: Debug>,
 {
-  get_property(scope, object, key)?
-    .try_cast::<v8::Function>()
-    .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::<v8::Function>()
+  {
+    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::<v8::FunctionTemplate>::new(
-    move |scope: &mut v8::HandleScope<'_>,
-          args: v8::FunctionCallbackArguments<'_>,
-          rv: v8::ReturnValue<'_>| {
-      f(scope, args, rv);
-    },
-  )
-  .build(scope)
+  this: v8::Local<'_, v8::Object>,
+) -> Option<v8::Local<'a, v8::Object>> {
+  let orig_key = original_call_site_key(scope);
+  let Some(orig) = this
+    .get_private(scope, orig_key)
+    .and_then(|v| v8::Local::<v8::Object>::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<String> {
-  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::<v8::Object>()
+        .get(scope, key)
+        .unwrap()
+        .cast::<v8::Function>()
+        .call(scope, orig.into(), &[]);
+      rv.set(orig_ret.unwrap_or_else(|| v8::undefined(scope).into()));
+    }
+  };
+}
+
+fn maybe_to_path_str(string: &str) -> Option<String> {
+  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<String> {
   }
 }
 
-fn original_call_site<'s>(
-  scope: &mut v8::HandleScope<'s>,
-  object: v8::Local<v8::Object>,
-) -> 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::<v8::String>(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::<v8::String>(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::<v8::String>(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::<v8::Function>()
-                  .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::<v8::FunctionTemplate>::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::<v8::String>(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::<v8::String>(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::<v8::String>(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::<v8::Object>();
-      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::<v8::Object>();
-    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::<NoAnsiColors>(&frame))
       .unwrap();
   }
diff --git a/core/runtime/bindings.rs b/core/runtime/bindings.rs
index 9f6a406..264ab28 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 a0cf375..d139cb1 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<EvalContextCodeCacheReadyCb>,
   pub(crate) cppgc_template: RefCell<Option<v8::Global<v8::FunctionTemplate>>>,
-  pub(crate) callsite_template: RefCell<Option<v8::Global<v8::ObjectTemplate>>>,
+  pub(crate) callsite_prototype: RefCell<Option<v8::Global<v8::Object>>>,
   waker: Arc<AtomicWaker>,
   /// Accessed through [`JsRuntimeState::with_inspector`].
   inspector: RefCell<Option<Rc<RefCell<JsRuntimeInspector>>>>,
@@ -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 0fad47f..b106d9c 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",


Test patch:
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 0000000..d9eba84
--- /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 0000000..c8c9348
--- /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 e97df85..00494ac 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,