Repository: denoland/deno_core
Pull request #874 (2024-08-17 20:01:43)

feat: update rusty_v8 (V8 12.9)
- Fast functions will no longer be able to use `fallback`, instead they should raise exceptions directly
- Fast function wasm memory has been removed


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 020edf1..b1eb73f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -67,7 +67,7 @@ jobs:
               os: macOS-latest
               jobs: [lint, test]
             windows:
-              os: windows-2019
+              os: windows-2022
               jobs: [lint, test]
       - name: Read job configuration (tag)
         uses: cloudposse/github-action-yaml-config-query@main
diff --git a/Cargo.lock b/Cargo.lock
index 191fcaf..19260b7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2728,9 +2728,9 @@ dependencies = [
 
 [[package]]
 name = "v8"
-version = "0.103.0"
+version = "0.104.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56d72310e5b559c0a8165a5d6bdf4c975ba77c61461039ccf615ce3bbe489ca5"
+checksum = "834593934c4b3693706bcbd24d4e2accf02a318a62efd2a4e5e33b5452074882"
 dependencies = [
  "bindgen",
  "bitflags",
diff --git a/Cargo.toml b/Cargo.toml
index 83d34bc..2ca6f1a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,7 +24,7 @@ deno_ops = { version = "0.181.0", path = "./ops" }
 serde_v8 = { version = "0.214.0", path = "./serde_v8" }
 deno_core_testing = { path = "./testing" }
 
-v8 = { version = "0.103.0", default-features = false }
+v8 = { version = "0.104.0", default-features = false }
 deno_ast = { version = "=0.40.0", features = ["transpiling"] }
 deno_unsync = "0.4.0"
 deno_core_icudata = "0.0.73"
diff --git a/core/benches/ops/async.rs b/core/benches/ops/async.rs
index 84656ff..a72da53 100644
--- a/core/benches/ops/async.rs
+++ b/core/benches/ops/async.rs
@@ -26,7 +26,7 @@ deno_core::extension!(
   ],
 );
 
-#[op2]
+#[op2(fast)]
 pub fn op_call_promise_resolver(scope: &mut v8::HandleScope, f: &v8::Function) {
   let recv = v8::undefined(scope).into();
   f.call(scope, recv, &[]);
diff --git a/core/benches/ops/sync.rs b/core/benches/ops/sync.rs
index 686a65d..1c64051 100644
--- a/core/benches/ops/sync.rs
+++ b/core/benches/ops/sync.rs
@@ -101,7 +101,7 @@ pub fn op_string_option_u32(#[string] s: &str) -> Option<u32> {
 #[op2(fast)]
 pub fn op_local(_s: v8::Local<v8::String>) {}
 
-#[op2]
+#[op2(fast)]
 pub fn op_local_scope(_scope: &mut v8::HandleScope, _s: v8::Local<v8::String>) {
 }
 
@@ -118,7 +118,7 @@ pub fn op_global_scope(
 ) {
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_scope(_scope: &mut v8::HandleScope) {}
 
 #[op2(nofast)]
diff --git a/core/cppgc.rs b/core/cppgc.rs
index 455a962..4327e3f 100644
--- a/core/cppgc.rs
+++ b/core/cppgc.rs
@@ -88,7 +88,7 @@ impl<T: GarbageCollected> std::ops::Deref for Ptr<T> {
 #[doc(hidden)]
 #[allow(clippy::needless_lifetimes)]
 pub fn try_unwrap_cppgc_object<'sc, T: GarbageCollected + 'static>(
-  scope: &mut v8::HandleScope<'sc>,
+  isolate: &mut v8::Isolate,
   val: v8::Local<'sc, v8::Value>,
 ) -> Option<Ptr<T>> {
   let Ok(obj): Result<v8::Local<v8::Object>, _> = val.try_into() else {
@@ -100,7 +100,7 @@ pub fn try_unwrap_cppgc_object<'sc, T: GarbageCollected + 'static>(
   }
 
   let obj =
-    unsafe { v8::Object::unwrap::<CPPGC_TAG, CppGcObject<T>>(scope, obj) }?;
+    unsafe { v8::Object::unwrap::<CPPGC_TAG, CppGcObject<T>>(isolate, obj) }?;
 
   if obj.tag != TypeId::of::<T>() {
     return None;
diff --git a/core/examples/wasm.rs b/core/examples/wasm.rs
deleted file mode 100644
index 8ebf2af..0000000
--- a/core/examples/wasm.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-#![allow(deprecated)]
-use deno_core::*;
-use std::mem::transmute;
-use std::ptr::NonNull;
-
-struct WasmMemory(NonNull<v8::WasmMemoryObject>);
-
-fn wasm_memory_unchecked(state: &mut OpState) -> &mut [u8] {
-  let WasmMemory(global) = state.borrow::<WasmMemory>();
-  // SAFETY: `v8::Local` is always non-null pointer; the `HandleScope` is
-  // already on the stack, but we don't have access to it.
-  let memory_object = unsafe {
-    transmute::<NonNull<v8::WasmMemoryObject>, v8::Local<v8::WasmMemoryObject>>(
-      *global,
-    )
-  };
-  let backing_store = memory_object.buffer().get_backing_store();
-  let ptr = backing_store.data().unwrap().as_ptr() as *mut u8;
-  let len = backing_store.byte_length();
-  // SAFETY: `ptr` is a valid pointer to `len` bytes.
-  unsafe { std::slice::from_raw_parts_mut(ptr, len) }
-}
-
-#[op2]
-#[buffer]
-fn op_get_wasm_module() -> Vec<u8> {
-  include_bytes!("wasm.wasm").as_slice().to_vec()
-}
-
-#[op2(fast)]
-fn op_wasm(state: &mut OpState, #[memory(caller)] memory: Option<&mut [u8]>) {
-  let memory = memory.unwrap_or_else(|| wasm_memory_unchecked(state));
-  memory[0] = 69;
-}
-
-#[op2(fast)]
-fn op_wasm_mem(memory: &v8::WasmMemoryObject) {
-  // let memory = memory.unwrap_or_else(|| wasm_memory_unchecked(state));
-  // memory[0] = 69;
-  let len = memory.buffer().byte_length();
-  let slice = unsafe {
-    std::ptr::slice_from_raw_parts_mut(
-      memory.buffer().data().unwrap().as_ptr() as *mut u8,
-      len,
-    )
-    .as_mut()
-    .unwrap()
-  };
-  slice[0] = 68;
-}
-
-#[op2]
-fn op_set_wasm_mem(
-  state: &mut OpState,
-  #[global] memory: v8::Global<v8::WasmMemoryObject>,
-) {
-  state.put(WasmMemory(memory.into_raw()));
-}
-
-// Build a deno_core::Extension providing custom ops
-deno_core::extension!(
-  wasm_example,
-  ops = [op_get_wasm_module, op_wasm, op_wasm_mem, op_set_wasm_mem]
-);
-
-fn main() {
-  // Initialize a runtime instance
-  let mut runtime = JsRuntime::new(RuntimeOptions {
-    extensions: vec![wasm_example::init_ops()],
-    ..Default::default()
-  });
-
-  runtime
-    .execute_script("<usage>", ascii_str_include!("wasm.js"))
-    .unwrap();
-}
diff --git a/core/ops.rs b/core/ops.rs
index 02bb5af..6a8878f 100644
--- a/core/ops.rs
+++ b/core/ops.rs
@@ -1,6 +1,5 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
 
-use crate::error::AnyError;
 use crate::error::GetErrorClassFn;
 use crate::gotham_state::GothamState;
 use crate::io::ResourceTable;
@@ -11,7 +10,6 @@ use crate::FeatureChecker;
 use crate::OpDecl;
 use futures::task::AtomicWaker;
 use std::cell::RefCell;
-use std::cell::UnsafeCell;
 use std::ops::Deref;
 use std::ops::DerefMut;
 use std::rc::Rc;
@@ -92,8 +90,6 @@ pub struct OpCtx {
   pub(crate) decl: OpDecl,
   pub(crate) fast_fn_info: Option<CFunction>,
   pub(crate) metrics_fn: Option<OpMetricsFn>,
-  /// If the last fast op failed, stores the error to be picked up by the slow op.
-  pub(crate) last_fast_error: UnsafeCell<Option<AnyError>>,
 
   op_driver: Rc<OpDriverImpl>,
   runtime_state: *const JsRuntimeState,
@@ -129,7 +125,6 @@ impl OpCtx {
       decl,
       op_driver,
       fast_fn_info,
-      last_fast_error: UnsafeCell::new(None),
       isolate,
       metrics_fn,
     }
@@ -194,32 +189,6 @@ impl OpCtx {
     }
   }
 
-  /// This takes the last error from an [`OpCtx`], assuming that no other code anywhere
-  /// can hold a `&mut` to the last_fast_error field.
-  ///
-  /// # Safety
-  ///
-  /// Must only be called from op implementations.
-  #[inline(always)]
-  pub unsafe fn unsafely_take_last_error_for_ops_only(
-    &self,
-  ) -> Option<AnyError> {
-    let opt_mut = &mut *self.last_fast_error.get();
-    opt_mut.take()
-  }
-
-  /// This set the last error for an [`OpCtx`], assuming that no other code anywhere
-  /// can hold a `&mut` to the last_fast_error field.
-  ///
-  /// # Safety
-  ///
-  /// Must only be called from op implementations.
-  #[inline(always)]
-  pub unsafe fn unsafely_set_last_error_for_ops_only(&self, error: AnyError) {
-    let opt_mut = &mut *self.last_fast_error.get();
-    *opt_mut = Some(error);
-  }
-
   pub(crate) fn op_driver(&self) -> &OpDriverImpl {
     &self.op_driver
   }
diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs
index 41676c0..d1f62a5 100644
--- a/core/ops_builtin.rs
+++ b/core/ops_builtin.rs
@@ -378,7 +378,7 @@ fn op_format_file_name(#[string] file_name: &str) -> String {
   format_file_name(file_name)
 }
 
-#[op2]
+#[op2(fast)]
 fn op_str_byte_length(
   scope: &mut v8::HandleScope,
   value: v8::Local<v8::Value>,
diff --git a/core/ops_builtin_v8.rs b/core/ops_builtin_v8.rs
index 54bf21d..a686a93 100644
--- a/core/ops_builtin_v8.rs
+++ b/core/ops_builtin_v8.rs
@@ -44,25 +44,25 @@ pub fn op_set_handled_promise_rejection_handler(
   *exception_state.js_handled_promise_rejection_cb.borrow_mut() = f;
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_ref_op(scope: &mut v8::HandleScope, promise_id: i32) {
   let context_state = JsRealm::state_from_scope(scope);
   context_state.unrefed_ops.borrow_mut().remove(&promise_id);
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_unref_op(scope: &mut v8::HandleScope, promise_id: i32) {
   let context_state = JsRealm::state_from_scope(scope);
   context_state.unrefed_ops.borrow_mut().insert(promise_id);
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_leak_tracing_enable(scope: &mut v8::HandleScope, enabled: bool) {
   let context_state = JsRealm::state_from_scope(scope);
   context_state.activity_traces.set_enabled(enabled);
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_leak_tracing_submit(
   scope: &mut v8::HandleScope,
   #[smi] kind: u8,
@@ -168,7 +168,7 @@ pub fn op_timer_queue_immediate(
   context_state.timers.queue_timer(0, (task, 0)) as _
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_timer_cancel(scope: &mut v8::HandleScope, id: f64) {
   let context_state = JsRealm::state_from_scope(scope);
   context_state.timers.cancel_timer(id as _);
@@ -177,13 +177,13 @@ pub fn op_timer_cancel(scope: &mut v8::HandleScope, id: f64) {
     .complete(RuntimeActivityType::Timer, id as _);
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_timer_ref(scope: &mut v8::HandleScope, id: f64) {
   let context_state = JsRealm::state_from_scope(scope);
   context_state.timers.ref_timer(id as _);
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_timer_unref(scope: &mut v8::HandleScope, id: f64) {
   let context_state = JsRealm::state_from_scope(scope);
   context_state.timers.unref_timer(id as _);
@@ -225,14 +225,14 @@ pub fn op_run_microtasks(isolate: *mut v8::Isolate) {
   };
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_has_tick_scheduled(scope: &mut v8::HandleScope) -> bool {
   JsRealm::state_from_scope(scope)
     .has_next_tick_scheduled
     .get()
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_set_has_tick_scheduled(scope: &mut v8::HandleScope, v: bool) {
   JsRealm::state_from_scope(scope)
     .has_next_tick_scheduled
@@ -776,7 +776,7 @@ pub fn op_get_promise_details<'a>(
   out.into()
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_set_promise_hooks(
   scope: &mut v8::HandleScope,
   init_hook: v8::Local<v8::Value>,
@@ -1005,7 +1005,7 @@ pub fn op_destructure_error(
 /// Effectively throw an uncatchable error. This will terminate runtime
 /// execution before any more JS code can run, except in the REPL where it
 /// should just output the error to the console.
-#[op2(reentrant)]
+#[op2(fast, reentrant)]
 pub fn op_dispatch_exception(
   scope: &mut v8::HandleScope,
   exception: v8::Local<v8::Value>,
@@ -1129,7 +1129,7 @@ pub fn op_set_format_exception_callback<'a>(
   old.map(|func| func.into())
 }
 
-#[op2]
+#[op2(fast)]
 pub fn op_event_loop_has_more_work(scope: &mut v8::HandleScope) -> bool {
   JsRuntime::has_more_work(scope)
 }
diff --git a/core/runtime/ops.rs b/core/runtime/ops.rs
index fc5d1da..9b9a8f9 100644
--- a/core/runtime/ops.rs
+++ b/core/runtime/ops.rs
@@ -1812,7 +1812,7 @@ mod tests {
   }
 
   // TODO(mmastrac): This is a dangerous op that we'll use to test resizable buffers in a later pass.
-  #[op2]
+  #[op2(fast)]
   pub fn op_buffer_slice_unsafe_callback(
     scope: &mut v8::HandleScope,
     buffer: v8::Local<v8::ArrayBuffer>,
diff --git a/ops/op2/README.md b/ops/op2/README.md
index a1e9573..77b4025 100644
--- a/ops/op2/README.md
+++ b/ops/op2/README.md
@@ -826,62 +826,6 @@ External
 <tr>
 <td>
 
-```text
-#[memory(caller)] &[u8]
-```
-
-</td><td>
-✅
-</td><td>
-WASM
-</td><td>
-When called from WASM code, contains a pointer to the WASM module's memory. Throws an exception if called from another context.
-</td></tr>
-<tr>
-<td>
-
-```text
-#[memory(caller)] &mut [u8]
-```
-
-</td><td>
-✅
-</td><td>
-WASM
-</td><td>
-When called from WASM code, contains a pointer to the WASM module's memory. Throws an exception if called from another context.
-</td></tr>
-<tr>
-<td>
-
-```text
-#[memory(caller)] Option<&[u8]>
-```
-
-</td><td>
-✅
-</td><td>
-WASM
-</td><td>
-When called from WASM code, contains a pointer to the WASM module's memory, otherwise `None`.
-</td></tr>
-<tr>
-<td>
-
-```text
-#[memory(caller)] Option<&mut [u8]>
-```
-
-</td><td>
-✅
-</td><td>
-WASM
-</td><td>
-When called from WASM code, contains a pointer to the WASM module's memory, otherwise `None`.
-</td></tr>
-<tr>
-<td>
-
 ```text
 &OpState
 ```
diff --git a/ops/op2/dispatch_fast.rs b/ops/op2/dispatch_fast.rs
index 5b6f043..26e8d57 100644
--- a/ops/op2/dispatch_fast.rs
+++ b/ops/op2/dispatch_fast.rs
@@ -16,7 +16,6 @@ use super::signature::ParsedSignature;
 use super::signature::RefType;
 use super::signature::Special;
 use super::signature::Strings;
-use super::signature::WasmMemorySource;
 use super::V8MappingError;
 use super::V8SignatureMappingError;
 use crate::op2::dispatch_async::map_async_return_type;
@@ -304,37 +303,51 @@ pub(crate) fn get_fast_signature(
   }))
 }
 
+fn create_scope(generator_state: &mut GeneratorState) -> TokenStream {
+  generator_state.needs_fast_api_callback_options = true;
+  gs_quote!(generator_state(fast_api_callback_options) => {
+    // SAFETY: This is using an &FastApiCallbackOptions inside a fast call.
+    unsafe {
+      deno_core::v8::CallbackScope::new(&*#fast_api_callback_options)
+    }
+  })
+}
+
+fn throw_type_error(
+  generator_state: &mut GeneratorState,
+  message: impl std::fmt::Display,
+) -> TokenStream {
+  let create_scope = create_scope(generator_state);
+  let message = format!("{message}");
+  quote!({
+    let mut scope = #create_scope;
+    let msg = deno_core::v8::String::new_from_one_byte(&mut scope, #message.as_bytes(), deno_core::v8::NewStringType::Normal).unwrap();
+    let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+    scope.throw_exception(exc);
+    // SAFETY: All fast return types have zero as a valid value
+    return unsafe { std::mem::zeroed() };
+  })
+}
+
 /// Sheds the error in a `Result<T, E>` as an early return, leaving just the `T` and requesting
 /// that v8 re-call the slow function to throw the error.
 pub(crate) fn generate_fast_result_early_exit(
   generator_state: &mut GeneratorState,
 ) -> TokenStream {
-  generator_state.needs_fast_api_callback_options = true;
-  generator_state.needs_fast_opctx = true;
-  gs_quote!(generator_state(fast_api_callback_options, opctx, result) => {
+  generator_state.needs_opctx = true;
+  let create_scope = create_scope(generator_state);
+  gs_quote!(generator_state(opctx, result) => {
     let #result = match #result {
       Ok(#result) => #result,
       Err(err) => {
         let err = err.into();
-
-        // FASTCALL FALLBACK: This is where we set the errors for the slow-call error pickup path. There
-        // is no code running between this and the other FASTCALL FALLBACK comment, except some V8 code
-        // required to perform the fallback process. This is why the below call is safe.
-
-        // The reason we need to do this is because V8 does not allow exceptions to be thrown from the
-        // fast call. Instead, you are required to set the fallback flag, which indicates to V8 that it
-        // should re-call the slow version of the function. Technically the slow call should perform the
-        // same operation and then throw the same error (because it should be idempotent), but in our
-        // case we stash the error and pick it up on the slow path before doing any work.
-
-        // TODO(mmastrac): We should allow an #[op] flag to re-perform slow calls without the error path when
-        // the method is performance sensitive.
-
-        // SAFETY: We guarantee that OpCtx has no mutable references once ops are live and being called,
-        // allowing us to perform this one little bit of mutable magic.
-        unsafe { #opctx.unsafely_set_last_error_for_ops_only(err); }
-        #fast_api_callback_options.fallback = true;
-
+        let mut scope = #create_scope;
+        let exception = deno_core::error::to_v8_error(
+          &mut scope,
+          #opctx.get_error_class_fn,
+          &err,
+        );
+        scope.throw_exception(exception);
         // SAFETY: All fast return types have zero as a valid value
         return unsafe { std::mem::zeroed() };
       }
@@ -388,7 +401,7 @@ pub(crate) fn generate_dispatch_fast(
   let call_args = fastsig.call_args(generator_state)?;
 
   let handle_result = if signature.ret_val.is_async() {
-    generator_state.needs_fast_opctx = true;
+    generator_state.needs_opctx = true;
     let (return_value, mapper, _) =
       map_async_return_type(generator_state, &signature.ret_val).map_err(
         |s| {
@@ -411,8 +424,8 @@ pub(crate) fn generate_dispatch_fast(
     })
   };
 
-  let with_js_runtime_state = if generator_state.needs_fast_js_runtime_state {
-    generator_state.needs_fast_opctx = true;
+  let with_js_runtime_state = if generator_state.needs_js_runtime_state {
+    generator_state.needs_opctx = true;
     gs_quote!(generator_state(js_runtime_state, opctx) => {
       let #js_runtime_state = #opctx.runtime_state();
     })
@@ -420,7 +433,7 @@ pub(crate) fn generate_dispatch_fast(
     quote!()
   };
 
-  let with_opctx = if generator_state.needs_fast_opctx {
+  let with_opctx = if generator_state.needs_opctx {
     generator_state.needs_fast_api_callback_options = true;
     gs_quote!(generator_state(opctx, fast_api_callback_options) => {
       let #opctx: &'s _ = unsafe {
@@ -433,13 +446,16 @@ pub(crate) fn generate_dispatch_fast(
   };
 
   let with_self = if generator_state.needs_self {
-    generator_state.needs_fast_scope = true;
     generator_state.needs_fast_api_callback_options = true;
-    gs_quote!(generator_state(self_ty, scope, fast_api_callback_options) => {
-      let Some(self_) = deno_core::_ops::try_unwrap_cppgc_object::<#self_ty>(&mut #scope, this.into()) else {
-        #fast_api_callback_options.fallback = true;
-        // SAFETY: All fast return types have zero as a valid value
-        return unsafe { std::mem::zeroed() };
+    let throw_exception = throw_type_error(
+      generator_state,
+      format!("expected {}", &generator_state.self_ty),
+    );
+    gs_quote!(generator_state(self_ty, fast_api_callback_options) => {
+      // SAFETY: Isolate is valid if this function is being called.
+      let isolate = unsafe { &mut *#fast_api_callback_options.isolate };
+      let Some(self_) = deno_core::_ops::try_unwrap_cppgc_object::<#self_ty>(isolate, this.into()) else {
+        #throw_exception
       };
       let self_ = &*self_;
     })
@@ -447,6 +463,15 @@ pub(crate) fn generate_dispatch_fast(
     quote!()
   };
 
+  let with_scope = if generator_state.needs_scope {
+    let create_scope = create_scope(generator_state);
+    gs_quote!(generator_state(scope) => {
+      let mut #scope = #create_scope;
+    })
+  } else {
+    quote!()
+  };
+
   let name = &generator_state.name;
   let call = if generator_state.needs_self {
     quote!(self_. #name)
@@ -454,22 +479,6 @@ pub(crate) fn generate_dispatch_fast(
     quote!(Self:: #name)
   };
 
-  let with_scope = if generator_state.needs_fast_scope {
-    generator_state.needs_fast_api_callback_options = true;
-    gs_quote!(generator_state(scope, fast_api_callback_options) => (
-      let mut #scope = unsafe {
-        if !this.is_object() {
-          #fast_api_callback_options.fallback = true;
-          // SAFETY: All fast return types have zero as a valid value
-          return unsafe { std::mem::zeroed() };
-        };
-        deno_core::v8::CallbackScope::new(this)
-      };
-    ))
-  } else {
-    quote!()
-  };
-
   let mut fastsig_metrics = fastsig.clone();
   fastsig_metrics.ensure_fast_api_callback_options();
 
@@ -569,13 +578,12 @@ fn map_v8_fastcall_arg_to_arg(
 ) -> Result<TokenStream, V8MappingError> {
   let GeneratorState {
     opctx,
-    scope,
     js_runtime_state,
-    fast_api_callback_options,
-    needs_fast_scope,
-    needs_fast_opctx: needs_opctx,
+    scope,
+    needs_scope,
+    needs_opctx,
     needs_fast_api_callback_options,
-    needs_fast_js_runtime_state: needs_js_runtime_state,
+    needs_js_runtime_state,
     ..
   } = generator_state;
 
@@ -587,13 +595,12 @@ fn map_v8_fastcall_arg_to_arg(
       _,
       BufferSource::ArrayBuffer,
     ) => {
-      *needs_fast_api_callback_options = true;
+      let throw_exception =
+        throw_type_error(generator_state, "expected ArrayBuffer");
       let buf = v8slice_to_buffer(arg_ident, &arg_temp, *buffer)?;
       quote!(
         let Ok(mut #arg_temp) = deno_core::_ops::to_v8_slice_buffer(#arg_ident.into()) else {
-          #fast_api_callback_options.fallback = true;
-          // SAFETY: All fast return types have zero as a valid value
-          return unsafe { std::mem::zeroed() };
+          #throw_exception
         };
         #buf
       )
@@ -603,39 +610,36 @@ fn map_v8_fastcall_arg_to_arg(
       _,
       BufferSource::TypedArray,
     ) => {
-      *needs_fast_api_callback_options = true;
+      let throw_exception =
+        throw_type_error(generator_state, "expected TypedArray");
       let buf = v8slice_to_buffer(arg_ident, &arg_temp, *buffer)?;
       quote!(
         let Ok(mut #arg_temp) = deno_core::_ops::to_v8_slice(#arg_ident.into()) else {
-          #fast_api_callback_options.fallback = true;
-          // SAFETY: All fast return types have zero as a valid value
-          return unsafe { std::mem::zeroed() };
+          #throw_exception
         };
         #buf
       )
     }
     Arg::Buffer(buffer, _, BufferSource::ArrayBuffer) => {
-      *needs_fast_api_callback_options = true;
+      let throw_exception =
+        throw_type_error(generator_state, "expected ArrayBuffer");
       let buf = byte_slice_to_buffer(arg_ident, &arg_temp, *buffer)?;
       quote!(
         // SAFETY: This slice doesn't outlive the function
         let Ok(mut #arg_temp) = (unsafe { deno_core::_ops::to_slice_buffer(#arg_ident.into()) }) else {
-          #fast_api_callback_options.fallback = true;
-          // SAFETY: All fast return types have zero as a valid value
-          return unsafe { std::mem::zeroed() };
+          #throw_exception
         };
         #buf
       )
     }
     Arg::Buffer(buffer, _, BufferSource::Any) => {
-      *needs_fast_api_callback_options = true;
+      let throw_exception =
+        throw_type_error(generator_state, "expected any buffer");
       let buf = byte_slice_to_buffer(arg_ident, &arg_temp, *buffer)?;
       quote!(
         // SAFETY: This slice doesn't outlive the function
         let Ok(mut #arg_temp) = (unsafe { deno_core::_ops::to_slice_buffer_any(#arg_ident.into()) }) else {
-          #fast_api_callback_options.fallback = true;
-          // SAFETY: All fast return types have zero as a valid value
-          return unsafe { std::mem::zeroed() };
+          #throw_exception
         };
         #buf
       )
@@ -644,8 +648,10 @@ fn map_v8_fastcall_arg_to_arg(
       fast_api_typed_array_to_buffer(arg_ident, arg_ident, *buffer)?
     }
     Arg::Special(Special::Isolate) => {
-      *needs_opctx = true;
-      quote!(let #arg_ident = #opctx.isolate;)
+      *needs_fast_api_callback_options = true;
+      gs_quote!(generator_state(fast_api_callback_options) => {
+       let #arg_ident = #fast_api_callback_options.isolate;
+      })
     }
     Arg::Ref(RefType::Ref, Special::OpState) => {
       *needs_opctx = true;
@@ -655,6 +661,10 @@ fn map_v8_fastcall_arg_to_arg(
       *needs_opctx = true;
       quote!(let #arg_ident = &mut ::std::cell::RefCell::borrow_mut(&#opctx.state);)
     }
+    Arg::Ref(_, Special::HandleScope) => {
+      *needs_scope = true;
+      quote!(let #arg_ident = &mut #scope;)
+    }
     Arg::RcRefCell(Special::OpState) => {
       *needs_opctx = true;
       quote!(let #arg_ident = #opctx.state.clone();)
@@ -722,69 +732,50 @@ fn map_v8_fastcall_arg_to_arg(
     | Arg::V8Ref(_, v8)
     | Arg::OptionV8Ref(_, v8) => {
       let arg_ident = arg_ident.clone();
-      // Note that we only request callback options if we were required to provide a type error
       let throw_type_error = || {
-        *needs_fast_api_callback_options = true;
-        Ok(quote! {
-          #fast_api_callback_options.fallback = true;
-          // SAFETY: All fast return types have zero as a valid value
-          return unsafe { std::mem::zeroed() };
-        })
+        Ok(throw_type_error(
+          generator_state,
+          format!("expected {v8:?}"),
+        ))
       };
       let extract_intermediate = v8_intermediate_to_arg(&arg_ident, arg);
       v8_to_arg(v8, &arg_ident, arg, throw_type_error, extract_intermediate)?
     }
-    Arg::WasmMemory(ref_type, WasmMemorySource::Caller) => {
-      *needs_fast_api_callback_options = true;
-      let convert = fast_api_typed_array_to_buffer(
-        arg_ident,
-        arg_ident,
-        BufferType::Slice(*ref_type, NumericArg::u8),
-      )?;
-      quote!(let #arg_ident = #fast_api_callback_options.wasm_memory; #convert;)
-    }
-    Arg::OptionWasmMemory(ref_type, WasmMemorySource::Caller) => {
-      *needs_fast_api_callback_options = true;
-      let convert = fast_api_typed_array_to_buffer(
-        arg_ident,
-        arg_ident,
-        BufferType::Slice(*ref_type, NumericArg::u8),
-      )?;
-      quote!(let #arg_ident = #fast_api_callback_options.wasm_memory; #convert; let #arg_ident = Some(#arg_ident);)
-    }
     Arg::CppGcResource(ty) => {
       let ty =
         syn::parse_str::<syn::Path>(ty).expect("Failed to reparse state type");
 
-      *needs_fast_scope = true;
       *needs_fast_api_callback_options = true;
-      quote! {
-        let Some(#arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident) else {
-          #fast_api_callback_options.fallback = true;
-          // SAFETY: All fast return types have zero as a valid value
-          return unsafe { std::mem::zeroed() };
+      let throw_exception =
+        throw_type_error(generator_state, format!("expected {ty:?}"));
+      gs_quote!(generator_state(fast_api_callback_options) => {
+        // SAFETY: Isolate is valid if this function is being called.
+        let isolate = unsafe { &mut *#fast_api_callback_options.isolate };
+        let Some(#arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(isolate, #arg_ident) else {
+          #throw_exception
         };
         let #arg_ident = &*#arg_ident;
-      }
+      })
     }
     Arg::OptionCppGcResource(ty) => {
       let ty =
         syn::parse_str::<syn::Path>(ty).expect("Failed to reparse state type");
 
-      *needs_fast_scope = true;
       *needs_fast_api_callback_options = true;
-      quote! {
+      let throw_exception =
+        throw_type_error(generator_state, format!("expected {ty:?}"));
+      gs_quote!(generator_state(fast_api_callback_options) => {
+        // SAFETY: Isolate is valid if this function is being called.
+        let isolate = unsafe { &mut *#fast_api_callback_options.isolate };
         let #arg_ident = if #arg_ident.is_null_or_undefined() {
           None
-        } else if let Some(#arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident) {
+        } else if let Some(#arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(isolate, #arg_ident) {
           Some(#arg_ident)
         } else {
-          #fast_api_callback_options.fallback = true;
-          // SAFETY: All fast return types have zero as a valid value
-          return unsafe { std::mem::zeroed() };
+          #throw_exception
         };
         let #arg_ident = #arg_ident.as_deref();
-      }
+      })
     }
     _ => quote!(let #arg_ident = #arg_ident as _;),
   };
@@ -841,13 +832,12 @@ fn map_arg_to_v8_fastcall_type(
     // Virtual OpState arguments
     Arg::RcRefCell(Special::OpState)
     | Arg::Ref(_, Special::OpState)
+    | Arg::Ref(RefType::Mut, Special::HandleScope)
     | Arg::Rc(Special::JsRuntimeState)
     | Arg::Ref(RefType::Ref, Special::JsRuntimeState)
     | Arg::State(..)
     | Arg::Special(Special::Isolate)
-    | Arg::OptionState(..)
-    | Arg::WasmMemory(..)
-    | Arg::OptionWasmMemory(..) => V8FastCallType::Virtual,
+    | Arg::OptionState(..) => V8FastCallType::Virtual,
     // Other types + ref types are not handled
     Arg::OptionNumeric(..)
     | Arg::Option(_)
diff --git a/ops/op2/dispatch_slow.rs b/ops/op2/dispatch_slow.rs
index 31bdc0c..30893db 100644
--- a/ops/op2/dispatch_slow.rs
+++ b/ops/op2/dispatch_slow.rs
@@ -21,7 +21,6 @@ use super::signature::RefType;
 use super::signature::RetVal;
 use super::signature::Special;
 use super::signature::Strings;
-use super::signature::WasmMemorySource;
 use super::V8MappingError;
 use super::V8SignatureMappingError;
 use proc_macro2::Ident;
@@ -58,30 +57,12 @@ pub(crate) fn generate_dispatch_slow_call(
 }
 
 pub(crate) fn generate_dispatch_slow(
-  config: &MacroConfig,
+  _config: &MacroConfig,
   generator_state: &mut GeneratorState,
   signature: &ParsedSignature,
 ) -> Result<TokenStream, V8SignatureMappingError> {
   let mut output = TokenStream::new();
 
-  // Fast ops require the slow op to check op_ctx for the last error
-  if config.fast && matches!(signature.ret_val, RetVal::Result(_)) {
-    generator_state.needs_opctx = true;
-    let throw_exception = throw_exception(generator_state);
-    // If the fast op returned an error, we must throw it rather than doing work.
-    output.extend(quote!{
-      // FASTCALL FALLBACK: This is where we pick up the errors for the slow-call error pickup
-      // path. There is no code running between this and the other FASTCALL FALLBACK comment,
-      // except some V8 code required to perform the fallback process. This is why the below call is safe.
-
-      // SAFETY: We guarantee that OpCtx has no mutable references once ops are live and being called,
-      // allowing us to perform this one little bit of mutable magic.
-      if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } {
-        #throw_exception
-      }
-    });
-  }
-
   let args = generate_dispatch_slow_call(generator_state, signature, 0)?;
 
   output.extend(gs_quote!(generator_state(result) => (let #result = {
@@ -533,13 +514,6 @@ pub fn from_arg(
         v8_intermediate_to_global_arg(&scope, &arg_ident, arg);
       v8_to_arg(v8, &arg_ident, arg, throw_type_error, extract_intermediate)?
     }
-    Arg::WasmMemory(_, WasmMemorySource::Caller) => throw_type_error(
-      generator_state,
-      "WASM caller memory unavailable in slowcalls".into(),
-    )?,
-    Arg::OptionWasmMemory(_, WasmMemorySource::Caller) => {
-      quote!(let #arg_ident = None;)
-    }
     Arg::SerdeV8(_class) => {
       *needs_scope = true;
       let scope = scope.clone();
diff --git a/ops/op2/generator_state.rs b/ops/op2/generator_state.rs
index 6769504..4f8738c 100644
--- a/ops/op2/generator_state.rs
+++ b/ops/op2/generator_state.rs
@@ -1,6 +1,7 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
 use proc_macro2::{Ident, TokenStream};
 
+#[derive(Clone)]
 pub struct GeneratorState {
   pub name: Ident,
   /// Identifiers for each of the arguments of the original function
@@ -42,14 +43,11 @@ pub struct GeneratorState {
   pub needs_args: bool,
   pub needs_retval: bool,
   pub needs_scope: bool,
-  pub needs_fast_scope: bool,
   pub needs_isolate: bool,
   pub needs_opstate: bool,
   pub needs_opctx: bool,
   pub needs_js_runtime_state: bool,
-  pub needs_fast_opctx: bool,
   pub needs_fast_api_callback_options: bool,
-  pub needs_fast_js_runtime_state: bool,
   pub needs_self: bool,
 }
 
diff --git a/ops/op2/mod.rs b/ops/op2/mod.rs
index 2e2d4e9..84d94ca 100644
--- a/ops/op2/mod.rs
+++ b/ops/op2/mod.rs
@@ -156,9 +156,9 @@ fn generate_op2(
     Ident::new("UNINIT", Span::call_site())
   };
 
-  let mut generator_state = GeneratorState {
+  let base_generator_state = GeneratorState {
     name,
-    args,
+    args: args.clone(),
     fn_args,
     scope,
     info,
@@ -169,8 +169,8 @@ fn generate_op2(
     result,
     retval,
     needs_args,
-    slow_function,
-    slow_function_metrics,
+    slow_function: slow_function.clone(),
+    slow_function_metrics: slow_function_metrics.clone(),
     fast_function,
     fast_function_metrics,
     promise_id,
@@ -182,19 +182,18 @@ fn generate_op2(
     needs_opctx: false,
     needs_opstate: false,
     needs_js_runtime_state: false,
-    needs_fast_scope: false,
-    needs_fast_opctx: false,
     needs_fast_api_callback_options: false,
-    needs_fast_js_runtime_state: false,
     needs_self: config.method.is_some(),
   };
 
+  let mut slow_generator_state = base_generator_state.clone();
+
   let name = func.sig.ident;
 
   let slow_fn = if signature.ret_val.is_async() {
-    generate_dispatch_async(&config, &mut generator_state, &signature)?
+    generate_dispatch_async(&config, &mut slow_generator_state, &signature)?
   } else {
-    generate_dispatch_slow(&config, &mut generator_state, &signature)?
+    generate_dispatch_slow(&config, &mut slow_generator_state, &signature)?
   };
   let is_async = signature.ret_val.is_async();
   let is_reentrant = config.reentrant;
@@ -206,8 +205,14 @@ fn generate_op2(
     _ => {}
   }
 
+  let mut fast_generator_state = base_generator_state.clone();
+
   let (fast_definition, fast_definition_metrics, fast_fn) =
-    match generate_dispatch_fast(&config, &mut generator_state, &signature)? {
+    match generate_dispatch_fast(
+      &config,
+      &mut fast_generator_state,
+      &signature,
+    )? {
       Some((fast_definition, fast_metrics_definition, fast_fn)) => {
         if !config.fast && !config.nofast && config.fast_alternatives.is_empty()
         {
@@ -235,13 +240,7 @@ fn generate_op2(
       }
     };
 
-  let GeneratorState {
-    slow_function,
-    slow_function_metrics,
-    ..
-  } = &generator_state;
-
-  let arg_count: usize = generator_state.args.len() + is_async as usize;
+  let arg_count: usize = args.len() + is_async as usize;
   let vis = func.vis;
   let generic = signature
     .generic_bounds
@@ -260,14 +259,14 @@ fn generate_op2(
   let callable = if let Some(ty) = config.method {
     let ident = format_ident!("{ty}");
     quote! {
-        trait Callable {
-          #op_fn_sig;
-        }
-        impl Callable for #ident {
-          #[inline(always)]
-          #(#attrs)*
-          #op_fn
-        }
+      trait Callable {
+        #op_fn_sig;
+      }
+      impl Callable for #ident {
+        #[inline(always)]
+        #(#attrs)*
+        #op_fn
+      }
     }
   } else {
     quote! {
@@ -310,7 +309,7 @@ fn generate_op2(
 
       impl <#(#generic : #bound),*> #name <#(#generic),*> {
         pub const fn name() -> &'static str {
-          stringify!(#name)
+          <Self as deno_core::_ops::Op>::NAME
         }
 
         #fast_fn
diff --git a/ops/op2/signature.rs b/ops/op2/signature.rs
index aa01f4b..3d035e2 100644
--- a/ops/op2/signature.rs
+++ b/ops/op2/signature.rs
@@ -119,7 +119,6 @@ pub enum V8Arg {
   SharedArrayBuffer,
   StringObject,
   SymbolObject,
-  WasmMemoryObject,
   WasmModuleObject,
   Primitive,
   BigInt,
@@ -207,10 +206,7 @@ impl BufferType {
           expand!(Copy)
         }
         JsBuffer | V8Slice(..) => expand!(Copy, Detach, Default),
-        Slice(..) | Ptr(..) => expand!(
-          extra = AttributeModifier::WasmMemory(WasmMemorySource::Caller),
-          Default
-        ),
+        Slice(..) | Ptr(..) => expand!(Default),
       },
       Position::RetVal => match self {
         Bytes | BytesMut | JsBuffer | V8Slice(..) | Vec(..) | BoxSlice(..) => {
@@ -280,8 +276,6 @@ pub enum Arg {
   OptionState(RefType, String),
   CppGcResource(String),
   OptionCppGcResource(String),
-  WasmMemory(RefType, WasmMemorySource),
-  OptionWasmMemory(RefType, WasmMemorySource),
   FromV8(String),
   ToV8(String),
 }
@@ -309,14 +303,7 @@ impl Arg {
       CBare(TSpecial(special)) => Ok(Arg::Special(special)),
       CBare(TString(string)) => Ok(Arg::String(string)),
       CBare(TBuffer(buffer)) => {
-        if let Some(AttributeModifier::WasmMemory(mode)) = attr.primary {
-          let BufferType::Slice(ref_type, NumericArg::u8) = buffer else {
-            unreachable!("non-u8-slice buffer");
-          };
-          Ok(Arg::WasmMemory(ref_type, mode))
-        } else {
-          Ok(Arg::Buffer(buffer, buffer_mode()?, buffer_source()?))
-        }
+        Ok(Arg::Buffer(buffer, buffer_mode()?, buffer_source()?))
       }
       COption(TNumeric(special)) => {
         Ok(Arg::OptionNumeric(special, NumericFlag::None))
@@ -324,14 +311,7 @@ impl Arg {
       COption(TSpecial(special)) => Ok(Arg::Option(special)),
       COption(TString(string)) => Ok(Arg::OptionString(string)),
       COption(TBuffer(buffer)) => {
-        if let Some(AttributeModifier::WasmMemory(mode)) = attr.primary {
-          let BufferType::Slice(ref_type, NumericArg::u8) = buffer else {
-            unreachable!("non-u8-slice buffer");
-          };
-          Ok(Arg::OptionWasmMemory(ref_type, mode))
-        } else {
-          Ok(Arg::OptionBuffer(buffer, buffer_mode()?, buffer_source()?))
-        }
+        Ok(Arg::OptionBuffer(buffer, buffer_mode()?, buffer_source()?))
       }
       CRc(TSpecial(special)) => Ok(Arg::Rc(special)),
       CRcRefCell(TSpecial(special)) => Ok(Arg::RcRefCell(special)),
@@ -371,8 +351,6 @@ impl Arg {
         | Special::HandleScope,
       ) => true,
       Self::State(..) | Self::OptionState(..) => true,
-      Self::WasmMemory(_, WasmMemorySource::Caller)
-      | Self::OptionWasmMemory(_, WasmMemorySource::Caller) => true,
       _ => false,
     }
   }
@@ -752,16 +730,6 @@ pub enum BufferSource {
   Any,
 }
 
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum WasmMemorySource {
-  /// The calling wasm module's memory space.
-  Caller,
-  // TODO(mmastrac): This needs to be implemented
-  /// A v8::WasmMemoryObject.
-  #[allow(unused)]
-  WasmMemoryObject,
-}
-
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub enum AttributeModifier {
   /// #[serde], for serde_v8 types.
@@ -785,8 +753,6 @@ pub enum AttributeModifier {
   Bigint,
   /// #[number], for u64/usize/i64/isize indicating value is a Number
   Number,
-  /// #[memory], for a WasmMemory-backed buffer
-  WasmMemory(WasmMemorySource),
   /// #[cppgc], for a resource backed managed by cppgc.
   CppGcResource,
 }
@@ -804,7 +770,6 @@ impl AttributeModifier {
       AttributeModifier::String(_) => "string",
       AttributeModifier::State => "state",
       AttributeModifier::Global => "global",
-      AttributeModifier::WasmMemory(..) => "memory",
       AttributeModifier::CppGcResource => "cppgc",
     }
   }
@@ -1271,7 +1236,6 @@ fn parse_attribute(
       (#[arraybuffer(copy)]) => Some(AttributeModifier::Buffer(BufferMode::Copy, BufferSource::ArrayBuffer)),
       (#[arraybuffer(detach)]) => Some(AttributeModifier::Buffer(BufferMode::Detach, BufferSource::ArrayBuffer)),
       (#[global]) => Some(AttributeModifier::Global),
-      (#[memory(caller)]) => Some(AttributeModifier::WasmMemory(WasmMemorySource::Caller)),
       (#[cppgc]) => Some(AttributeModifier::CppGcResource),
       (#[to_v8]) => Some(AttributeModifier::ToV8),
       (#[from_v8]) => Some(AttributeModifier::FromV8),
@@ -1377,7 +1341,6 @@ fn parse_type_path(
           Arg::String(string) => Ok(COption(TString(string))),
           Arg::Numeric(numeric, _) => Ok(COption(TNumeric(numeric))),
           Arg::Buffer(buffer, ..) => Ok(COption(TBuffer(buffer))),
-          Arg::WasmMemory(ref_type, ..) => Ok(COption(TBuffer(BufferType::Slice(ref_type, NumericArg::u8)))),
           Arg::V8Ref(RefType::Ref, v8) => Ok(COption(TV8(v8))),
           Arg::V8Ref(RefType::Mut, v8) => Ok(COption(TV8Mut(v8))),
           Arg::V8Local(v8) => Ok(COptionV8Local(TV8(v8))),
@@ -1611,7 +1574,6 @@ pub(crate) fn parse_type(
       }
       AttributeModifier::String(_)
       | AttributeModifier::Buffer(..)
-      | AttributeModifier::WasmMemory(..)
       | AttributeModifier::Bigint
       | AttributeModifier::Global => {
         // We handle this as part of the normal parsing process
@@ -2034,10 +1996,6 @@ mod tests {
     >;
     (RcRefCell(OpState), Numeric(__SMI__, None)) -> FutureResult(SerdeV8(ExtremelyLongTypeNameThatForcesEverythingToWrapAndAddsCommas))
   );
-  test!(
-    fn op_wasm_memory(#[memory(caller)] memory: Option<&[u8]>);
-    (OptionWasmMemory(Ref, Caller)) -> Infallible(Void)
-  );
   expect_fail!(
     op_cppgc_resource_owned,
     ArgError(
diff --git a/ops/op2/valid_args.md b/ops/op2/valid_args.md
index 058c0d5..7933088 100644
--- a/ops/op2/valid_args.md
+++ b/ops/op2/valid_args.md
@@ -1,70 +1,66 @@
-| Supported | Rust                                | Fastcall | V8                                                  | Notes                                                                                                                                                                                                          |
-| --------- | ----------------------------------- | -------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| X         | bool                                | X        | Bool                                                |                                                                                                                                                                                                                |
-| X         | i8                                  | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | u8                                  | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | i16                                 | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | u16                                 | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | i32                                 | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | u32                                 | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | #[smi] ResourceId                   | X        | Uint32, Int32, Number, BigInt                       | SMI is internally represented as a signed integer, but unsigned `#[smi]` types will be bit-converted to unsigned values for the Rust call. JavaScript code will continue to see signed integers.               |
-| X         | #[bigint] i64                       | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | #[bigint] u64                       | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | #[bigint] isize                     | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | #[bigint] usize                     | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | f32                                 | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | f64                                 | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | #[string] String                    | X        | String                                              | Fastcall available only if string is Latin-1. Will always create an allocated, UTF-8 copy of the String data.                                                                                                  |
-| X         | #[string] &str                      | X        | String                                              | Fastcall available only if string is Latin-1. Will create an owned `String` copy of the String data if it doesn't fit on the stack. Will never allocate in a fastcall, but will copy Latin-1 -> UTF-8.         |
-| X         | #[string] Cow<str>                  | X        | String                                              | Fastcall available only if string is Latin-1. Will create a `Cow::Owned` copy of the String data if it doesn't fit on the stack. Will always be `Cow::Borrowed` in a fastcall, but will copy Latin-1 -> UTF-8. |
-| X         | #[string(onebyte)] Cow<[u8]>        | X        | String                                              | Fastest `String`-type method. If the string is not Latin-1, will throw a TypeError.                                                                                                                            |
-| X         | &v8::Value                          | X        | any                                                 |                                                                                                                                                                                                                |
-| X         | &v8::**V8**                         | X        | **V8**                                              |                                                                                                                                                                                                                |
-| X         | v8::Local<v8::Value>                | X        | any                                                 |                                                                                                                                                                                                                |
-| X         | v8::Local<v8::**V8**>               | X        | **V8**                                              |                                                                                                                                                                                                                |
-| X         | #[global] v8::Global<v8::Value>     |          | any                                                 | ⚠️ Slower than `v8::Local`.                                                                                                                                                                                     |
-| X         | #[global] v8::Global<v8::**V8**>    |          | **V8**                                              | ⚠️ Slower than `v8::Local`.                                                                                                                                                                                     |
-| X         | #[serde] SerdeType                  |          | any                                                 | ⚠️ May be slow.                                                                                                                                                                                                 |
-| X         | #[serde] (Tuple, Tuple)             |          | any                                                 | ⚠️ May be slow.                                                                                                                                                                                                 |
-|           | #[anybuffer] &mut [u8]              | X        | ArrayBuffer, ArrayBufferView (resizable=true,false) | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
-|           | #[anybuffer] &[u8]                  | X        | ArrayBuffer, ArrayBufferView (resizable=true,false) | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
-|           | #[anybuffer] *mut u8                | X        | ArrayBuffer, ArrayBufferView (resizable=true,false) | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
-|           | #[anybuffer] *const u8              | X        | ArrayBuffer, ArrayBufferView (resizable=true,false) | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
-| X         | #[arraybuffer] &mut [u8]            | X        | ArrayBuffer (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
-| X         | #[arraybuffer] &[u8]                | X        | ArrayBuffer (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
-| X         | #[arraybuffer] *mut u8              | X        | ArrayBuffer (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
-| X         | #[arraybuffer] *const u8            | X        | ArrayBuffer (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
-| X         | #[arraybuffer(copy)] Vec<u8>        | X        | ArrayBuffer (resizable=true,false)                  | Safe, but forces a copy.                                                                                                                                                                                       |
-| X         | #[arraybuffer(copy)] Box<[u8]>      | X        | ArrayBuffer (resizable=true,false)                  | Safe, but forces a copy.                                                                                                                                                                                       |
-| X         | #[arraybuffer(copy)] bytes::Bytes   | X        | ArrayBuffer (resizable=true,false)                  | Safe, but forces a copy.                                                                                                                                                                                       |
-|           | #[buffer] &mut [u8]                 | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
-|           | #[buffer] &[u8]                     | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
-|           | #[buffer] *mut u8                   | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
-|           | #[buffer] *const u8                 | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
-| X         | #[buffer(copy)] Vec<u8>             | X        | UInt8Array (resizable=true,false)                   | Safe, but forces a copy.                                                                                                                                                                                       |
-| X         | #[buffer(copy)] Box<[u8]>           | X        | UInt8Array (resizable=true,false)                   | Safe, but forces a copy.                                                                                                                                                                                       |
-| X         | #[buffer(copy)] bytes::Bytes        | X        | UInt8Array (resizable=true,false)                   | Safe, but forces a copy.                                                                                                                                                                                       |
-| X         | #[buffer] &mut [u32]                | X        | UInt32Array (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
-| X         | #[buffer] &[u32]                    | X        | UInt32Array (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
-| X         | #[buffer(copy)] Vec<u32>            | X        | UInt32Array (resizable=true,false)                  | Safe, but forces a copy.                                                                                                                                                                                       |
-| X         | #[buffer(copy)] Box<[u32]>          | X        | UInt32Array (resizable=true,false)                  | Safe, but forces a copy.                                                                                                                                                                                       |
-|           | #[buffer] V8Slice                   | X        | ArrayBufferView (resizable=false)                   | ⚠️ JS may modify the contents of slices obtained from buffer.                                                                                                                                                   |
-|           | #[buffer(detach)] V8Slice           | X        | ArrayBufferView (resizable=true,false)              | Safe.                                                                                                                                                                                                          |
-|           | #[buffer] V8ResizableSlice          | X        | ArrayBufferView (resizable=true)                    | ⚠️ JS may modify the contents of slices obtained from buffer.                                                                                                                                                   |
-|           | #[buffer] JsBuffer                  | X        | ArrayBufferView (resizable=false)                   | ⚠️ JS may modify the contents of slices obtained from buffer.                                                                                                                                                   |
-| X         | #[buffer(detach)] JsBuffer          |          | ArrayBufferView (resizable=true,false)              | Safe.                                                                                                                                                                                                          |
-|           | #[buffer(unsafe)] bytes::Bytes      | X        | ArrayBufferView (resizable=false)                   | ⚠️ JS may modify the contents of the buffer.                                                                                                                                                                    |
-|           | #[buffer(detach)] bytes::Bytes      | X        | ArrayBufferView (resizable=true,false)              | Safe.                                                                                                                                                                                                          |
-| X         | *const std::ffi::c_void             | X        | External                                            |                                                                                                                                                                                                                |
-| X         | *mut std::ffi::c_void               | X        | External                                            |                                                                                                                                                                                                                |
-| X         | #[memory(caller)] &[u8]             | X        | WASM                                                | When called from WASM code, contains a pointer to the WASM module's memory. Throws an exception if called from another context.                                                                                |
-| X         | #[memory(caller)] &mut [u8]         | X        | WASM                                                | When called from WASM code, contains a pointer to the WASM module's memory. Throws an exception if called from another context.                                                                                |
-| X         | #[memory(caller)] Option<&[u8]>     | X        | WASM                                                | When called from WASM code, contains a pointer to the WASM module's memory, otherwise `None`.                                                                                                                  |
-| X         | #[memory(caller)] Option<&mut [u8]> | X        | WASM                                                | When called from WASM code, contains a pointer to the WASM module's memory, otherwise `None`.                                                                                                                  |
-| X         | &OpState                            | X        |                                                     |                                                                                                                                                                                                                |
-| X         | &mut OpState                        | X        |                                                     |                                                                                                                                                                                                                |
-| X         | Rc<RefCell<OpState>>                | X        |                                                     |                                                                                                                                                                                                                |
-| X         | #[state] &StateObject               | X        |                                                     | Extracts an object from `OpState`.                                                                                                                                                                             |
-| X         | #[state] &mut StateObject           | X        |                                                     | Extracts an object from `OpState`.                                                                                                                                                                             |
-| X         | &JsRuntimeState                     | X        |                                                     | Only usable in `deno_core`.                                                                                                                                                                                    |
-| X         | *mut v8::Isolate                    | X        |                                                     | ⚠️ Extremely dangerous, may crash if you don't use `nofast` depending on what you do.                                                                                                                           |
+| Supported | Rust                              | Fastcall | V8                                                  | Notes                                                                                                                                                                                                          |
+| --------- | --------------------------------- | -------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| X         | bool                              | X        | Bool                                                |                                                                                                                                                                                                                |
+| X         | i8                                | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | u8                                | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | i16                               | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | u16                               | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | i32                               | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | u32                               | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | #[smi] ResourceId                 | X        | Uint32, Int32, Number, BigInt                       | SMI is internally represented as a signed integer, but unsigned `#[smi]` types will be bit-converted to unsigned values for the Rust call. JavaScript code will continue to see signed integers.               |
+| X         | #[bigint] i64                     | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | #[bigint] u64                     | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | #[bigint] isize                   | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | #[bigint] usize                   | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | f32                               | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | f64                               | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | #[string] String                  | X        | String                                              | Fastcall available only if string is Latin-1. Will always create an allocated, UTF-8 copy of the String data.                                                                                                  |
+| X         | #[string] &str                    | X        | String                                              | Fastcall available only if string is Latin-1. Will create an owned `String` copy of the String data if it doesn't fit on the stack. Will never allocate in a fastcall, but will copy Latin-1 -> UTF-8.         |
+| X         | #[string] Cow<str>                | X        | String                                              | Fastcall available only if string is Latin-1. Will create a `Cow::Owned` copy of the String data if it doesn't fit on the stack. Will always be `Cow::Borrowed` in a fastcall, but will copy Latin-1 -> UTF-8. |
+| X         | #[string(onebyte)] Cow<[u8]>      | X        | String                                              | Fastest `String`-type method. If the string is not Latin-1, will throw a TypeError.                                                                                                                            |
+| X         | &v8::Value                        | X        | any                                                 |                                                                                                                                                                                                                |
+| X         | &v8::**V8**                       | X        | **V8**                                              |                                                                                                                                                                                                                |
+| X         | v8::Local<v8::Value>              | X        | any                                                 |                                                                                                                                                                                                                |
+| X         | v8::Local<v8::**V8**>             | X        | **V8**                                              |                                                                                                                                                                                                                |
+| X         | #[global] v8::Global<v8::Value>   |          | any                                                 | ⚠️ Slower than `v8::Local`.                                                                                                                                                                                     |
+| X         | #[global] v8::Global<v8::**V8**>  |          | **V8**                                              | ⚠️ Slower than `v8::Local`.                                                                                                                                                                                     |
+| X         | #[serde] SerdeType                |          | any                                                 | ⚠️ May be slow.                                                                                                                                                                                                 |
+| X         | #[serde] (Tuple, Tuple)           |          | any                                                 | ⚠️ May be slow.                                                                                                                                                                                                 |
+|           | #[anybuffer] &mut [u8]            | X        | ArrayBuffer, ArrayBufferView (resizable=true,false) | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
+|           | #[anybuffer] &[u8]                | X        | ArrayBuffer, ArrayBufferView (resizable=true,false) | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
+|           | #[anybuffer] *mut u8              | X        | ArrayBuffer, ArrayBufferView (resizable=true,false) | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
+|           | #[anybuffer] *const u8            | X        | ArrayBuffer, ArrayBufferView (resizable=true,false) | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
+| X         | #[arraybuffer] &mut [u8]          | X        | ArrayBuffer (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
+| X         | #[arraybuffer] &[u8]              | X        | ArrayBuffer (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
+| X         | #[arraybuffer] *mut u8            | X        | ArrayBuffer (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
+| X         | #[arraybuffer] *const u8          | X        | ArrayBuffer (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
+| X         | #[arraybuffer(copy)] Vec<u8>      | X        | ArrayBuffer (resizable=true,false)                  | Safe, but forces a copy.                                                                                                                                                                                       |
+| X         | #[arraybuffer(copy)] Box<[u8]>    | X        | ArrayBuffer (resizable=true,false)                  | Safe, but forces a copy.                                                                                                                                                                                       |
+| X         | #[arraybuffer(copy)] bytes::Bytes | X        | ArrayBuffer (resizable=true,false)                  | Safe, but forces a copy.                                                                                                                                                                                       |
+|           | #[buffer] &mut [u8]               | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
+|           | #[buffer] &[u8]                   | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
+|           | #[buffer] *mut u8                 | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
+|           | #[buffer] *const u8               | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
+| X         | #[buffer(copy)] Vec<u8>           | X        | UInt8Array (resizable=true,false)                   | Safe, but forces a copy.                                                                                                                                                                                       |
+| X         | #[buffer(copy)] Box<[u8]>         | X        | UInt8Array (resizable=true,false)                   | Safe, but forces a copy.                                                                                                                                                                                       |
+| X         | #[buffer(copy)] bytes::Bytes      | X        | UInt8Array (resizable=true,false)                   | Safe, but forces a copy.                                                                                                                                                                                       |
+| X         | #[buffer] &mut [u32]              | X        | UInt32Array (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
+| X         | #[buffer] &[u32]                  | X        | UInt32Array (resizable=true,false)                  | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
+| X         | #[buffer(copy)] Vec<u32>          | X        | UInt32Array (resizable=true,false)                  | Safe, but forces a copy.                                                                                                                                                                                       |
+| X         | #[buffer(copy)] Box<[u32]>        | X        | UInt32Array (resizable=true,false)                  | Safe, but forces a copy.                                                                                                                                                                                       |
+|           | #[buffer] V8Slice                 | X        | ArrayBufferView (resizable=false)                   | ⚠️ JS may modify the contents of slices obtained from buffer.                                                                                                                                                   |
+|           | #[buffer(detach)] V8Slice         | X        | ArrayBufferView (resizable=true,false)              | Safe.                                                                                                                                                                                                          |
+|           | #[buffer] V8ResizableSlice        | X        | ArrayBufferView (resizable=true)                    | ⚠️ JS may modify the contents of slices obtained from buffer.                                                                                                                                                   |
+|           | #[buffer] JsBuffer                | X        | ArrayBufferView (resizable=false)                   | ⚠️ JS may modify the contents of slices obtained from buffer.                                                                                                                                                   |
+| X         | #[buffer(detach)] JsBuffer        |          | ArrayBufferView (resizable=true,false)              | Safe.                                                                                                                                                                                                          |
+|           | #[buffer(unsafe)] bytes::Bytes    | X        | ArrayBufferView (resizable=false)                   | ⚠️ JS may modify the contents of the buffer.                                                                                                                                                                    |
+|           | #[buffer(detach)] bytes::Bytes    | X        | ArrayBufferView (resizable=true,false)              | Safe.                                                                                                                                                                                                          |
+| X         | *const std::ffi::c_void           | X        | External                                            |                                                                                                                                                                                                                |
+| X         | *mut std::ffi::c_void             | X        | External                                            |                                                                                                                                                                                                                |
+| X         | &OpState                          | X        |                                                     |                                                                                                                                                                                                                |
+| X         | &mut OpState                      | X        |                                                     |                                                                                                                                                                                                                |
+| X         | Rc<RefCell<OpState>>              | X        |                                                     |                                                                                                                                                                                                                |
+| X         | #[state] &StateObject             | X        |                                                     | Extracts an object from `OpState`.                                                                                                                                                                             |
+| X         | #[state] &mut StateObject         | X        |                                                     | Extracts an object from `OpState`.                                                                                                                                                                             |
+| X         | &JsRuntimeState                   | X        |                                                     | Only usable in `deno_core`.                                                                                                                                                                                    |
+| X         | *mut v8::Isolate                  | X        |                                                     | ⚠️ Extremely dangerous, may crash if you don't use `nofast` depending on what you do.                                                                                                                           |
diff --git a/tools/util.js b/tools/util.js
index c5cc87f..3091ec3 100644
--- a/tools/util.js
+++ b/tools/util.js
@@ -11,11 +11,19 @@ async function getFilesFromGit(baseDir, args) {
     throw new Error("gitLsFiles failed");
   }
 
-  const files = output.split("\0").filter((line) => line.length > 0).map(
-    (filePath) => {
-      return Deno.realPathSync(baseDir + "/" + filePath);
-    },
-  );
+  const files = output
+    .split("\0")
+    .filter((line) => line.length > 0)
+    .map(
+      (filePath) => {
+        try {
+          return Deno.realPathSync(baseDir + "/" + filePath);
+        } catch {
+          return null;
+        }
+      },
+    )
+    .filter((x) => !!x);
 
   return files;
 }


Test patch:
diff --git a/ops/op2/test_cases/async/async_arg_return.out b/ops/op2/test_cases/async/async_arg_return.out
index a671bd2..911f4a6 100644
--- a/ops/op2/test_cases/async/async_arg_return.out
+++ b/ops/op2/test_cases/async/async_arg_return.out
@@ -23,7 +23,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async {
         pub const fn name() -> &'static str {
-            stringify!(op_async)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/async/async_arg_return_result.out b/ops/op2/test_cases/async/async_arg_return_result.out
index c67b15b..14a5cee 100644
--- a/ops/op2/test_cases/async/async_arg_return_result.out
+++ b/ops/op2/test_cases/async/async_arg_return_result.out
@@ -23,7 +23,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async {
         pub const fn name() -> &'static str {
-            stringify!(op_async)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/async/async_cppgc.out b/ops/op2/test_cases/async/async_cppgc.out
index 6b544cf..a75f823 100644
--- a/ops/op2/test_cases/async/async_cppgc.out
+++ b/ops/op2/test_cases/async/async_cppgc.out
@@ -23,7 +23,7 @@ const fn op_make_cppgc_object() -> ::deno_core::_ops::OpDecl {
     }
     impl op_make_cppgc_object {
         pub const fn name() -> &'static str {
-            stringify!(op_make_cppgc_object)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -147,7 +147,7 @@ const fn op_use_cppgc_object() -> ::deno_core::_ops::OpDecl {
     }
     impl op_use_cppgc_object {
         pub const fn name() -> &'static str {
-            stringify!(op_use_cppgc_object)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -271,7 +271,7 @@ const fn op_use_optional_cppgc_object() -> ::deno_core::_ops::OpDecl {
     }
     impl op_use_optional_cppgc_object {
         pub const fn name() -> &'static str {
-            stringify!(op_use_optional_cppgc_object)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/async/async_deferred.out b/ops/op2/test_cases/async/async_deferred.out
index 9ed3cd4..35e4309 100644
--- a/ops/op2/test_cases/async/async_deferred.out
+++ b/ops/op2/test_cases/async/async_deferred.out
@@ -51,7 +51,7 @@ pub const fn op_async_deferred() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async_deferred {
         pub const fn name() -> &'static str {
-            stringify!(op_async_deferred)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/async/async_jsbuffer.out b/ops/op2/test_cases/async/async_jsbuffer.out
index cc86522..786bbdc 100644
--- a/ops/op2/test_cases/async/async_jsbuffer.out
+++ b/ops/op2/test_cases/async/async_jsbuffer.out
@@ -23,7 +23,7 @@ pub const fn op_async_v8_buffer() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async_v8_buffer {
         pub const fn name() -> &'static str {
-            stringify!(op_async_v8_buffer)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/async/async_lazy.out b/ops/op2/test_cases/async/async_lazy.out
index 580420f..58e7216 100644
--- a/ops/op2/test_cases/async/async_lazy.out
+++ b/ops/op2/test_cases/async/async_lazy.out
@@ -51,7 +51,7 @@ pub const fn op_async_lazy() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async_lazy {
         pub const fn name() -> &'static str {
-            stringify!(op_async_lazy)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/async/async_op_metadata.out b/ops/op2/test_cases/async/async_op_metadata.out
index f7e5ba9..2321e32 100644
--- a/ops/op2/test_cases/async/async_op_metadata.out
+++ b/ops/op2/test_cases/async/async_op_metadata.out
@@ -25,7 +25,7 @@ const fn op_blob_read_part() -> ::deno_core::_ops::OpDecl {
     }
     impl op_blob_read_part {
         pub const fn name() -> &'static str {
-            stringify!(op_blob_read_part)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -132,7 +132,7 @@ const fn op_broadcast_recv() -> ::deno_core::_ops::OpDecl {
     }
     impl op_broadcast_recv {
         pub const fn name() -> &'static str {
-            stringify!(op_broadcast_recv)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/async/async_opstate.out b/ops/op2/test_cases/async/async_opstate.out
index ab3cfb6..e662b7c 100644
--- a/ops/op2/test_cases/async/async_opstate.out
+++ b/ops/op2/test_cases/async/async_opstate.out
@@ -23,7 +23,7 @@ pub const fn op_async_opstate() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async_opstate {
         pub const fn name() -> &'static str {
-            stringify!(op_async_opstate)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/async/async_result.out b/ops/op2/test_cases/async/async_result.out
index 94d4cc4..6be9fc1 100644
--- a/ops/op2/test_cases/async/async_result.out
+++ b/ops/op2/test_cases/async/async_result.out
@@ -23,7 +23,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async {
         pub const fn name() -> &'static str {
-            stringify!(op_async)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/async/async_result_impl.out b/ops/op2/test_cases/async/async_result_impl.out
index 4e7e1ee..fa7c79d 100644
--- a/ops/op2/test_cases/async/async_result_impl.out
+++ b/ops/op2/test_cases/async/async_result_impl.out
@@ -23,7 +23,7 @@ pub const fn op_async_result_impl() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async_result_impl {
         pub const fn name() -> &'static str {
-            stringify!(op_async_result_impl)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/async/async_result_smi.out b/ops/op2/test_cases/async/async_result_smi.out
index f528d99..6200ff4 100644
--- a/ops/op2/test_cases/async/async_result_smi.out
+++ b/ops/op2/test_cases/async/async_result_smi.out
@@ -23,7 +23,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async {
         pub const fn name() -> &'static str {
-            stringify!(op_async)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/async/async_v8_global.out b/ops/op2/test_cases/async/async_v8_global.out
index 27e286b..7f92913 100644
--- a/ops/op2/test_cases/async/async_v8_global.out
+++ b/ops/op2/test_cases/async/async_v8_global.out
@@ -23,7 +23,7 @@ pub const fn op_async_v8_global() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async_v8_global {
         pub const fn name() -> &'static str {
-            stringify!(op_async_v8_global)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/async/async_void.out b/ops/op2/test_cases/async/async_void.out
index 66391d2..4e102c0 100644
--- a/ops/op2/test_cases/async/async_void.out
+++ b/ops/op2/test_cases/async/async_void.out
@@ -23,7 +23,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl {
     }
     impl op_async {
         pub const fn name() -> &'static str {
-            stringify!(op_async)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/add.out b/ops/op2/test_cases/sync/add.out
index f1fa380..41dd9b6 100644
--- a/ops/op2/test_cases/sync/add.out
+++ b/ops/op2/test_cases/sync/add.out
@@ -52,7 +52,7 @@ const fn op_add() -> ::deno_core::_ops::OpDecl {
     }
     impl op_add {
         pub const fn name() -> &'static str {
-            stringify!(op_add)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/add_options.out b/ops/op2/test_cases/sync/add_options.out
index 6fe6225..cae543d 100644
--- a/ops/op2/test_cases/sync/add_options.out
+++ b/ops/op2/test_cases/sync/add_options.out
@@ -23,7 +23,7 @@ pub const fn op_test_add_option() -> ::deno_core::_ops::OpDecl {
     }
     impl op_test_add_option {
         pub const fn name() -> &'static str {
-            stringify!(op_test_add_option)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/bigint.out b/ops/op2/test_cases/sync/bigint.out
index cfd45e4..dc599d7 100644
--- a/ops/op2/test_cases/sync/bigint.out
+++ b/ops/op2/test_cases/sync/bigint.out
@@ -43,7 +43,7 @@ pub const fn op_bigint() -> ::deno_core::_ops::OpDecl {
     }
     impl op_bigint {
         pub const fn name() -> &'static str {
-            stringify!(op_bigint)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/bool.out b/ops/op2/test_cases/sync/bool.out
index 051880d..4d1af67 100644
--- a/ops/op2/test_cases/sync/bool.out
+++ b/ops/op2/test_cases/sync/bool.out
@@ -47,7 +47,7 @@ pub const fn op_bool() -> ::deno_core::_ops::OpDecl {
     }
     impl op_bool {
         pub const fn name() -> &'static str {
-            stringify!(op_bool)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/bool_result.out b/ops/op2/test_cases/sync/bool_result.out
index da9907c..e08a5db 100644
--- a/ops/op2/test_cases/sync/bool_result.out
+++ b/ops/op2/test_cases/sync/bool_result.out
@@ -51,7 +51,7 @@ pub const fn op_bool() -> ::deno_core::_ops::OpDecl {
     }
     impl op_bool {
         pub const fn name() -> &'static str {
-            stringify!(op_bool)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -110,10 +110,15 @@ pub const fn op_bool() -> ::deno_core::_ops::OpDecl {
                 Ok(result) => result,
                 Err(err) => {
                     let err = err.into();
-                    unsafe {
-                        opctx.unsafely_set_last_error_for_ops_only(err);
-                    }
-                    fast_api_callback_options.fallback = true;
+                    let mut scope = unsafe {
+                        deno_core::v8::CallbackScope::new(&*fast_api_callback_options)
+                    };
+                    let exception = deno_core::error::to_v8_error(
+                        &mut scope,
+                        opctx.get_error_class_fn,
+                        &err,
+                    );
+                    scope.throw_exception(exception);
                     return unsafe { std::mem::zeroed() };
                 }
             };
@@ -131,23 +136,6 @@ pub const fn op_bool() -> ::deno_core::_ops::OpDecl {
             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)
-            };
-            if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } {
-                let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) };
-                let err = err.into();
-                let exception = deno_core::error::to_v8_error(
-                    &mut scope,
-                    opctx.get_error_class_fn,
-                    &err,
-                );
-                scope.throw_exception(exception);
-                return 1;
-            }
             let result = {
                 let arg0 = args.get(0usize as i32);
                 let arg0 = arg0.is_true();
@@ -157,6 +145,12 @@ pub const fn op_bool() -> ::deno_core::_ops::OpDecl {
                 Ok(result) => deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv),
                 Err(err) => {
                     let mut scope = unsafe { deno_core::v8::CallbackScope::new(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 err = err.into();
                     let exception = deno_core::error::to_v8_error(
                         &mut scope,
diff --git a/ops/op2/test_cases/sync/buffers.out b/ops/op2/test_cases/sync/buffers.out
index 7ba513e..565c38b 100644
--- a/ops/op2/test_cases/sync/buffers.out
+++ b/ops/op2/test_cases/sync/buffers.out
@@ -56,7 +56,7 @@ const fn op_buffers() -> ::deno_core::_ops::OpDecl {
     }
     impl op_buffers {
         pub const fn name() -> &'static str {
-            stringify!(op_buffers)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -336,7 +336,7 @@ const fn op_buffers_32() -> ::deno_core::_ops::OpDecl {
     }
     impl op_buffers_32 {
         pub const fn name() -> &'static str {
-            stringify!(op_buffers_32)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -583,7 +583,7 @@ const fn op_buffers_option() -> ::deno_core::_ops::OpDecl {
     }
     impl op_buffers_option {
         pub const fn name() -> &'static str {
-            stringify!(op_buffers_option)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/buffers_copy.out b/ops/op2/test_cases/sync/buffers_copy.out
index d5aab94..cde2506 100644
--- a/ops/op2/test_cases/sync/buffers_copy.out
+++ b/ops/op2/test_cases/sync/buffers_copy.out
@@ -54,7 +54,7 @@ const fn op_buffers() -> ::deno_core::_ops::OpDecl {
     }
     impl op_buffers {
         pub const fn name() -> &'static str {
-            stringify!(op_buffers)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -288,7 +288,7 @@ const fn op_buffers_32() -> ::deno_core::_ops::OpDecl {
     }
     impl op_buffers_32 {
         pub const fn name() -> &'static str {
-            stringify!(op_buffers_32)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/buffers_out.out b/ops/op2/test_cases/sync/buffers_out.out
index aaaba93..2fb9d40 100644
--- a/ops/op2/test_cases/sync/buffers_out.out
+++ b/ops/op2/test_cases/sync/buffers_out.out
@@ -23,7 +23,7 @@ const fn op_buffers() -> ::deno_core::_ops::OpDecl {
     }
     impl op_buffers {
         pub const fn name() -> &'static str {
-            stringify!(op_buffers)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -145,7 +145,7 @@ const fn op_buffers_option() -> ::deno_core::_ops::OpDecl {
     }
     impl op_buffers_option {
         pub const fn name() -> &'static str {
-            stringify!(op_buffers_option)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -277,7 +277,7 @@ const fn op_arraybuffers() -> ::deno_core::_ops::OpDecl {
     }
     impl op_arraybuffers {
         pub const fn name() -> &'static str {
-            stringify!(op_arraybuffers)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/cfg.out b/ops/op2/test_cases/sync/cfg.out
index 3cf3fdd..c76b5e4 100644
--- a/ops/op2/test_cases/sync/cfg.out
+++ b/ops/op2/test_cases/sync/cfg.out
@@ -45,7 +45,7 @@ pub const fn op_maybe_windows() -> ::deno_core::_ops::OpDecl {
     }
     impl op_maybe_windows {
         pub const fn name() -> &'static str {
-            stringify!(op_maybe_windows)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -189,7 +189,7 @@ pub const fn op_maybe_windows() -> ::deno_core::_ops::OpDecl {
     }
     impl op_maybe_windows {
         pub const fn name() -> &'static str {
-            stringify!(op_maybe_windows)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/clippy_allow.out b/ops/op2/test_cases/sync/clippy_allow.out
index c7a2234..1628b67 100644
--- a/ops/op2/test_cases/sync/clippy_allow.out
+++ b/ops/op2/test_cases/sync/clippy_allow.out
@@ -45,7 +45,7 @@ pub const fn op_extra_annotation() -> ::deno_core::_ops::OpDecl {
     }
     impl op_extra_annotation {
         pub const fn name() -> &'static str {
-            stringify!(op_extra_annotation)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -187,7 +187,7 @@ pub const fn op_clippy_internal() -> ::deno_core::_ops::OpDecl {
     }
     impl op_clippy_internal {
         pub const fn name() -> &'static str {
-            stringify!(op_clippy_internal)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/cppgc_resource.out b/ops/op2/test_cases/sync/cppgc_resource.out
index b99b8c8..f6fefee 100644
--- a/ops/op2/test_cases/sync/cppgc_resource.out
+++ b/ops/op2/test_cases/sync/cppgc_resource.out
@@ -51,7 +51,7 @@ const fn op_cppgc_object() -> ::deno_core::_ops::OpDecl {
     }
     impl op_cppgc_object {
         pub const fn name() -> &'static str {
-            stringify!(op_cppgc_object)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -96,19 +96,28 @@ const fn op_cppgc_object() -> ::deno_core::_ops::OpDecl {
             let fast_api_callback_options: &'s mut _ = unsafe {
                 &mut *fast_api_callback_options
             };
-            let mut scope = unsafe {
-                if !this.is_object() {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
-                }
-                deno_core::v8::CallbackScope::new(this)
-            };
             let result = {
+                let isolate = unsafe { &mut *fast_api_callback_options.isolate };
                 let Some(arg0) = deno_core::_ops::try_unwrap_cppgc_object::<
                     Wrap,
-                >(&mut scope, arg0) else {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
+                >(isolate, arg0) else {
+                    {
+                        let mut scope = unsafe {
+                            deno_core::v8::CallbackScope::new(
+                                &*fast_api_callback_options,
+                            )
+                        };
+                        let msg = deno_core::v8::String::new_from_one_byte(
+                                &mut scope,
+                                "expected Path { leading_colon: None, segments: [PathSegment { ident: Ident(Wrap), arguments: PathArguments::None }] }"
+                                    .as_bytes(),
+                                deno_core::v8::NewStringType::Normal,
+                            )
+                            .unwrap();
+                        let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                        scope.throw_exception(exc);
+                        return unsafe { std::mem::zeroed() };
+                    }
                 };
                 let arg0 = &*arg0;
                 Self::call(arg0)
@@ -244,7 +253,7 @@ const fn op_option_cppgc_object() -> ::deno_core::_ops::OpDecl {
     }
     impl op_option_cppgc_object {
         pub const fn name() -> &'static str {
-            stringify!(op_option_cppgc_object)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -289,23 +298,32 @@ const fn op_option_cppgc_object() -> ::deno_core::_ops::OpDecl {
             let fast_api_callback_options: &'s mut _ = unsafe {
                 &mut *fast_api_callback_options
             };
-            let mut scope = unsafe {
-                if !this.is_object() {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
-                }
-                deno_core::v8::CallbackScope::new(this)
-            };
             let result = {
+                let isolate = unsafe { &mut *fast_api_callback_options.isolate };
                 let arg0 = if arg0.is_null_or_undefined() {
                     None
                 } else if let Some(arg0) = deno_core::_ops::try_unwrap_cppgc_object::<
                     Wrap,
-                >(&mut scope, arg0) {
+                >(isolate, arg0) {
                     Some(arg0)
                 } else {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
+                    {
+                        let mut scope = unsafe {
+                            deno_core::v8::CallbackScope::new(
+                                &*fast_api_callback_options,
+                            )
+                        };
+                        let msg = deno_core::v8::String::new_from_one_byte(
+                                &mut scope,
+                                "expected Path { leading_colon: None, segments: [PathSegment { ident: Ident(Wrap), arguments: PathArguments::None }] }"
+                                    .as_bytes(),
+                                deno_core::v8::NewStringType::Normal,
+                            )
+                            .unwrap();
+                        let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                        scope.throw_exception(exc);
+                        return unsafe { std::mem::zeroed() };
+                    }
                 };
                 let arg0 = arg0.as_deref();
                 Self::call(arg0)
@@ -417,7 +435,7 @@ const fn op_make_cppgc_object() -> ::deno_core::_ops::OpDecl {
     }
     impl op_make_cppgc_object {
         pub const fn name() -> &'static str {
-            stringify!(op_make_cppgc_object)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -538,7 +556,7 @@ const fn op_use_cppgc_object() -> ::deno_core::_ops::OpDecl {
     }
     impl op_use_cppgc_object {
         pub const fn name() -> &'static str {
-            stringify!(op_use_cppgc_object)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -583,19 +601,28 @@ const fn op_use_cppgc_object() -> ::deno_core::_ops::OpDecl {
             let fast_api_callback_options: &'s mut _ = unsafe {
                 &mut *fast_api_callback_options
             };
-            let mut scope = unsafe {
-                if !this.is_object() {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
-                }
-                deno_core::v8::CallbackScope::new(this)
-            };
             let result = {
+                let isolate = unsafe { &mut *fast_api_callback_options.isolate };
                 let Some(arg0) = deno_core::_ops::try_unwrap_cppgc_object::<
                     Wrap,
-                >(&mut scope, arg0) else {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
+                >(isolate, arg0) else {
+                    {
+                        let mut scope = unsafe {
+                            deno_core::v8::CallbackScope::new(
+                                &*fast_api_callback_options,
+                            )
+                        };
+                        let msg = deno_core::v8::String::new_from_one_byte(
+                                &mut scope,
+                                "expected Path { leading_colon: None, segments: [PathSegment { ident: Ident(Wrap), arguments: PathArguments::None }] }"
+                                    .as_bytes(),
+                                deno_core::v8::NewStringType::Normal,
+                            )
+                            .unwrap();
+                        let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                        scope.throw_exception(exc);
+                        return unsafe { std::mem::zeroed() };
+                    }
                 };
                 let arg0 = &*arg0;
                 Self::call(arg0)
@@ -703,7 +730,7 @@ const fn op_make_cppgc_object_option() -> ::deno_core::_ops::OpDecl {
     }
     impl op_make_cppgc_object_option {
         pub const fn name() -> &'static str {
-            stringify!(op_make_cppgc_object_option)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -827,7 +854,7 @@ const fn op_use_cppgc_object_option() -> ::deno_core::_ops::OpDecl {
     }
     impl op_use_cppgc_object_option {
         pub const fn name() -> &'static str {
-            stringify!(op_use_cppgc_object_option)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -872,19 +899,28 @@ const fn op_use_cppgc_object_option() -> ::deno_core::_ops::OpDecl {
             let fast_api_callback_options: &'s mut _ = unsafe {
                 &mut *fast_api_callback_options
             };
-            let mut scope = unsafe {
-                if !this.is_object() {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
-                }
-                deno_core::v8::CallbackScope::new(this)
-            };
             let result = {
+                let isolate = unsafe { &mut *fast_api_callback_options.isolate };
                 let Some(arg0) = deno_core::_ops::try_unwrap_cppgc_object::<
                     Wrap,
-                >(&mut scope, arg0) else {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
+                >(isolate, arg0) else {
+                    {
+                        let mut scope = unsafe {
+                            deno_core::v8::CallbackScope::new(
+                                &*fast_api_callback_options,
+                            )
+                        };
+                        let msg = deno_core::v8::String::new_from_one_byte(
+                                &mut scope,
+                                "expected Path { leading_colon: None, segments: [PathSegment { ident: Ident(Wrap), arguments: PathArguments::None }] }"
+                                    .as_bytes(),
+                                deno_core::v8::NewStringType::Normal,
+                            )
+                            .unwrap();
+                        let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                        scope.throw_exception(exc);
+                        return unsafe { std::mem::zeroed() };
+                    }
                 };
                 let arg0 = &*arg0;
                 Self::call(arg0)
diff --git a/ops/op2/test_cases/sync/doc_comment.out b/ops/op2/test_cases/sync/doc_comment.out
index 92a440f..1f3cdb9 100644
--- a/ops/op2/test_cases/sync/doc_comment.out
+++ b/ops/op2/test_cases/sync/doc_comment.out
@@ -44,7 +44,7 @@ pub const fn op_has_doc_comment() -> ::deno_core::_ops::OpDecl {
     }
     impl op_has_doc_comment {
         pub const fn name() -> &'static str {
-            stringify!(op_has_doc_comment)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/fast_alternative.out b/ops/op2/test_cases/sync/fast_alternative.out
index 1f0dca0..4668661 100644
--- a/ops/op2/test_cases/sync/fast_alternative.out
+++ b/ops/op2/test_cases/sync/fast_alternative.out
@@ -23,7 +23,7 @@ const fn op_slow() -> ::deno_core::_ops::OpDecl {
     }
     impl op_slow {
         pub const fn name() -> &'static str {
-            stringify!(op_slow)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -169,7 +169,7 @@ const fn op_fast() -> ::deno_core::_ops::OpDecl {
     }
     impl op_fast {
         pub const fn name() -> &'static str {
-            stringify!(op_fast)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -332,7 +332,7 @@ const fn op_slow_generic<T: Trait>() -> ::deno_core::_ops::OpDecl {
     }
     impl<T: Trait> op_slow_generic<T> {
         pub const fn name() -> &'static str {
-            stringify!(op_slow_generic)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -478,7 +478,7 @@ const fn op_fast_generic<T: Trait>() -> ::deno_core::_ops::OpDecl {
     }
     impl<T: Trait> op_fast_generic<T> {
         pub const fn name() -> &'static str {
-            stringify!(op_fast_generic)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/from_v8.out b/ops/op2/test_cases/sync/from_v8.out
index 5aa25ac..33fe31a 100644
--- a/ops/op2/test_cases/sync/from_v8.out
+++ b/ops/op2/test_cases/sync/from_v8.out
@@ -23,7 +23,7 @@ pub const fn op_from_v8_arg() -> ::deno_core::_ops::OpDecl {
     }
     impl op_from_v8_arg {
         pub const fn name() -> &'static str {
-            stringify!(op_from_v8_arg)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/generics.out b/ops/op2/test_cases/sync/generics.out
index f26ccd5..be44814 100644
--- a/ops/op2/test_cases/sync/generics.out
+++ b/ops/op2/test_cases/sync/generics.out
@@ -43,7 +43,7 @@ pub const fn op_generics<T: Trait>() -> ::deno_core::_ops::OpDecl {
     }
     impl<T: Trait> op_generics<T> {
         pub const fn name() -> &'static str {
-            stringify!(op_generics)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -183,7 +183,7 @@ pub const fn op_generics_static<T: Trait + 'static>() -> ::deno_core::_ops::OpDe
     }
     impl<T: Trait + 'static> op_generics_static<T> {
         pub const fn name() -> &'static str {
-            stringify!(op_generics_static)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -323,7 +323,7 @@ pub const fn op_generics_static_where<T: Trait + 'static>() -> ::deno_core::_ops
     }
     impl<T: Trait + 'static> op_generics_static_where<T> {
         pub const fn name() -> &'static str {
-            stringify!(op_generics_static_where)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/nofast.out b/ops/op2/test_cases/sync/nofast.out
index 6f71efb..2761785 100644
--- a/ops/op2/test_cases/sync/nofast.out
+++ b/ops/op2/test_cases/sync/nofast.out
@@ -23,7 +23,7 @@ const fn op_nofast() -> ::deno_core::_ops::OpDecl {
     }
     impl op_nofast {
         pub const fn name() -> &'static str {
-            stringify!(op_nofast)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/op_state_attr.out b/ops/op2/test_cases/sync/op_state_attr.out
index c33e541..68d1d85 100644
--- a/ops/op2/test_cases/sync/op_state_attr.out
+++ b/ops/op2/test_cases/sync/op_state_attr.out
@@ -43,7 +43,7 @@ const fn op_state_rc() -> ::deno_core::_ops::OpDecl {
     }
     impl op_state_rc {
         pub const fn name() -> &'static str {
-            stringify!(op_state_rc)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/op_state_rc.out b/ops/op2/test_cases/sync/op_state_rc.out
index 05342b4..3b2fee3 100644
--- a/ops/op2/test_cases/sync/op_state_rc.out
+++ b/ops/op2/test_cases/sync/op_state_rc.out
@@ -43,7 +43,7 @@ const fn op_state_rc() -> ::deno_core::_ops::OpDecl {
     }
     impl op_state_rc {
         pub const fn name() -> &'static str {
-            stringify!(op_state_rc)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/op_state_ref.out b/ops/op2/test_cases/sync/op_state_ref.out
index 361a6c0..b8c7ff3 100644
--- a/ops/op2/test_cases/sync/op_state_ref.out
+++ b/ops/op2/test_cases/sync/op_state_ref.out
@@ -43,7 +43,7 @@ const fn op_state_ref() -> ::deno_core::_ops::OpDecl {
     }
     impl op_state_ref {
         pub const fn name() -> &'static str {
-            stringify!(op_state_ref)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -211,7 +211,7 @@ const fn op_state_mut() -> ::deno_core::_ops::OpDecl {
     }
     impl op_state_mut {
         pub const fn name() -> &'static str {
-            stringify!(op_state_mut)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -359,7 +359,7 @@ const fn op_state_and_v8() -> ::deno_core::_ops::OpDecl {
     }
     impl op_state_and_v8 {
         pub const fn name() -> &'static str {
-            stringify!(op_state_and_v8)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -499,7 +499,7 @@ const fn op_state_and_v8_local() -> ::deno_core::_ops::OpDecl {
     }
     impl op_state_and_v8_local {
         pub const fn name() -> &'static str {
-            stringify!(op_state_and_v8_local)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -555,8 +555,22 @@ const fn op_state_and_v8_local() -> ::deno_core::_ops::OpDecl {
                 let Ok(mut arg1) = deno_core::_ops::v8_try_convert::<
                     deno_core::v8::Function,
                 >(arg1) else {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
+                    {
+                        let mut scope = unsafe {
+                            deno_core::v8::CallbackScope::new(
+                                &*fast_api_callback_options,
+                            )
+                        };
+                        let msg = deno_core::v8::String::new_from_one_byte(
+                                &mut scope,
+                                "expected Function".as_bytes(),
+                                deno_core::v8::NewStringType::Normal,
+                            )
+                            .unwrap();
+                        let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                        scope.throw_exception(exc);
+                        return unsafe { std::mem::zeroed() };
+                    }
                 };
                 let arg1 = arg1;
                 Self::call(arg0, arg1)
diff --git a/ops/op2/test_cases/sync/result_external.out b/ops/op2/test_cases/sync/result_external.out
index 84c2092..9318f95 100644
--- a/ops/op2/test_cases/sync/result_external.out
+++ b/ops/op2/test_cases/sync/result_external.out
@@ -43,7 +43,7 @@ pub const fn op_external_with_result() -> ::deno_core::_ops::OpDecl {
     }
     impl op_external_with_result {
         pub const fn name() -> &'static str {
-            stringify!(op_external_with_result)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -97,10 +97,15 @@ pub const fn op_external_with_result() -> ::deno_core::_ops::OpDecl {
                 Ok(result) => result,
                 Err(err) => {
                     let err = err.into();
-                    unsafe {
-                        opctx.unsafely_set_last_error_for_ops_only(err);
-                    }
-                    fast_api_callback_options.fallback = true;
+                    let mut scope = unsafe {
+                        deno_core::v8::CallbackScope::new(&*fast_api_callback_options)
+                    };
+                    let exception = deno_core::error::to_v8_error(
+                        &mut scope,
+                        opctx.get_error_class_fn,
+                        &err,
+                    );
+                    scope.throw_exception(exception);
                     return unsafe { std::mem::zeroed() };
                 }
             };
@@ -119,35 +124,18 @@ pub const fn op_external_with_result() -> ::deno_core::_ops::OpDecl {
             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)
-            };
-            if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } {
-                let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) };
-                let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(
-                    info,
-                );
-                let err = err.into();
-                let exception = deno_core::error::to_v8_error(
-                    &mut scope,
-                    opctx.get_error_class_fn,
-                    &err,
-                );
-                scope.throw_exception(exception);
-                return 1;
-            }
             let result = { Self::call() };
             match result {
                 Ok(result) => {
                     rv.set(deno_core::_ops::RustToV8::to_v8(result, &mut scope))
                 }
                 Err(err) => {
-                    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 err = err.into();
                     let exception = deno_core::error::to_v8_error(
                         &mut scope,
diff --git a/ops/op2/test_cases/sync/result_primitive.out b/ops/op2/test_cases/sync/result_primitive.out
index d6e5463..b5ccc75 100644
--- a/ops/op2/test_cases/sync/result_primitive.out
+++ b/ops/op2/test_cases/sync/result_primitive.out
@@ -43,7 +43,7 @@ pub const fn op_u32_with_result() -> ::deno_core::_ops::OpDecl {
     }
     impl op_u32_with_result {
         pub const fn name() -> &'static str {
-            stringify!(op_u32_with_result)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -97,10 +97,15 @@ pub const fn op_u32_with_result() -> ::deno_core::_ops::OpDecl {
                 Ok(result) => result,
                 Err(err) => {
                     let err = err.into();
-                    unsafe {
-                        opctx.unsafely_set_last_error_for_ops_only(err);
-                    }
-                    fast_api_callback_options.fallback = true;
+                    let mut scope = unsafe {
+                        deno_core::v8::CallbackScope::new(&*fast_api_callback_options)
+                    };
+                    let exception = deno_core::error::to_v8_error(
+                        &mut scope,
+                        opctx.get_error_class_fn,
+                        &err,
+                    );
+                    scope.throw_exception(exception);
                     return unsafe { std::mem::zeroed() };
                 }
             };
@@ -118,34 +123,17 @@ pub const fn op_u32_with_result() -> ::deno_core::_ops::OpDecl {
             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)
-            };
-            if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } {
-                let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) };
-                let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(
-                    info,
-                );
-                let err = err.into();
-                let exception = deno_core::error::to_v8_error(
-                    &mut scope,
-                    opctx.get_error_class_fn,
-                    &err,
-                );
-                scope.throw_exception(exception);
-                return 1;
-            }
             let result = { Self::call() };
             match result {
                 Ok(result) => deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv),
                 Err(err) => {
                     let mut scope = unsafe { deno_core::v8::CallbackScope::new(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 err = err.into();
                     let exception = deno_core::error::to_v8_error(
                         &mut scope,
diff --git a/ops/op2/test_cases/sync/result_scope.out b/ops/op2/test_cases/sync/result_scope.out
index 952f5da..9871e4e 100644
--- a/ops/op2/test_cases/sync/result_scope.out
+++ b/ops/op2/test_cases/sync/result_scope.out
@@ -14,8 +14,28 @@ pub const fn op_void_with_result() -> ::deno_core::_ops::OpDecl {
             false,
             Self::v8_fn_ptr as _,
             Self::v8_fn_ptr_metrics as _,
-            None,
-            None,
+            Some({
+                use deno_core::v8::fast_api::Type as CType;
+                deno_core::v8::fast_api::CFunction::new(
+                    Self::v8_fn_ptr_fast as _,
+                    &deno_core::v8::fast_api::CFunctionInfo::new(
+                        CType::Void.scalar(),
+                        &[CType::V8Value.scalar(), CType::CallbackOptions.scalar()],
+                        deno_core::v8::fast_api::Int64Representation::BigInt,
+                    ),
+                )
+            }),
+            Some({
+                use deno_core::v8::fast_api::Type as CType;
+                deno_core::v8::fast_api::CFunction::new(
+                    Self::v8_fn_ptr_fast_metrics as _,
+                    &deno_core::v8::fast_api::CFunctionInfo::new(
+                        CType::Void.scalar(),
+                        &[CType::V8Value.scalar(), CType::CallbackOptions.scalar()],
+                        deno_core::v8::fast_api::Int64Representation::BigInt,
+                    ),
+                )
+            }),
             ::deno_core::OpMetadata {
                 ..::deno_core::OpMetadata::default()
             },
@@ -23,7 +43,79 @@ pub const fn op_void_with_result() -> ::deno_core::_ops::OpDecl {
     }
     impl op_void_with_result {
         pub const fn name() -> &'static str {
-            stringify!(op_void_with_result)
+            <Self as deno_core::_ops::Op>::NAME
+        }
+        #[allow(clippy::too_many_arguments)]
+        extern "C" fn v8_fn_ptr_fast_metrics<'s>(
+            this: deno_core::v8::Local<deno_core::v8::Object>,
+            fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions<
+                's,
+            >,
+        ) -> () {
+            let fast_api_callback_options: &'s mut _ = unsafe {
+                &mut *fast_api_callback_options
+            };
+            let opctx: &'s _ = unsafe {
+                &*(deno_core::v8::Local::<
+                    deno_core::v8::External,
+                >::cast_unchecked(unsafe { fast_api_callback_options.data })
+                    .value() as *const deno_core::_ops::OpCtx)
+            };
+            deno_core::_ops::dispatch_metrics_fast(
+                opctx,
+                deno_core::_ops::OpMetricsEvent::Dispatched,
+            );
+            let res = Self::v8_fn_ptr_fast(this, fast_api_callback_options);
+            deno_core::_ops::dispatch_metrics_fast(
+                opctx,
+                deno_core::_ops::OpMetricsEvent::Completed,
+            );
+            res
+        }
+        #[allow(clippy::too_many_arguments)]
+        extern "C" fn v8_fn_ptr_fast<'s>(
+            this: deno_core::v8::Local<deno_core::v8::Object>,
+            fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions<
+                's,
+            >,
+        ) -> () {
+            #[cfg(debug_assertions)]
+            let _reentrancy_check_guard = deno_core::_ops::reentrancy_check(
+                &<Self as deno_core::_ops::Op>::DECL,
+            );
+            let fast_api_callback_options: &'s mut _ = unsafe {
+                &mut *fast_api_callback_options
+            };
+            let mut scope = unsafe {
+                deno_core::v8::CallbackScope::new(&*fast_api_callback_options)
+            };
+            let opctx: &'s _ = unsafe {
+                &*(deno_core::v8::Local::<
+                    deno_core::v8::External,
+                >::cast_unchecked(unsafe { fast_api_callback_options.data })
+                    .value() as *const deno_core::_ops::OpCtx)
+            };
+            let result = {
+                let arg0 = &mut scope;
+                Self::call(arg0)
+            };
+            let result = match result {
+                Ok(result) => result,
+                Err(err) => {
+                    let err = err.into();
+                    let mut scope = unsafe {
+                        deno_core::v8::CallbackScope::new(&*fast_api_callback_options)
+                    };
+                    let exception = deno_core::error::to_v8_error(
+                        &mut scope,
+                        opctx.get_error_class_fn,
+                        &err,
+                    );
+                    scope.throw_exception(exception);
+                    return unsafe { std::mem::zeroed() };
+                }
+            };
+            result as _
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/result_scope.rs b/ops/op2/test_cases/sync/result_scope.rs
index b2b59b6..70c1ce7 100644
--- a/ops/op2/test_cases/sync/result_scope.rs
+++ b/ops/op2/test_cases/sync/result_scope.rs
@@ -5,7 +5,7 @@ deno_ops_compile_test_runner::prelude!();
 use deno_core::error::AnyError;
 use deno_core::v8;
 
-#[op2]
+#[op2(fast)]
 pub fn op_void_with_result(
   _scope: &mut v8::HandleScope,
 ) -> Result<(), AnyError> {
diff --git a/ops/op2/test_cases/sync/result_void.out b/ops/op2/test_cases/sync/result_void.out
index b0d7b1b..0f0d3f8 100644
--- a/ops/op2/test_cases/sync/result_void.out
+++ b/ops/op2/test_cases/sync/result_void.out
@@ -43,7 +43,7 @@ pub const fn op_void_with_result() -> ::deno_core::_ops::OpDecl {
     }
     impl op_void_with_result {
         pub const fn name() -> &'static str {
-            stringify!(op_void_with_result)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -97,10 +97,15 @@ pub const fn op_void_with_result() -> ::deno_core::_ops::OpDecl {
                 Ok(result) => result,
                 Err(err) => {
                     let err = err.into();
-                    unsafe {
-                        opctx.unsafely_set_last_error_for_ops_only(err);
-                    }
-                    fast_api_callback_options.fallback = true;
+                    let mut scope = unsafe {
+                        deno_core::v8::CallbackScope::new(&*fast_api_callback_options)
+                    };
+                    let exception = deno_core::error::to_v8_error(
+                        &mut scope,
+                        opctx.get_error_class_fn,
+                        &err,
+                    );
+                    scope.throw_exception(exception);
                     return unsafe { std::mem::zeroed() };
                 }
             };
@@ -118,34 +123,17 @@ pub const fn op_void_with_result() -> ::deno_core::_ops::OpDecl {
             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)
-            };
-            if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } {
-                let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) };
-                let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(
-                    info,
-                );
-                let err = err.into();
-                let exception = deno_core::error::to_v8_error(
-                    &mut scope,
-                    opctx.get_error_class_fn,
-                    &err,
-                );
-                scope.throw_exception(exception);
-                return 1;
-            }
             let result = { Self::call() };
             match result {
                 Ok(result) => deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv),
                 Err(err) => {
                     let mut scope = unsafe { deno_core::v8::CallbackScope::new(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 err = err.into();
                     let exception = deno_core::error::to_v8_error(
                         &mut scope,
diff --git a/ops/op2/test_cases/sync/serde_v8.out b/ops/op2/test_cases/sync/serde_v8.out
index 767bf9f..1e2890b 100644
--- a/ops/op2/test_cases/sync/serde_v8.out
+++ b/ops/op2/test_cases/sync/serde_v8.out
@@ -23,7 +23,7 @@ pub const fn op_serde_v8() -> ::deno_core::_ops::OpDecl {
     }
     impl op_serde_v8 {
         pub const fn name() -> &'static str {
-            stringify!(op_serde_v8)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/smi.out b/ops/op2/test_cases/sync/smi.out
index cb30253..3e7fd81 100644
--- a/ops/op2/test_cases/sync/smi.out
+++ b/ops/op2/test_cases/sync/smi.out
@@ -56,7 +56,7 @@ const fn op_smi_unsigned_return() -> ::deno_core::_ops::OpDecl {
     }
     impl op_smi_unsigned_return {
         pub const fn name() -> &'static str {
-            stringify!(op_smi_unsigned_return)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -292,7 +292,7 @@ const fn op_smi_signed_return() -> ::deno_core::_ops::OpDecl {
     }
     impl op_smi_signed_return {
         pub const fn name() -> &'static str {
-            stringify!(op_smi_signed_return)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -495,7 +495,7 @@ const fn op_smi_option() -> ::deno_core::_ops::OpDecl {
     }
     impl op_smi_option {
         pub const fn name() -> &'static str {
-            stringify!(op_smi_option)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/string_cow.out b/ops/op2/test_cases/sync/string_cow.out
index affb50a..a14c271 100644
--- a/ops/op2/test_cases/sync/string_cow.out
+++ b/ops/op2/test_cases/sync/string_cow.out
@@ -47,7 +47,7 @@ const fn op_string_cow() -> ::deno_core::_ops::OpDecl {
     }
     impl op_string_cow {
         pub const fn name() -> &'static str {
-            stringify!(op_string_cow)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/string_onebyte.out b/ops/op2/test_cases/sync/string_onebyte.out
index 5f71abe..57c16ae 100644
--- a/ops/op2/test_cases/sync/string_onebyte.out
+++ b/ops/op2/test_cases/sync/string_onebyte.out
@@ -47,7 +47,7 @@ const fn op_string_onebyte() -> ::deno_core::_ops::OpDecl {
     }
     impl op_string_onebyte {
         pub const fn name() -> &'static str {
-            stringify!(op_string_onebyte)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/string_option_return.out b/ops/op2/test_cases/sync/string_option_return.out
index 0715750..02a5c6f 100644
--- a/ops/op2/test_cases/sync/string_option_return.out
+++ b/ops/op2/test_cases/sync/string_option_return.out
@@ -23,7 +23,7 @@ pub const fn op_string_return() -> ::deno_core::_ops::OpDecl {
     }
     impl op_string_return {
         pub const fn name() -> &'static str {
-            stringify!(op_string_return)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/string_owned.out b/ops/op2/test_cases/sync/string_owned.out
index 75476ee..6edb486 100644
--- a/ops/op2/test_cases/sync/string_owned.out
+++ b/ops/op2/test_cases/sync/string_owned.out
@@ -47,7 +47,7 @@ const fn op_string_owned() -> ::deno_core::_ops::OpDecl {
     }
     impl op_string_owned {
         pub const fn name() -> &'static str {
-            stringify!(op_string_owned)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/string_ref.out b/ops/op2/test_cases/sync/string_ref.out
index bb7b2b6..83b335c 100644
--- a/ops/op2/test_cases/sync/string_ref.out
+++ b/ops/op2/test_cases/sync/string_ref.out
@@ -47,7 +47,7 @@ const fn op_string_owned() -> ::deno_core::_ops::OpDecl {
     }
     impl op_string_owned {
         pub const fn name() -> &'static str {
-            stringify!(op_string_owned)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
diff --git a/ops/op2/test_cases/sync/string_return.out b/ops/op2/test_cases/sync/string_return.out
index 8b6bf4e..4366262 100644
--- a/ops/op2/test_cases/sync/string_return.out
+++ b/ops/op2/test_cases/sync/string_return.out
@@ -23,7 +23,7 @@ pub const fn op_string_return() -> ::deno_core::_ops::OpDecl {
     }
     impl op_string_return {
         pub const fn name() -> &'static str {
-            stringify!(op_string_return)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -120,7 +120,7 @@ pub const fn op_string_return_ref() -> ::deno_core::_ops::OpDecl {
     }
     impl op_string_return_ref {
         pub const fn name() -> &'static str {
-            stringify!(op_string_return_ref)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
@@ -217,7 +217,7 @@ pub const fn op_string_return_cow() -> ::deno_core::_ops::OpDecl {
     }
     impl op_string_return_cow {
         pub const fn name() -> &'static str {
-            stringify!(op_string_return_cow)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/to_v8.out b/ops/op2/test_cases/sync/to_v8.out
index d55855e..757d995 100644
--- a/ops/op2/test_cases/sync/to_v8.out
+++ b/ops/op2/test_cases/sync/to_v8.out
@@ -23,7 +23,7 @@ pub const fn op_to_v8_return() -> ::deno_core::_ops::OpDecl {
     }
     impl op_to_v8_return {
         pub const fn name() -> &'static str {
-            stringify!(op_to_v8_return)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/v8_global.out b/ops/op2/test_cases/sync/v8_global.out
index b23d06a..878c5e7 100644
--- a/ops/op2/test_cases/sync/v8_global.out
+++ b/ops/op2/test_cases/sync/v8_global.out
@@ -23,7 +23,7 @@ pub const fn op_global() -> ::deno_core::_ops::OpDecl {
     }
     impl op_global {
         pub const fn name() -> &'static str {
-            stringify!(op_global)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/v8_handlescope.out b/ops/op2/test_cases/sync/v8_handlescope.out
index 1206e62..82f98d2 100644
--- a/ops/op2/test_cases/sync/v8_handlescope.out
+++ b/ops/op2/test_cases/sync/v8_handlescope.out
@@ -23,7 +23,7 @@ const fn op_handlescope() -> ::deno_core::_ops::OpDecl {
     }
     impl op_handlescope {
         pub const fn name() -> &'static str {
-            stringify!(op_handlescope)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/v8_lifetime.out b/ops/op2/test_cases/sync/v8_lifetime.out
index 05b9325..ba3b8c5 100644
--- a/ops/op2/test_cases/sync/v8_lifetime.out
+++ b/ops/op2/test_cases/sync/v8_lifetime.out
@@ -23,7 +23,7 @@ pub const fn op_v8_lifetime() -> ::deno_core::_ops::OpDecl {
     }
     impl op_v8_lifetime {
         pub const fn name() -> &'static str {
-            stringify!(op_v8_lifetime)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(
diff --git a/ops/op2/test_cases/sync/v8_ref_option.out b/ops/op2/test_cases/sync/v8_ref_option.out
index ad7fd3c..1a57b98 100644
--- a/ops/op2/test_cases/sync/v8_ref_option.out
+++ b/ops/op2/test_cases/sync/v8_ref_option.out
@@ -53,7 +53,7 @@ pub const fn op_v8_lifetime() -> ::deno_core::_ops::OpDecl {
     }
     impl op_v8_lifetime {
         pub const fn name() -> &'static str {
-            stringify!(op_v8_lifetime)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[allow(clippy::too_many_arguments)]
         extern "C" fn v8_fn_ptr_fast_metrics<'s>(
@@ -104,8 +104,22 @@ pub const fn op_v8_lifetime() -> ::deno_core::_ops::OpDecl {
                 let Ok(mut arg0) = deno_core::_ops::v8_try_convert_option::<
                     deno_core::v8::String,
                 >(arg0) else {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
+                    {
+                        let mut scope = unsafe {
+                            deno_core::v8::CallbackScope::new(
+                                &*fast_api_callback_options,
+                            )
+                        };
+                        let msg = deno_core::v8::String::new_from_one_byte(
+                                &mut scope,
+                                "expected String".as_bytes(),
+                                deno_core::v8::NewStringType::Normal,
+                            )
+                            .unwrap();
+                        let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                        scope.throw_exception(exc);
+                        return unsafe { std::mem::zeroed() };
+                    }
                 };
                 let arg0 = match &arg0 {
                     None => None,
@@ -114,8 +128,22 @@ pub const fn op_v8_lifetime() -> ::deno_core::_ops::OpDecl {
                 let Ok(mut arg1) = deno_core::_ops::v8_try_convert_option::<
                     deno_core::v8::String,
                 >(arg1) else {
-                    fast_api_callback_options.fallback = true;
-                    return unsafe { std::mem::zeroed() };
+                    {
+                        let mut scope = unsafe {
+                            deno_core::v8::CallbackScope::new(
+                                &*fast_api_callback_options,
+                            )
+                        };
+                        let msg = deno_core::v8::String::new_from_one_byte(
+                                &mut scope,
+                                "expected String".as_bytes(),
+                                deno_core::v8::NewStringType::Normal,
+                            )
+                            .unwrap();
+                        let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                        scope.throw_exception(exc);
+                        return unsafe { std::mem::zeroed() };
+                    }
                 };
                 let arg1 = match &arg1 {
                     None => None,
diff --git a/ops/op2/test_cases/sync/v8_string.out b/ops/op2/test_cases/sync/v8_string.out
index 42ee2e2..a997db4 100644
--- a/ops/op2/test_cases/sync/v8_string.out
+++ b/ops/op2/test_cases/sync/v8_string.out
@@ -23,7 +23,7 @@ const fn op_v8_string() -> ::deno_core::_ops::OpDecl {
     }
     impl op_v8_string {
         pub const fn name() -> &'static str {
-            stringify!(op_v8_string)
+            <Self as deno_core::_ops::Op>::NAME
         }
         #[inline(always)]
         fn slow_function_impl<'s>(