Repository: denoland/deno_core
Pull request #295 (2023-11-01 19:59:33)

perf(ops): trim async op cruft
Removes a lot of the old async machinery that is no longer used, and provides a path forward for op tracking, used in Deno test. The metrics object has been trimmed down significantly.

Before:
```
test bench_op_async_void                  ... bench:     104,595 ns/iter (+/- 4,252)
```

After:
```
test bench_op_async_void                  ... bench:      75,782 ns/iter (+/- 3,128)
```

Changes:

 - Don't create a scope for async ops unless or until we really need one
 - Start using the new metrics functions
 - Replace OpTracker with a new metrics tracker that is optional and zero-cost when unused



Patch:
diff --git a/core/01_core.js b/core/01_core.js
index 804ad0a..49bd561 100644
--- a/core/01_core.js
+++ b/core/01_core.js
@@ -637,12 +637,21 @@ for (let i = 0; i < 10; i++) {
   }
 
   function metrics() {
-    const { 0: aggregate, 1: perOps } = ops.op_metrics();
-    aggregate.ops = ObjectFromEntries(ArrayPrototypeMap(
-      ops.op_op_names(),
-      (opName, opId) => [opName, perOps[opId]],
-    ));
-    return aggregate;
+    // TODO(mmastrac): we should replace this with a newer API
+    return {
+      opsDispatched: 0,
+      opsDispatchedSync: 0,
+      opsDispatchedAsync: 0,
+      opsDispatchedAsyncUnref: 0,
+      opsCompleted: 0,
+      opsCompletedSync: 0,
+      opsCompletedAsync: 0,
+      opsCompletedAsyncUnref: 0,
+      bytesSentControl: 0,
+      bytesSentData: 0,
+      bytesReceived: 0,
+      ops: {},
+    };
   }
 
   let reportExceptionCallback = undefined;
diff --git a/core/benches/ops/sync.rs b/core/benches/ops/sync.rs
index 8fe76b7..97b9e4b 100644
--- a/core/benches/ops/sync.rs
+++ b/core/benches/ops/sync.rs
@@ -11,6 +11,8 @@ deno_core::extension!(
   ops = [
     op_void,
     op_void_nofast,
+    op_void_metrics,
+    op_void_nofast_metrics,
     op_u32,
     op_option_u32,
     op_string,
@@ -46,6 +48,12 @@ pub fn op_void() {}
 #[op2(nofast)]
 pub fn op_void_nofast() {}
 
+#[op2(fast)]
+pub fn op_void_metrics() {}
+
+#[op2(nofast)]
+pub fn op_void_nofast_metrics() {}
+
 #[op2(fast)]
 pub fn op_u32() -> u32 {
   1
@@ -151,6 +159,14 @@ fn bench_op(
     // We need to feature gate this here to prevent IDE errors
     #[cfg(feature = "unsafe_runtime_options")]
     unsafe_expose_natives_and_gc: true,
+    // Add metrics just for the metrics ops
+    op_metrics_factory_fn: Some(Box::new(|_, _, op| {
+      if op.name.ends_with("_metrics") {
+        Some(std::rc::Rc::new(|_, _| {}))
+      } else {
+        None
+      }
+    })),
     ..Default::default()
   });
   let err_mapper =
@@ -211,6 +227,22 @@ fn bench_op_void_nofast(b: &mut Bencher) {
   bench_op(b, BENCH_COUNT, "op_void_nofast", 0, "op_void_nofast();");
 }
 
+/// A void function with no return value.
+fn bench_op_void_metrics(b: &mut Bencher) {
+  bench_op(b, BENCH_COUNT, "op_void_metrics", 0, "op_void_metrics()");
+}
+
+/// A void function with no return value.
+fn bench_op_void_nofast_metrics(b: &mut Bencher) {
+  bench_op(
+    b,
+    BENCH_COUNT,
+    "op_void_nofast_metrics",
+    0,
+    "op_void_nofast_metrics()",
+  );
+}
+
 /// A function with a numeric return value.
 fn bench_op_u32(b: &mut Bencher) {
   bench_op(b, BENCH_COUNT, "op_u32", 0, "accum += op_u32();");
@@ -422,6 +454,8 @@ benchmark_group!(
   bench_op_void,
   bench_op_void_2x,
   bench_op_void_nofast,
+  bench_op_void_metrics,
+  bench_op_void_nofast_metrics,
   bench_op_u32,
   bench_op_option_u32,
   bench_op_string_bytestring,
diff --git a/core/lib.rs b/core/lib.rs
index ca22172..2f27b7d 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -105,8 +105,8 @@ pub use crate::ops_builtin::op_print;
 pub use crate::ops_builtin::op_resources;
 pub use crate::ops_builtin::op_void_async;
 pub use crate::ops_builtin::op_void_sync;
-pub use crate::ops_metrics::OpMetrics;
-pub use crate::ops_metrics::OpsTracker;
+pub use crate::ops_metrics::OpMetricsSummary;
+pub use crate::ops_metrics::OpMetricsSummaryTracker;
 pub use crate::path::strip_unc_prefix;
 pub use crate::resources::AsyncResult;
 pub use crate::resources::Resource;
@@ -142,10 +142,12 @@ pub mod _ops {
   pub use super::error_codes::get_error_code;
   pub use super::extensions::Op;
   pub use super::extensions::OpDecl;
-  pub use super::ops::to_op_result;
   pub use super::ops::OpCtx;
-  pub use super::ops::OpMetricsEvent;
   pub use super::ops::OpResult;
+  pub use super::ops_metrics::dispatch_metrics_async;
+  pub use super::ops_metrics::dispatch_metrics_fast;
+  pub use super::ops_metrics::dispatch_metrics_slow;
+  pub use super::ops_metrics::OpMetricsEvent;
   pub use super::runtime::ops::*;
   pub use super::runtime::ops_rust_to_v8::*;
   pub use super::runtime::V8_WRAPPER_OBJECT_INDEX;
diff --git a/core/ops.rs b/core/ops.rs
index f4b9390..bc1649f 100644
--- a/core/ops.rs
+++ b/core/ops.rs
@@ -3,12 +3,12 @@
 use crate::error::AnyError;
 use crate::error::GetErrorClassFn;
 use crate::gotham_state::GothamState;
+use crate::ops_metrics::OpMetricsFn;
 use crate::resources::ResourceTable;
 use crate::runtime::ContextState;
 use crate::runtime::JsRuntimeState;
 use crate::FeatureChecker;
 use crate::OpDecl;
-use crate::OpsTracker;
 use anyhow::Error;
 use futures::task::AtomicWaker;
 use futures::Future;
@@ -113,45 +113,26 @@ impl OpError {
   }
 }
 
-pub fn to_op_result<R: Serialize + 'static>(
-  get_class: GetErrorClassFn,
-  result: Result<R, Error>,
-) -> OpResult {
-  match result {
-    Ok(v) => OpResult::Ok(v.into()),
-    Err(err) => OpResult::Err(OpError::new(get_class, err)),
-  }
-}
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum OpMetricsEvent {
-  /// Entered an op dispatch.
-  Dispatched,
-  /// Left an op synchronously.
-  Completed,
-  /// Left an op asynchronously.
-  CompletedAsync,
-  /// Left an op synchronously with an exception.
-  Error,
-  /// Left an op asynchronously with an exception.
-  ErrorAsync,
-}
-
-pub type OpMetricsFn = Rc<dyn Fn(&OpDecl, OpMetricsEvent)>;
-
 /// Per-op context.
 ///
 // Note: We don't worry too much about the size of this struct because it's allocated once per realm, and is
 // stored in a contiguous array.
 pub struct OpCtx {
+  /// The id for this op. Will be identical across realms.
   pub id: OpId,
-  /// A stashed Isolate that ops can use in callbacks
+
+  /// A stashed Isolate that ops can make use of. This is a raw isolate pointer, and as such, is
+  /// extremely dangerous to use.
   pub isolate: *mut Isolate,
+
+  #[doc(hidden)]
   pub state: Rc<RefCell<OpState>>,
+  #[doc(hidden)]
   pub get_error_class_fn: GetErrorClassFn,
-  pub decl: Rc<OpDecl>,
-  pub fast_fn_c_info: Option<NonNull<v8::fast_api::CFunctionInfo>>,
-  pub runtime_state: Weak<RefCell<JsRuntimeState>>,
+
+  pub(crate) decl: Rc<OpDecl>,
+  pub(crate) fast_fn_c_info: Option<NonNull<v8::fast_api::CFunctionInfo>>,
+  pub(crate) runtime_state: Weak<RefCell<JsRuntimeState>>,
   pub(crate) metrics_fn: Option<OpMetricsFn>,
   pub(crate) context_state: Rc<RefCell<ContextState>>,
   /// If the last fast op failed, stores the error to be picked up by the slow op.
@@ -177,9 +158,9 @@ impl OpCtx {
     // version and require a slightly different set of arguments (for example, it may need the fastcall
     // callback information to get the `OpCtx`).
     let fast_fn = if metrics_fn.is_some() {
-      &decl.fast_fn
-    } else {
       &decl.fast_fn_with_metrics
+    } else {
+      &decl.fast_fn
     };
 
     if let Some(fast_fn) = fast_fn {
@@ -213,6 +194,11 @@ impl OpCtx {
     }
   }
 
+  #[inline(always)]
+  pub fn decl(&self) -> &OpDecl {
+    &self.decl
+  }
+
   pub fn metrics_enabled(&self) -> bool {
     self.metrics_fn.is_some()
   }
@@ -247,21 +233,16 @@ impl OpCtx {
 /// Maintains the resources and ops inside a JS runtime.
 pub struct OpState {
   pub resource_table: ResourceTable,
-  pub tracker: OpsTracker,
   pub(crate) gotham_state: GothamState,
   pub waker: Arc<AtomicWaker>,
   pub feature_checker: Arc<FeatureChecker>,
 }
 
 impl OpState {
-  pub fn new(
-    ops_count: usize,
-    maybe_feature_checker: Option<Arc<FeatureChecker>>,
-  ) -> OpState {
+  pub fn new(maybe_feature_checker: Option<Arc<FeatureChecker>>) -> OpState {
     OpState {
       resource_table: Default::default(),
       gotham_state: Default::default(),
-      tracker: OpsTracker::new(ops_count),
       waker: Arc::new(AtomicWaker::new()),
       feature_checker: maybe_feature_checker.unwrap_or_default(),
     }
diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs
index 871113a..90b4bbb 100644
--- a/core/ops_builtin.rs
+++ b/core/ops_builtin.rs
@@ -6,7 +6,6 @@ use crate::io::BufMutView;
 use crate::io::BufView;
 use crate::op2;
 use crate::ops_builtin_v8;
-use crate::ops_metrics::OpMetrics;
 use crate::resources::ResourceId;
 use crate::JsBuffer;
 use crate::OpState;
@@ -35,7 +34,6 @@ deno_core::extension!(
     op_void_async_deferred,
     op_add,
     op_add_async,
-    // TODO(@AaronO): track IO metrics for builtin streams
     op_read,
     op_read_all,
     op_write,
@@ -44,7 +42,6 @@ deno_core::extension!(
     op_write_all,
     op_write_type_error,
     op_shutdown,
-    op_metrics,
     op_format_file_name,
     op_is_proxy,
     op_str_byte_length,
@@ -145,14 +142,6 @@ pub fn op_try_close(state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId) {
   }
 }
 
-#[op2]
-#[serde]
-pub fn op_metrics(state: &mut OpState) -> (OpMetrics, Vec<OpMetrics>) {
-  let aggregate = state.tracker.aggregate();
-  let per_op = state.tracker.per_op().clone();
-  (aggregate, per_op)
-}
-
 /// Builtin utility to print to stdout/stderr
 #[op2(fast)]
 pub fn op_print(#[string] msg: &str, is_err: bool) -> Result<(), Error> {
diff --git a/core/ops_metrics.rs b/core/ops_metrics.rs
index ae606f6..67de7c8 100644
--- a/core/ops_metrics.rs
+++ b/core/ops_metrics.rs
@@ -1,92 +1,136 @@
 // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
 
+use crate::ops::OpCtx;
 use crate::serde::Serialize;
+use crate::OpDecl;
 use crate::OpId;
 use std::cell::Ref;
 use std::cell::RefCell;
 use std::cell::RefMut;
+use std::rc::Rc;
 
-// TODO(@AaronO): split into AggregateMetrics & PerOpMetrics
-#[derive(Clone, Default, Debug, Serialize)]
+/// The type of op metrics event.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[repr(u8)]
+pub enum OpMetricsEvent {
+  /// Entered an op dispatch.
+  Dispatched,
+  /// Left an op synchronously.
+  Completed,
+  /// Left an op asynchronously.
+  CompletedAsync,
+  /// Left an op synchronously with an exception.
+  Error,
+  /// Left an op asynchronously with an exception.
+  ErrorAsync,
+}
+
+/// A callback to receieve an [`OpMetricsEvent`].
+pub type OpMetricsFn = Rc<dyn Fn(&OpCtx, OpMetricsEvent)>;
+
+// TODO(mmastrac): this would be better as a trait
+/// A callback to retrieve an optional [`OpMetricsFn`] for this op.
+pub type OpMetricsFactoryFn =
+  Box<dyn Fn(OpId, usize, &OpDecl) -> Option<OpMetricsFn>>;
+
+#[doc(hidden)]
+pub fn dispatch_metrics_fast(opctx: &OpCtx, metrics: OpMetricsEvent) {
+  // SAFETY: this should only be called from ops where we know the function is Some
+  unsafe { (opctx.metrics_fn.as_ref().unwrap_unchecked())(opctx, metrics) }
+}
+
+#[doc(hidden)]
+pub fn dispatch_metrics_slow(opctx: &OpCtx, metrics: OpMetricsEvent) {
+  // SAFETY: this should only be called from ops where we know the function is Some
+  unsafe { (opctx.metrics_fn.as_ref().unwrap_unchecked())(opctx, metrics) }
+}
+
+#[doc(hidden)]
+pub fn dispatch_metrics_async(opctx: &OpCtx, metrics: OpMetricsEvent) {
+  // SAFETY: this should only be called from ops where we know the function is Some
+  unsafe { (opctx.metrics_fn.as_ref().unwrap_unchecked())(opctx, metrics) }
+}
+
+/// Used for both aggregate and per-op metrics.
+#[derive(Clone, Default, Debug, Serialize, PartialEq, Eq)]
 #[serde(rename_all = "camelCase")]
-pub struct OpMetrics {
-  pub ops_dispatched: u64,
+pub struct OpMetricsSummary {
   pub ops_dispatched_sync: u64,
   pub ops_dispatched_async: u64,
-  // TODO(bartlomieju): this field is never updated
-  pub ops_dispatched_async_unref: u64,
-  pub ops_completed: u64,
-  pub ops_completed_sync: u64,
   pub ops_completed_async: u64,
-  // TODO(bartlomieju): this field is never updated
-  pub ops_completed_async_unref: u64,
-  pub bytes_sent_control: u64,
-  pub bytes_sent_data: u64,
-  pub bytes_received: u64,
 }
 
-// TODO(@AaronO): track errors
-#[derive(Default, Debug)]
-pub struct OpsTracker {
-  ops: RefCell<Vec<OpMetrics>>,
+impl OpMetricsSummary {
+  /// Does this op have outstanding async op dispatches?
+  pub fn has_outstanding_ops(&self) -> bool {
+    self.ops_dispatched_async > self.ops_completed_async
+  }
 }
 
-impl OpsTracker {
-  pub fn new(ops_count: usize) -> Self {
-    Self {
-      ops: RefCell::new(vec![Default::default(); ops_count]),
-    }
-  }
+#[derive(Default, Debug)]
+pub struct OpMetricsSummaryTracker {
+  ops: RefCell<Vec<OpMetricsSummary>>,
+}
 
-  pub fn per_op(&self) -> Ref<'_, Vec<OpMetrics>> {
+impl OpMetricsSummaryTracker {
+  pub fn per_op(&self) -> Ref<'_, Vec<OpMetricsSummary>> {
     self.ops.borrow()
   }
 
-  pub fn aggregate(&self) -> OpMetrics {
-    let mut sum = OpMetrics::default();
+  pub fn aggregate(&self) -> OpMetricsSummary {
+    let mut sum = OpMetricsSummary::default();
 
     for metrics in self.ops.borrow().iter() {
-      sum.ops_dispatched += metrics.ops_dispatched;
       sum.ops_dispatched_sync += metrics.ops_dispatched_sync;
       sum.ops_dispatched_async += metrics.ops_dispatched_async;
-      sum.ops_dispatched_async_unref += metrics.ops_dispatched_async_unref;
-      sum.ops_completed += metrics.ops_completed;
-      sum.ops_completed_sync += metrics.ops_completed_sync;
       sum.ops_completed_async += metrics.ops_completed_async;
-      sum.ops_completed_async_unref += metrics.ops_completed_async_unref;
-      sum.bytes_sent_control += metrics.bytes_sent_control;
-      sum.bytes_sent_data += metrics.bytes_sent_data;
-      sum.bytes_received += metrics.bytes_received;
     }
 
     sum
   }
 
   #[inline]
-  fn metrics_mut(&self, id: OpId) -> RefMut<OpMetrics> {
+  fn metrics_mut(&self, id: OpId) -> RefMut<OpMetricsSummary> {
     RefMut::map(self.ops.borrow_mut(), |ops| &mut ops[id as usize])
   }
 
-  #[inline]
-  pub fn track_sync(&self, id: OpId) {
-    let mut metrics = self.metrics_mut(id);
-    metrics.ops_dispatched += 1;
-    metrics.ops_completed += 1;
-    metrics.ops_dispatched_sync += 1;
-    metrics.ops_completed_sync += 1;
+  /// Returns a [`OpMetricsFn`] for this tracker.
+  fn op_metrics_fn(self: Rc<Self>) -> OpMetricsFn {
+    Rc::new(move |ctx, event| match event {
+      OpMetricsEvent::Dispatched => {
+        if ctx.decl.is_async {
+          self.metrics_mut(ctx.id).ops_dispatched_async += 1;
+        } else {
+          self.metrics_mut(ctx.id).ops_dispatched_sync += 1;
+        }
+      }
+      OpMetricsEvent::Completed
+      | OpMetricsEvent::Error
+      | OpMetricsEvent::CompletedAsync
+      | OpMetricsEvent::ErrorAsync => {
+        if ctx.decl.is_async {
+          self.metrics_mut(ctx.id).ops_completed_async += 1;
+        }
+      }
+    })
   }
 
-  #[inline]
-  pub fn track_async(&self, id: OpId) {
-    let mut metrics = self.metrics_mut(id);
-    metrics.ops_dispatched += 1;
-    metrics.ops_dispatched_async += 1;
-  }
-
-  #[inline]
-  pub fn track_async_completed(&self, id: OpId) {
-    let mut metrics = self.metrics_mut(id);
-    metrics.ops_completed += 1;
-    metrics.ops_completed_async += 1;
+  /// Retrieves the metrics factory function for this tracker.
+  pub fn op_metrics_factory_fn(
+    self: Rc<Self>,
+    op_enabled: impl Fn(&OpDecl) -> bool + 'static,
+  ) -> OpMetricsFactoryFn {
+    Box::new(move |_, total, op| {
+      let mut ops = self.ops.borrow_mut();
+      if ops.capacity() == 0 {
+        ops.reserve_exact(total);
+      }
+      ops.push(OpMetricsSummary::default());
+      if op_enabled(op) {
+        Some(self.clone().op_metrics_fn())
+      } else {
+        None
+      }
+    })
   }
 }
diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs
index fcab70d..a6a7fbd 100644
--- a/core/runtime/jsruntime.rs
+++ b/core/runtime/jsruntime.rs
@@ -2,7 +2,6 @@
 
 use super::bindings;
 use super::jsrealm::JsRealmInner;
-use super::ops::dispatch_metrics_async;
 use super::snapshot_util;
 use crate::error::exception_to_err_result;
 use crate::error::generic_error;
@@ -22,6 +21,9 @@ use crate::modules::ModuleId;
 use crate::modules::ModuleLoader;
 use crate::modules::ModuleMap;
 use crate::ops::*;
+use crate::ops_metrics::dispatch_metrics_async;
+use crate::ops_metrics::OpMetricsEvent;
+use crate::ops_metrics::OpMetricsFactoryFn;
 use crate::runtime::ContextState;
 use crate::runtime::JsRealm;
 use crate::source_map::SourceMapCache;
@@ -364,8 +366,6 @@ fn v8_init(
   v8::V8::initialize();
 }
 
-pub type OpMetricsFactoryFn = Box<dyn Fn(&OpDecl) -> Option<OpMetricsFn>>;
-
 #[derive(Default)]
 pub struct RuntimeOptions {
   /// Source map reference for errors.
@@ -384,7 +384,7 @@ pub struct RuntimeOptions {
 
   /// Provide a function that may optionally provide a metrics collector
   /// for a given op.
-  pub op_metrics_fn: Option<OpMetricsFactoryFn>,
+  pub op_metrics_factory_fn: Option<OpMetricsFactoryFn>,
 
   /// JsRuntime extensions, not to be confused with ES modules.
   /// Only ops registered by extensions will be initialized. If you need
@@ -603,14 +603,17 @@ impl JsRuntime {
 
     let weak = Rc::downgrade(&state_rc);
     let context_state = Rc::new(RefCell::new(ContextState::default()));
+    let count = ops.len();
     let mut op_ctxs = ops
       .into_iter()
       .enumerate()
       .map(|(id, decl)| {
-        let metrics_fn =
-          options.op_metrics_fn.as_ref().and_then(|f| (f)(&decl));
+        let metrics_fn = options
+          .op_metrics_factory_fn
+          .as_ref()
+          .and_then(|f| (f)(id as _, count, &decl));
         OpCtx::new(
-          id as u16,
+          id as _,
           std::ptr::null_mut(),
           context_state.clone(),
           Rc::new(decl),
@@ -1109,7 +1112,7 @@ impl JsRuntime {
 
     let ops = Self::collect_ops(&mut options.extensions);
 
-    let mut op_state = OpState::new(ops.len(), options.feature_checker.take());
+    let mut op_state = OpState::new(options.feature_checker.take());
 
     // Setup state
     for e in &mut options.extensions {
@@ -1990,12 +1993,6 @@ impl JsRuntime {
         };
         // TODO(mmastrac): If this task is really errored, things could be pretty bad
         let PendingOp(promise_id, op_id, resp, metrics_event) = item.unwrap();
-        state
-          .borrow()
-          .op_state
-          .borrow()
-          .tracker
-          .track_async_completed(op_id);
         context_state.unrefed_ops.remove(&promise_id);
         dispatched_ops |= true;
         args.push(v8::Integer::new(scope, promise_id).into());
diff --git a/core/runtime/ops.rs b/core/runtime/ops.rs
index 44b69c3..f846a63 100644
--- a/core/runtime/ops.rs
+++ b/core/runtime/ops.rs
@@ -1,9 +1,7 @@
 // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
 use crate::ops::*;
 use crate::OpResult;
-use crate::PromiseId;
 use anyhow::Error;
-use futures::future::Either;
 use futures::future::Future;
 use futures::future::FutureExt;
 use futures::task::noop_waker_ref;
@@ -11,7 +9,6 @@ use serde::Deserialize;
 use serde_v8::from_v8;
 use serde_v8::V8Sliceable;
 use std::borrow::Cow;
-use std::cell::RefCell;
 use std::ffi::c_void;
 use std::future::ready;
 use std::mem::MaybeUninit;
@@ -26,117 +23,6 @@ use v8::WriteOptions;
 /// so this is a limited resource!
 pub const STRING_STACK_BUFFER_SIZE: usize = 1024 * 8;
 
-#[inline]
-pub fn queue_fast_async_op<R: serde::Serialize + 'static>(
-  ctx: &OpCtx,
-  promise_id: PromiseId,
-  op: impl Future<Output = Result<R, Error>> + 'static,
-) {
-  RefCell::borrow(&ctx.state).tracker.track_async(ctx.id);
-  let fut =
-    op.map(|result| crate::_ops::to_op_result(ctx.get_error_class_fn, result));
-  ctx
-    .context_state
-    .borrow_mut()
-    .pending_ops
-    .spawn(OpCall::new(ctx, promise_id, fut));
-}
-
-#[inline]
-pub fn map_async_op1<R: serde::Serialize + 'static>(
-  ctx: &OpCtx,
-  op: impl Future<Output = Result<R, Error>> + 'static,
-) -> impl Future<Output = OpResult> {
-  RefCell::borrow(&ctx.state).tracker.track_async(ctx.id);
-  op.map(|res| crate::_ops::to_op_result(ctx.get_error_class_fn, res))
-}
-
-#[inline]
-pub fn map_async_op2<R: serde::Serialize + 'static>(
-  ctx: &OpCtx,
-  op: impl Future<Output = R> + 'static,
-) -> impl Future<Output = OpResult> {
-  let state = RefCell::borrow(&ctx.state);
-  state.tracker.track_async(ctx.id);
-
-  op.map(|res| OpResult::Ok(res.into()))
-}
-
-#[inline]
-pub fn map_async_op3<R: serde::Serialize + 'static>(
-  ctx: &OpCtx,
-  op: Result<impl Future<Output = Result<R, Error>> + 'static, Error>,
-) -> impl Future<Output = OpResult> {
-  RefCell::borrow(&ctx.state).tracker.track_async(ctx.id);
-  match op {
-    Err(err) => Either::Left(ready(OpResult::Err(OpError::new(
-      ctx.get_error_class_fn,
-      err,
-    )))),
-    Ok(fut) => Either::Right(
-      fut.map(|res| crate::_ops::to_op_result(ctx.get_error_class_fn, res)),
-    ),
-  }
-}
-
-#[inline]
-pub fn map_async_op4<R: serde::Serialize + 'static>(
-  ctx: &OpCtx,
-  op: Result<impl Future<Output = R> + 'static, Error>,
-) -> impl Future<Output = OpResult> {
-  RefCell::borrow(&ctx.state).tracker.track_async(ctx.id);
-  match op {
-    Err(err) => Either::Left(ready(OpResult::Err(OpError::new(
-      ctx.get_error_class_fn,
-      err,
-    )))),
-    Ok(fut) => Either::Right(fut.map(|r| OpResult::Ok(r.into()))),
-  }
-}
-
-pub fn queue_async_op<'s>(
-  ctx: &OpCtx,
-  scope: &'s mut v8::HandleScope,
-  deferred: bool,
-  promise_id: PromiseId,
-  op: impl Future<Output = OpResult> + 'static,
-) -> Option<v8::Local<'s, v8::Value>> {
-  // An op's realm (as given by `OpCtx::realm_idx`) must match the realm in
-  // which it is invoked. Otherwise, we might have cross-realm object exposure.
-  // deno_core doesn't currently support such exposure, even though embedders
-  // can cause them, so we panic in debug mode (since the check is expensive).
-  // TODO(mmastrac): Restore this
-  // debug_assert_eq!(
-  //   runtime_state.borrow().context(ctx.realm_idx as usize, scope),
-  //   Some(scope.get_current_context())
-  // );
-
-  let id = ctx.id;
-  let metrics = ctx.metrics_enabled();
-
-  // TODO(mmastrac): We have to poll every future here because that assumption is baked into a large number
-  // of ops. If we can figure out a way around this, we can remove this call to boxed_local and save a malloc per future.
-  let mut pinned = op
-    .map(move |res| PendingOp(promise_id, id, res, metrics))
-    .boxed_local();
-
-  match pinned.poll_unpin(&mut Context::from_waker(noop_waker_ref())) {
-    Poll::Pending => {}
-    Poll::Ready(res) => {
-      if deferred {
-        ctx.context_state.borrow_mut().pending_ops.spawn(ready(res));
-        return None;
-      } else {
-        ctx.state.borrow_mut().tracker.track_async_completed(ctx.id);
-        return Some(res.2.to_v8(scope).unwrap());
-      }
-    }
-  }
-
-  ctx.context_state.borrow_mut().pending_ops.spawn(pinned);
-  None
-}
-
 #[inline]
 pub fn map_async_op_infallible<R: 'static>(
   ctx: &OpCtx,
@@ -150,7 +36,6 @@ pub fn map_async_op_infallible<R: 'static>(
   ) -> Result<v8::Local<'r, v8::Value>, serde_v8::Error>,
 ) -> Option<R> {
   let id = ctx.id;
-  ctx.state.borrow().tracker.track_async(id);
   let metrics = ctx.metrics_enabled();
 
   if lazy {
@@ -182,10 +67,6 @@ pub fn map_async_op_infallible<R: 'static>(
         if deferred {
           ready(res).boxed_local()
         } else {
-          if ctx.metrics_enabled() {
-            dispatch_metrics_async(ctx, OpMetricsEvent::Completed);
-          }
-          ctx.state.borrow_mut().tracker.track_async_completed(ctx.id);
           return Some(res.2);
         }
       }
@@ -219,7 +100,6 @@ pub fn map_async_op_fallible<R: 'static, E: Into<Error> + 'static>(
   ) -> Result<v8::Local<'r, v8::Value>, serde_v8::Error>,
 ) -> Option<Result<R, E>> {
   let id = ctx.id;
-  ctx.state.borrow().tracker.track_async(id);
   let metrics = ctx.metrics_enabled();
 
   if lazy {
@@ -255,14 +135,6 @@ pub fn map_async_op_fallible<R: 'static, E: Into<Error> + 'static>(
         if deferred {
           ready(res).boxed_local()
         } else {
-          if ctx.metrics_enabled() {
-            if res.2.is_err() {
-              dispatch_metrics_async(ctx, OpMetricsEvent::Error);
-            } else {
-              dispatch_metrics_async(ctx, OpMetricsEvent::Completed);
-            }
-          }
-          ctx.state.borrow_mut().tracker.track_async_completed(ctx.id);
           return Some(res.2);
         }
       }
@@ -288,18 +160,6 @@ pub fn map_async_op_fallible<R: 'static, E: Into<Error> + 'static>(
   None
 }
 
-pub fn dispatch_metrics_fast(opctx: &OpCtx, metrics: OpMetricsEvent) {
-  (opctx.metrics_fn.as_ref().unwrap())(&opctx.decl, metrics)
-}
-
-pub fn dispatch_metrics_slow(opctx: &OpCtx, metrics: OpMetricsEvent) {
-  (opctx.metrics_fn.as_ref().unwrap())(&opctx.decl, metrics)
-}
-
-pub fn dispatch_metrics_async(opctx: &OpCtx, metrics: OpMetricsEvent) {
-  (opctx.metrics_fn.as_ref().unwrap())(&opctx.decl, metrics)
-}
-
 macro_rules! try_number_some {
   ($n:ident $type:ident $is:ident) => {
     if $n.$is() {
diff --git a/ops/op2/dispatch_async.rs b/ops/op2/dispatch_async.rs
index 643e2c7..0ad67d7 100644
--- a/ops/op2/dispatch_async.rs
+++ b/ops/op2/dispatch_async.rs
@@ -49,11 +49,11 @@ pub(crate) fn generate_dispatch_async(
   let args = generate_dispatch_slow_call(generator_state, signature, 1)?;
 
   // Always need context and args
-  // TODO(mmastrac): Do we?
   generator_state.needs_opctx = true;
   generator_state.needs_args = true;
-  generator_state.needs_scope = true;
-  generator_state.needs_retval = true;
+
+  // We don't have an isolate-only fast path for async yet
+  generator_state.needs_scope |= generator_state.needs_isolate;
 
   let (return_value, mapper, return_value_immediate) =
     map_async_return_type(generator_state, &signature.ret_val).map_err(
@@ -62,10 +62,7 @@ pub(crate) fn generate_dispatch_async(
       },
     )?;
 
-  output.extend(gs_quote!(generator_state(result, opctx) => {
-    if #opctx.metrics_enabled() {
-      ::deno_core::_ops::dispatch_metrics_async(&#opctx, ::deno_core::_ops::OpMetricsEvent::Dispatched);
-    }
+  output.extend(gs_quote!(generator_state(result) => {
     let #result = {
       #args
     };
@@ -74,13 +71,10 @@ pub(crate) fn generate_dispatch_async(
   // TODO(mmastrac): we should save this unwrapped result
   if signature.ret_val.unwrap_result().is_some() {
     let exception = throw_exception(generator_state);
-    output.extend(gs_quote!(generator_state(opctx, result) => {
+    output.extend(gs_quote!(generator_state(result) => {
       let #result = match #result {
         Ok(#result) => #result,
         Err(err) => {
-          if #opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(&#opctx, ::deno_core::_ops::OpMetricsEvent::Error);
-          }
           // Handle eager error -- this will leave only a Future<R> or Future<Result<R>>
           #exception
         }
@@ -105,10 +99,12 @@ pub(crate) fn generate_dispatch_async(
         #return_value
       }) {
         // Eager poll returned a value
-        #return_value_immediate
+        #return_value_immediate;
+        return 0;
       }
     }));
   }
+  output.extend(quote!(return 2;));
 
   let with_scope = if generator_state.needs_scope {
     with_scope(generator_state)
@@ -141,8 +137,9 @@ pub(crate) fn generate_dispatch_async(
   };
 
   Ok(
-    gs_quote!(generator_state(info, slow_function, slow_function_metrics) => {
-      extern "C" fn #slow_function(#info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    gs_quote!(generator_state(info, slow_function, slow_function_metrics, opctx) => {
+      #[inline(always)]
+      fn slow_function_impl(#info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         #with_scope
         #with_retval
         #with_args
@@ -152,8 +149,25 @@ pub(crate) fn generate_dispatch_async(
         #output
       }
 
+      extern "C" fn #slow_function(#info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(#info);
+      }
+
       extern "C" fn #slow_function_metrics(#info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::#slow_function(#info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+          &*info
+        });
+        let #opctx = unsafe {
+          &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data()).value()
+            as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(&#opctx, ::deno_core::_ops::OpMetricsEvent::Dispatched);
+        let res = Self::slow_function_impl(#info);
+        if res == 0 {
+          ::deno_core::_ops::dispatch_metrics_async(&#opctx, ::deno_core::_ops::OpMetricsEvent::Completed);
+        } else if res == 1 {
+          ::deno_core::_ops::dispatch_metrics_async(&#opctx, ::deno_core::_ops::OpMetricsEvent::Error);
+        }
       }
     }),
   )
diff --git a/ops/op2/dispatch_slow.rs b/ops/op2/dispatch_slow.rs
index d82c398..60a66d5 100644
--- a/ops/op2/dispatch_slow.rs
+++ b/ops/op2/dispatch_slow.rs
@@ -137,7 +137,8 @@ pub(crate) fn generate_dispatch_slow(
 
   Ok(
     gs_quote!(generator_state(opctx, info, slow_function, slow_function_metrics) => {
-      extern "C" fn #slow_function(#info: *const ::deno_core::v8::FunctionCallbackInfo) {
+      #[inline(always)]
+      fn slow_function_impl(#info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         #with_scope
         #with_retval
         #with_args
@@ -146,20 +147,30 @@ pub(crate) fn generate_dispatch_slow(
         #with_opstate
         #with_js_runtime_state
 
-        #output
+        #output;
+        return 0;
+      }
+
+      extern "C" fn #slow_function(#info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(#info);
       }
+
       extern "C" fn #slow_function_metrics(#info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
           &*info
         });
         let #opctx = unsafe {
-            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data()).value()
-                as *const ::deno_core::_ops::OpCtx)
+          &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data()).value()
+              as *const ::deno_core::_ops::OpCtx)
         };
 
         ::deno_core::_ops::dispatch_metrics_slow(&#opctx, ::deno_core::_ops::OpMetricsEvent::Dispatched);
-        Self::#slow_function(#info);
-        ::deno_core::_ops::dispatch_metrics_slow(&#opctx, ::deno_core::_ops::OpMetricsEvent::Completed);
+        let res = Self::slow_function_impl(#info);
+        if res == 0 {
+          ::deno_core::_ops::dispatch_metrics_slow(&#opctx, ::deno_core::_ops::OpMetricsEvent::Completed);
+        } else {
+          ::deno_core::_ops::dispatch_metrics_slow(&#opctx, ::deno_core::_ops::OpMetricsEvent::Error);
+        }
       }
     }),
   )
@@ -830,7 +841,7 @@ pub(crate) fn throw_exception(
       &err,
     );
     #scope.throw_exception(exception);
-    return;
+    return 1;
   })
 }
 
@@ -853,7 +864,7 @@ fn throw_type_error(
     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);
-    return;
+    return 1;
   }))
 }
 
@@ -874,7 +885,7 @@ fn throw_type_error_string(
     let msg = ::deno_core::v8::String::new(&mut #scope, &format!("{}", ::deno_core::anyhow::Error::from(#message))).unwrap();
     let exc = ::deno_core::v8::Exception::type_error(&mut #scope, msg);
     #scope.throw_exception(exc);
-    return;
+    return 1;
   }))
 }
 
@@ -894,6 +905,6 @@ fn throw_type_error_static_string(
     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);
-    return;
+    return 1;
   }))
 }


Test patch:
diff --git a/core/runtime/tests/ops.rs b/core/runtime/tests/ops.rs
index 20bc48e..e805166 100644
--- a/core/runtime/tests/ops.rs
+++ b/core/runtime/tests/ops.rs
@@ -8,6 +8,7 @@ use crate::runtime::tests::Mode;
 use crate::*;
 use anyhow::bail;
 use anyhow::Error;
+use futures::Future;
 use log::debug;
 use pretty_assertions::assert_eq;
 use std::cell::RefCell;
@@ -550,72 +551,100 @@ await op_void_async_deferred();
   res.unwrap();
 }
 
-#[tokio::test]
-pub async fn test_op_metrics() {
-  #[op2(async)]
-  pub async fn op_async() {
-    println!("op_async!");
-  }
+#[op2(async)]
+pub async fn op_async() {
+  println!("op_async!");
+}
 
-  #[op2(async)]
-  pub async fn op_async_yield() {
-    tokio::task::yield_now().await;
-    println!("op_async_yield!");
-  }
+#[op2(async)]
+#[allow(unreachable_code)]
+pub fn op_async_impl_future_error() -> Result<impl Future<Output = ()>, AnyError>
+{
+  bail!("dead");
+  Ok(async {})
+}
 
-  #[op2(async)]
-  pub async fn op_async_yield_error() -> Result<(), AnyError> {
-    tokio::task::yield_now().await;
-    println!("op_async_yield_error!");
-    bail!("dead");
-  }
+#[op2(async)]
+pub async fn op_async_yield() {
+  tokio::task::yield_now().await;
+  println!("op_async_yield!");
+}
 
-  #[op2(async)]
-  pub async fn op_async_error() -> Result<(), AnyError> {
-    println!("op_async_error!");
-    bail!("dead");
-  }
+#[op2(async)]
+pub async fn op_async_yield_error() -> Result<(), AnyError> {
+  tokio::task::yield_now().await;
+  println!("op_async_yield_error!");
+  bail!("dead");
+}
 
-  #[op2(async(deferred), fast)]
-  pub async fn op_async_deferred() {
-    println!("op_async_deferred!");
-  }
+#[op2(async)]
+pub async fn op_async_error() -> Result<(), AnyError> {
+  println!("op_async_error!");
+  bail!("dead");
+}
 
-  #[op2(async(lazy), fast)]
-  pub async fn op_async_lazy() {
-    println!("op_async_lazy!");
-  }
+#[op2(async(deferred), fast)]
+pub async fn op_async_deferred() {
+  println!("op_async_deferred!");
+}
 
-  #[op2(fast)]
-  pub fn op_sync() {
-    println!("op_sync!");
-  }
+#[op2(async(lazy), fast)]
+pub async fn op_async_lazy() {
+  println!("op_async_lazy!");
+}
 
-  deno_core::extension!(
-    test_ext,
-    ops = [
-      op_async,
-      op_async_error,
-      op_async_yield,
-      op_async_yield_error,
-      op_async_lazy,
-      op_async_deferred,
-      op_sync
-    ],
-  );
+#[op2(fast)]
+pub fn op_sync() {
+  println!("op_sync!");
+}
+
+#[op2(fast)]
+pub fn op_sync_error() -> Result<(), AnyError> {
+  bail!("Always fails");
+}
 
+#[op2(fast)]
+pub fn op_sync_arg_error(_: u32) {
+  panic!("Should never be called")
+}
+
+#[op2(async)]
+pub async fn op_async_arg_error(_: u32) {
+  panic!("Should never be called")
+}
+
+deno_core::extension!(
+  test_ext,
+  ops = [
+    op_async,
+    op_async_error,
+    op_async_yield,
+    op_async_yield_error,
+    op_async_lazy,
+    op_async_deferred,
+    op_async_impl_future_error,
+    op_sync,
+    op_sync_error,
+    op_sync_arg_error,
+    op_async_arg_error,
+  ],
+);
+
+#[tokio::test]
+pub async fn test_op_metrics() {
   let out = Rc::new(RefCell::new(vec![]));
 
   let out_clone = out.clone();
   let mut runtime = JsRuntime::new(RuntimeOptions {
     extensions: vec![test_ext::init_ops()],
-    op_metrics_fn: Some(Box::new(move |op| {
-      if !op.name.starts_with("op_async") && !op.name.starts_with("op_sync") {
+    op_metrics_factory_fn: Some(Box::new(move |_, _, op| {
+      let name = op.name;
+      if !name.starts_with("op_async") && !name.starts_with("op_sync") {
         return None;
       }
       let out_clone = out_clone.clone();
-      Some(Rc::new(move |op, metrics| {
-        let s = format!("{} {:?}", op.name, metrics);
+      Some(Rc::new(move |_, metrics| {
+        let s = format!("{} {:?}", name, metrics);
         println!("{s}");
         out_clone.borrow_mut().push(s);
       }))
@@ -627,15 +656,19 @@ pub async fn test_op_metrics() {
   .execute_script_static(
     "filename.js",
     r#"
-  const { op_sync, op_async, op_async_error, op_async_yield, op_async_yield_error, op_async_deferred, op_async_lazy } = Deno.core.ensureFastOps();
+    const { op_sync, op_sync_error, op_async, op_async_error, op_async_yield, op_async_yield_error, op_async_deferred, op_async_lazy, op_async_impl_future_error, op_sync_arg_error, op_async_arg_error } = Deno.core.ensureFastOps();
     async function go() {
       op_sync();
+      try { op_sync_error(); } catch {}
       await op_async();
       try { await op_async_error() } catch {}
       await op_async_yield();
       try { await op_async_yield_error() } catch {}
       await op_async_deferred();
       await op_async_lazy();
+      try { await op_async_impl_future_error() } catch {}
+      try { op_sync_arg_error() } catch {}
+      try { await op_async_arg_error() } catch {}
     }
 
     go()
@@ -652,6 +685,8 @@ pub async fn test_op_metrics() {
     out,
     r#"op_sync Dispatched
 op_sync Completed
+op_sync_error Dispatched
+op_sync_error Error
 op_async Dispatched
 op_async Completed
 op_async_error Dispatched
@@ -663,6 +698,65 @@ op_async_yield_error ErrorAsync
 op_async_deferred Dispatched
 op_async_deferred CompletedAsync
 op_async_lazy Dispatched
-op_async_lazy CompletedAsync"#
+op_async_lazy CompletedAsync
+op_async_impl_future_error Dispatched
+op_async_impl_future_error Error
+op_sync_arg_error Dispatched
+op_sync_arg_error Error
+op_async_arg_error Dispatched
+op_async_arg_error Error"#
+  );
+}
+
+#[tokio::test]
+pub async fn test_op_metrics_summary_tracker() {
+  let tracker = Rc::new(OpMetricsSummaryTracker::default());
+  // We want to limit the tracker to just the ops we care about
+  let op_enabled = |op: &OpDecl| {
+    op.name.starts_with("op_async") || op.name.starts_with("op_sync")
+  };
+  let mut runtime = JsRuntime::new(RuntimeOptions {
+    extensions: vec![test_ext::init_ops()],
+    op_metrics_factory_fn: Some(
+      tracker.clone().op_metrics_factory_fn(op_enabled),
+    ),
+    ..Default::default()
+  });
+
+  let promise = runtime
+  .execute_script_static(
+    "filename.js",
+    r#"
+    const { op_sync, op_sync_error, op_async, op_async_error, op_async_yield, op_async_yield_error, op_async_deferred, op_async_lazy, op_async_impl_future_error, op_sync_arg_error, op_async_arg_error } = Deno.core.ensureFastOps();
+    async function go() {
+      op_sync();
+      try { op_sync_error(); } catch {}
+      await op_async();
+      try { await op_async_error() } catch {}
+      await op_async_yield();
+      try { await op_async_yield_error() } catch {}
+      await op_async_deferred();
+      await op_async_lazy();
+      try { await op_async_impl_future_error() } catch {}
+      try { op_sync_arg_error() } catch {}
+      try { await op_async_arg_error() } catch {}
+    }
+
+    go()
+    "#,
+  )
+  .unwrap();
+  runtime
+    .resolve_value(promise)
+    .await
+    .expect("Failed to await promise");
+  drop(runtime);
+  assert_eq!(
+    tracker.aggregate(),
+    OpMetricsSummary {
+      ops_completed_async: 8,
+      ops_dispatched_async: 8,
+      ops_dispatched_sync: 3
+    }
   );
 }
diff --git a/ops/op2/test_cases/async/async_arg_return.out b/ops/op2/test_cases/async/async_arg_return.out
index 1b29679..f6bb579 100644
--- a/ops/op2/test_cases/async/async_arg_return.out
+++ b/ops/op2/test_cases/async/async_arg_return.out
@@ -22,8 +22,8 @@ impl op_async {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -34,12 +34,6 @@ impl op_async {
             &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let Some(arg0) = ::deno_core::_ops::to_i32_option(&arg0) else {
@@ -52,7 +46,7 @@ impl op_async {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             Self::call(arg0)
@@ -67,11 +61,38 @@ impl op_async {
             result,
             |scope, result| { Ok(::deno_core::_ops::RustToV8::to_v8(result, scope)) },
         ) {
-            ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+            ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+            return 0;
         }
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub async fn call(x: i32) -> i32 {
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 8064c8b..5ba8527 100644
--- a/ops/op2/test_cases/async/async_arg_return_result.out
+++ b/ops/op2/test_cases/async/async_arg_return_result.out
@@ -22,8 +22,8 @@ impl op_async {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -34,12 +34,6 @@ impl op_async {
             &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let Some(arg0) = ::deno_core::_ops::to_i32_option(&arg0) else {
@@ -52,7 +46,7 @@ impl op_async {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             Self::call(arg0)
@@ -72,6 +66,9 @@ impl op_async {
                     ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
                 }
                 Err(err) => {
+                    let mut scope = unsafe {
+                        ::deno_core::v8::CallbackScope::new(&*info)
+                    };
                     let err = err.into();
                     let exception = ::deno_core::error::to_v8_error(
                         &mut scope,
@@ -79,13 +76,40 @@ impl op_async {
                         &err,
                     );
                     scope.throw_exception(exception);
-                    return;
+                    return 1;
                 }
             };
+            return 0;
         }
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub async fn call(x: i32) -> std::io::Result<i32> {
diff --git a/ops/op2/test_cases/async/async_deferred.out b/ops/op2/test_cases/async/async_deferred.out
index 10a1611..3817449 100644
--- a/ops/op2/test_cases/async/async_deferred.out
+++ b/ops/op2/test_cases/async/async_deferred.out
@@ -85,8 +85,8 @@ impl op_async_deferred {
             |scope, result| { Ok(::deno_core::_ops::RustToV8::to_v8(result, scope)) },
         );
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -97,12 +97,6 @@ impl op_async_deferred {
             &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = { Self::call() };
         let promise_id = ::deno_core::_ops::to_i32_option(&args.get(0))
             .unwrap_or_default();
@@ -114,9 +108,35 @@ impl op_async_deferred {
             result,
             |scope, result| { Ok(::deno_core::_ops::RustToV8::to_v8(result, scope)) },
         );
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub async fn call() -> std::io::Result<i32> {
diff --git a/ops/op2/test_cases/async/async_jsbuffer.out b/ops/op2/test_cases/async/async_jsbuffer.out
index 4d84e6e..c3a43e5 100644
--- a/ops/op2/test_cases/async/async_jsbuffer.out
+++ b/ops/op2/test_cases/async/async_jsbuffer.out
@@ -22,7 +22,8 @@ impl op_async_v8_buffer {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -34,12 +35,6 @@ impl op_async_v8_buffer {
             &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let mut arg0_temp;
@@ -57,7 +52,7 @@ impl op_async_v8_buffer {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg0 = ::deno_core::serde_v8::JsBuffer::from_parts(arg0_temp);
@@ -88,13 +83,40 @@ impl op_async_v8_buffer {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
-            }
+            };
+            return 0;
         }
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub async fn call(buf: JsBuffer) -> JsBuffer {
diff --git a/ops/op2/test_cases/async/async_lazy.out b/ops/op2/test_cases/async/async_lazy.out
index c7c6056..8122364 100644
--- a/ops/op2/test_cases/async/async_lazy.out
+++ b/ops/op2/test_cases/async/async_lazy.out
@@ -85,8 +85,8 @@ impl op_async_lazy {
             |scope, result| { Ok(::deno_core::_ops::RustToV8::to_v8(result, scope)) },
         );
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -97,12 +97,6 @@ impl op_async_lazy {
             &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = { Self::call() };
         let promise_id = ::deno_core::_ops::to_i32_option(&args.get(0))
             .unwrap_or_default();
@@ -114,9 +108,35 @@ impl op_async_lazy {
             result,
             |scope, result| { Ok(::deno_core::_ops::RustToV8::to_v8(result, scope)) },
         );
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub async fn call() -> std::io::Result<i32> {
diff --git a/ops/op2/test_cases/async/async_opstate.out b/ops/op2/test_cases/async/async_opstate.out
index 28f729f..c5a5d92 100644
--- a/ops/op2/test_cases/async/async_opstate.out
+++ b/ops/op2/test_cases/async/async_opstate.out
@@ -22,8 +22,8 @@ impl op_async_opstate {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -35,12 +35,6 @@ impl op_async_opstate {
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
         let opstate = &opctx.state;
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = {
             let arg0 = opstate.clone();
             Self::call(arg0)
@@ -60,6 +54,9 @@ impl op_async_opstate {
                     ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
                 }
                 Err(err) => {
+                    let mut scope = unsafe {
+                        ::deno_core::v8::CallbackScope::new(&*info)
+                    };
                     let err = err.into();
                     let exception = ::deno_core::error::to_v8_error(
                         &mut scope,
@@ -67,13 +64,40 @@ impl op_async_opstate {
                         &err,
                     );
                     scope.throw_exception(exception);
-                    return;
+                    return 1;
                 }
             };
+            return 0;
         }
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub async fn call(state: Rc<RefCell<OpState>>) -> std::io::Result<i32> {
diff --git a/ops/op2/test_cases/async/async_result.out b/ops/op2/test_cases/async/async_result.out
index 0aad08a..dbb16e1 100644
--- a/ops/op2/test_cases/async/async_result.out
+++ b/ops/op2/test_cases/async/async_result.out
@@ -22,8 +22,8 @@ impl op_async {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -34,12 +34,6 @@ impl op_async {
             &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = { Self::call() };
         let promise_id = ::deno_core::_ops::to_i32_option(&args.get(0))
             .unwrap_or_default();
@@ -56,6 +50,9 @@ impl op_async {
                     ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
                 }
                 Err(err) => {
+                    let mut scope = unsafe {
+                        ::deno_core::v8::CallbackScope::new(&*info)
+                    };
                     let err = err.into();
                     let exception = ::deno_core::error::to_v8_error(
                         &mut scope,
@@ -63,13 +60,40 @@ impl op_async {
                         &err,
                     );
                     scope.throw_exception(exception);
-                    return;
+                    return 1;
                 }
             };
+            return 0;
         }
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub async fn call() -> std::io::Result<i32> {
diff --git a/ops/op2/test_cases/async/async_result_impl.out b/ops/op2/test_cases/async/async_result_impl.out
index 26b90a8..0c3baaa 100644
--- a/ops/op2/test_cases/async/async_result_impl.out
+++ b/ops/op2/test_cases/async/async_result_impl.out
@@ -22,8 +22,8 @@ impl op_async_result_impl {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -34,12 +34,6 @@ impl op_async_result_impl {
             &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let Some(arg0) = ::deno_core::_ops::to_i32_option(&arg0) else {
@@ -52,7 +46,7 @@ impl op_async_result_impl {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             Self::call(arg0)
@@ -60,12 +54,7 @@ impl op_async_result_impl {
         let result = match result {
             Ok(result) => result,
             Err(err) => {
-                if opctx.metrics_enabled() {
-                    ::deno_core::_ops::dispatch_metrics_async(
-                        &opctx,
-                        ::deno_core::_ops::OpMetricsEvent::Error,
-                    );
-                }
+                let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
                 let err = err.into();
                 let exception = ::deno_core::error::to_v8_error(
                     &mut scope,
@@ -73,7 +62,7 @@ impl op_async_result_impl {
                     &err,
                 );
                 scope.throw_exception(exception);
-                return;
+                return 1;
             }
         };
         let promise_id = ::deno_core::_ops::to_i32_option(&args.get(0))
@@ -91,6 +80,9 @@ impl op_async_result_impl {
                     ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
                 }
                 Err(err) => {
+                    let mut scope = unsafe {
+                        ::deno_core::v8::CallbackScope::new(&*info)
+                    };
                     let err = err.into();
                     let exception = ::deno_core::error::to_v8_error(
                         &mut scope,
@@ -98,13 +90,40 @@ impl op_async_result_impl {
                         &err,
                     );
                     scope.throw_exception(exception);
-                    return;
+                    return 1;
                 }
             };
+            return 0;
         }
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call(x: i32) -> Result<impl Future<Output = std::io::Result<i32>>, AnyError> {
diff --git a/ops/op2/test_cases/async/async_result_smi.out b/ops/op2/test_cases/async/async_result_smi.out
index 54de6a3..e4bdde0 100644
--- a/ops/op2/test_cases/async/async_result_smi.out
+++ b/ops/op2/test_cases/async/async_result_smi.out
@@ -22,8 +22,8 @@ impl op_async {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -34,12 +34,6 @@ impl op_async {
             &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let Some(arg0) = ::deno_core::_ops::to_i32_option(&arg0) else {
@@ -52,7 +46,7 @@ impl op_async {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             Self::call(arg0)
@@ -88,6 +82,9 @@ impl op_async {
                     )
                 }
                 Err(err) => {
+                    let mut scope = unsafe {
+                        ::deno_core::v8::CallbackScope::new(&*info)
+                    };
                     let err = err.into();
                     let exception = ::deno_core::error::to_v8_error(
                         &mut scope,
@@ -95,13 +92,40 @@ impl op_async {
                         &err,
                     );
                     scope.throw_exception(exception);
-                    return;
+                    return 1;
                 }
             };
+            return 0;
         }
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub async fn call(rid: ResourceId) -> std::io::Result<ResourceId> {
diff --git a/ops/op2/test_cases/async/async_v8_global.out b/ops/op2/test_cases/async/async_v8_global.out
index a630a94..f0152b4 100644
--- a/ops/op2/test_cases/async/async_v8_global.out
+++ b/ops/op2/test_cases/async/async_v8_global.out
@@ -22,7 +22,8 @@ impl op_async_v8_global {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -34,12 +35,6 @@ impl op_async_v8_global {
             &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let Ok(mut arg0) = ::deno_core::_ops::v8_try_convert::<
@@ -54,7 +49,7 @@ impl op_async_v8_global {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = ::deno_core::v8::Global::new(&mut scope, arg0);
             Self::call(arg0)
@@ -69,11 +64,38 @@ impl op_async_v8_global {
             result,
             |scope, result| { Ok(::deno_core::_ops::RustToV8::to_v8(result, scope)) },
         ) {
-            ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+            ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+            return 0;
         }
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub async fn call(_s: v8::Global<v8::String>) {}
diff --git a/ops/op2/test_cases/async/async_void.out b/ops/op2/test_cases/async/async_void.out
index fabef0e..d3d87d8 100644
--- a/ops/op2/test_cases/async/async_void.out
+++ b/ops/op2/test_cases/async/async_void.out
@@ -22,8 +22,8 @@ impl op_async {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -34,12 +34,6 @@ impl op_async {
             &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
                 .value() as *const ::deno_core::_ops::OpCtx)
         };
-        if opctx.metrics_enabled() {
-            ::deno_core::_ops::dispatch_metrics_async(
-                &opctx,
-                ::deno_core::_ops::OpMetricsEvent::Dispatched,
-            );
-        }
         let result = { Self::call() };
         let promise_id = ::deno_core::_ops::to_i32_option(&args.get(0))
             .unwrap_or_default();
@@ -51,11 +45,38 @@ impl op_async {
             result,
             |scope, result| { Ok(::deno_core::_ops::RustToV8::to_v8(result, scope)) },
         ) {
-            ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+            ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+            return 0;
         }
+        return 2;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
-        Self::v8_fn_ptr(info)
+        let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(::deno_core::v8::Local::<::deno_core::v8::External>::cast(args.data())
+                .value() as *const ::deno_core::_ops::OpCtx)
+        };
+        ::deno_core::_ops::dispatch_metrics_async(
+            &opctx,
+            ::deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else if res == 1 {
+            ::deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub async fn call() {}
diff --git a/ops/op2/test_cases/sync/add.out b/ops/op2/test_cases/sync/add.out
index dd366c3..d4556e3 100644
--- a/ops/op2/test_cases/sync/add.out
+++ b/ops/op2/test_cases/sync/add.out
@@ -76,7 +76,8 @@ impl op_add {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -95,7 +96,7 @@ impl op_add {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             let arg1 = args.get(1usize as i32);
@@ -109,12 +110,16 @@ impl op_add {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1 as _;
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -128,11 +133,18 @@ impl op_add {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(a: u32, b: u32) -> u32 {
diff --git a/ops/op2/test_cases/sync/add_options.out b/ops/op2/test_cases/sync/add_options.out
index 0522d13..6e78060 100644
--- a/ops/op2/test_cases/sync/add_options.out
+++ b/ops/op2/test_cases/sync/add_options.out
@@ -22,7 +22,8 @@ impl op_test_add_option {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -41,7 +42,7 @@ impl op_test_add_option {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             let arg1 = args.get(1usize as i32);
@@ -60,14 +61,18 @@ impl op_test_add_option {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 };
                 let arg1 = arg1 as _;
                 Some(arg1)
             };
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -81,11 +86,18 @@ impl op_test_add_option {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call(a: u32, b: Option<u32>) -> u32 {
diff --git a/ops/op2/test_cases/sync/bigint.out b/ops/op2/test_cases/sync/bigint.out
index 32f8c54..076327d 100644
--- a/ops/op2/test_cases/sync/bigint.out
+++ b/ops/op2/test_cases/sync/bigint.out
@@ -66,13 +66,18 @@ impl op_bigint {
         let result = { Self::call() };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
         let result = { Self::call() };
-        rv.set(::deno_core::_ops::RustToV8::to_v8(result, &mut scope))
+        rv.set(::deno_core::_ops::RustToV8::to_v8(result, &mut scope));
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -86,11 +91,18 @@ impl op_bigint {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call() -> u64 {
diff --git a/ops/op2/test_cases/sync/bool.out b/ops/op2/test_cases/sync/bool.out
index fde6a96..43641f6 100644
--- a/ops/op2/test_cases/sync/bool.out
+++ b/ops/op2/test_cases/sync/bool.out
@@ -73,7 +73,8 @@ impl op_bool {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -85,7 +86,11 @@ impl op_bool {
             let arg0 = arg0.is_true();
             Self::call(arg0)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -99,11 +104,18 @@ impl op_bool {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call(arg: bool) -> bool {
diff --git a/ops/op2/test_cases/sync/bool_result.out b/ops/op2/test_cases/sync/bool_result.out
index ba97ece..033d4fa 100644
--- a/ops/op2/test_cases/sync/bool_result.out
+++ b/ops/op2/test_cases/sync/bool_result.out
@@ -91,7 +91,8 @@ impl op_bool {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -111,7 +112,7 @@ impl op_bool {
                 &err,
             );
             scope.throw_exception(exception);
-            return;
+            return 1;
         }
         let result = {
             let arg0 = args.get(0usize as i32);
@@ -129,9 +130,13 @@ impl op_bool {
                     &err,
                 );
                 scope.throw_exception(exception);
-                return;
+                return 1;
             }
         };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -145,11 +150,18 @@ impl op_bool {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call(arg: bool) -> Result<bool, AnyError> {
diff --git a/ops/op2/test_cases/sync/buffers.out b/ops/op2/test_cases/sync/buffers.out
index f6bc6fe..b7a1649 100644
--- a/ops/op2/test_cases/sync/buffers.out
+++ b/ops/op2/test_cases/sync/buffers.out
@@ -127,7 +127,8 @@ impl op_buffers {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -151,7 +152,7 @@ impl op_buffers {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg0 = arg0_temp.as_ref();
@@ -171,7 +172,7 @@ impl op_buffers {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg1 = arg1_temp.as_mut();
@@ -191,7 +192,7 @@ impl op_buffers {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg2 = if arg2_temp.len() == 0 {
@@ -215,7 +216,7 @@ impl op_buffers {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg3 = if arg3_temp.len() == 0 {
@@ -225,7 +226,11 @@ impl op_buffers {
             };
             Self::call(arg0, arg1, arg2, arg3)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -239,11 +244,18 @@ impl op_buffers {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_a: &[u8], _b: &mut [u8], _c: *const u8, _d: *mut u8) {}
@@ -378,7 +390,8 @@ impl op_buffers_32 {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -402,7 +415,7 @@ impl op_buffers_32 {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg0 = arg0_temp.as_ref();
@@ -422,7 +435,7 @@ impl op_buffers_32 {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg1 = arg1_temp.as_mut();
@@ -442,7 +455,7 @@ impl op_buffers_32 {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg2 = if arg2_temp.len() == 0 {
@@ -466,7 +479,7 @@ impl op_buffers_32 {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg3 = if arg3_temp.len() == 0 {
@@ -476,7 +489,11 @@ impl op_buffers_32 {
             };
             Self::call(arg0, arg1, arg2, arg3)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -490,11 +507,18 @@ impl op_buffers_32 {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_a: &[u32], _b: &mut [u32], _c: *const u32, _d: *mut u32) {}
@@ -524,7 +548,8 @@ impl op_buffers_option {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -554,7 +579,7 @@ impl op_buffers_option {
                             msg,
                         );
                         scope.throw_exception(exc);
-                        return;
+                        return 1;
                     }
                 };
                 let arg0 = arg0_temp.as_ref();
@@ -582,7 +607,7 @@ impl op_buffers_option {
                             msg,
                         );
                         scope.throw_exception(exc);
-                        return;
+                        return 1;
                     }
                 };
                 let arg1 = ::deno_core::serde_v8::JsBuffer::from_parts(arg1_temp);
@@ -590,7 +615,11 @@ impl op_buffers_option {
             };
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -604,11 +633,18 @@ impl op_buffers_option {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_a: Option<&[u8]>, _b: Option<JsBuffer>) {}
diff --git a/ops/op2/test_cases/sync/buffers_copy.out b/ops/op2/test_cases/sync/buffers_copy.out
index 90fd9d6..ca42ac6 100644
--- a/ops/op2/test_cases/sync/buffers_copy.out
+++ b/ops/op2/test_cases/sync/buffers_copy.out
@@ -108,7 +108,8 @@ impl op_buffers {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -132,7 +133,7 @@ impl op_buffers {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg0 = arg0_temp.to_vec();
@@ -152,7 +153,7 @@ impl op_buffers {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg1 = arg1_temp.to_boxed_slice();
@@ -172,13 +173,17 @@ impl op_buffers {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg2 = arg2_temp.to_vec().into();
             Self::call(arg0, arg1, arg2)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -192,11 +197,18 @@ impl op_buffers {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_a: Vec<u8>, _b: Box<[u8]>, _c: bytes::Bytes) {}
@@ -301,7 +313,8 @@ impl op_buffers_32 {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -325,7 +338,7 @@ impl op_buffers_32 {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg0 = arg0_temp.to_vec();
@@ -345,13 +358,17 @@ impl op_buffers_32 {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg1 = arg1_temp.to_boxed_slice();
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -365,11 +382,18 @@ impl op_buffers_32 {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_a: Vec<u32>, _b: Box<[u32]>) {}
diff --git a/ops/op2/test_cases/sync/buffers_out.out b/ops/op2/test_cases/sync/buffers_out.out
index cc2c767..c515b29 100644
--- a/ops/op2/test_cases/sync/buffers_out.out
+++ b/ops/op2/test_cases/sync/buffers_out.out
@@ -22,7 +22,8 @@ impl op_buffers {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -47,7 +48,7 @@ impl op_buffers {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg0 = ::deno_core::serde_v8::JsBuffer::from_parts(arg0_temp);
@@ -63,9 +64,13 @@ impl op_buffers {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             }
-        }
+        };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -79,11 +84,18 @@ impl op_buffers {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(buffer: JsBuffer) -> JsBuffer {
@@ -115,7 +127,8 @@ impl op_buffers_option {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -146,7 +159,7 @@ impl op_buffers_option {
                             msg,
                         );
                         scope.throw_exception(exc);
-                        return;
+                        return 1;
                     }
                 };
                 let arg0 = ::deno_core::serde_v8::JsBuffer::from_parts(arg0_temp);
@@ -164,9 +177,13 @@ impl op_buffers_option {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             }
-        }
+        };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -180,11 +197,18 @@ impl op_buffers_option {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(buffer: Option<JsBuffer>) -> Option<JsBuffer> {
@@ -216,7 +240,8 @@ impl op_arraybuffers {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -241,7 +266,7 @@ impl op_arraybuffers {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             let arg0 = ::deno_core::serde_v8::JsBuffer::from_parts(arg0_temp);
@@ -255,7 +280,11 @@ impl op_arraybuffers {
                 >::from(result),
                 &mut scope,
             ),
-        )
+        );
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -269,11 +298,18 @@ impl op_arraybuffers {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(buffer: JsBuffer) -> JsBuffer {
diff --git a/ops/op2/test_cases/sync/cfg.out b/ops/op2/test_cases/sync/cfg.out
index 7770e98..7d8df32 100644
--- a/ops/op2/test_cases/sync/cfg.out
+++ b/ops/op2/test_cases/sync/cfg.out
@@ -68,12 +68,17 @@ impl op_maybe_windows {
         let result = { Self::call() };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
         let result = { Self::call() };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -87,11 +92,18 @@ impl op_maybe_windows {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     /// This is a doc comment.
@@ -169,12 +181,17 @@ impl op_maybe_windows {
         let result = { Self::call() };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
         let result = { Self::call() };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -188,11 +205,18 @@ impl op_maybe_windows {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     /// This is a doc comment.
diff --git a/ops/op2/test_cases/sync/clippy_allow.out b/ops/op2/test_cases/sync/clippy_allow.out
index 049946c..2dd1e4a 100644
--- a/ops/op2/test_cases/sync/clippy_allow.out
+++ b/ops/op2/test_cases/sync/clippy_allow.out
@@ -68,12 +68,17 @@ impl op_extra_annotation {
         let result = { Self::call() };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
         let result = { Self::call() };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -87,11 +92,18 @@ impl op_extra_annotation {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     /// This is a doc comment.
@@ -167,12 +179,17 @@ impl op_clippy_internal {
         let result = { Self::call() };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
         let result = { Self::call() };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -186,11 +203,18 @@ impl op_clippy_internal {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call() -> () {
diff --git a/ops/op2/test_cases/sync/doc_comment.out b/ops/op2/test_cases/sync/doc_comment.out
index 6393158..d6b1d2d 100644
--- a/ops/op2/test_cases/sync/doc_comment.out
+++ b/ops/op2/test_cases/sync/doc_comment.out
@@ -67,12 +67,17 @@ impl op_has_doc_comment {
         let result = { Self::call() };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
         let result = { Self::call() };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -86,11 +91,18 @@ impl op_has_doc_comment {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     /// This is a doc comment.
diff --git a/ops/op2/test_cases/sync/fast_alternative.out b/ops/op2/test_cases/sync/fast_alternative.out
index ecf35ac..637a04d 100644
--- a/ops/op2/test_cases/sync/fast_alternative.out
+++ b/ops/op2/test_cases/sync/fast_alternative.out
@@ -22,7 +22,8 @@ impl op_slow {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -41,7 +42,7 @@ impl op_slow {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1 as _;
             let arg2 = args.get(1usize as i32);
@@ -54,13 +55,17 @@ impl op_slow {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg2 = arg2 as _;
             let arg0 = &mut scope;
             Self::call(arg0, arg1, arg2)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -74,11 +79,18 @@ impl op_slow {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_scope: &v8::HandleScope, a: u32, b: u32) -> u32 {
@@ -164,7 +176,8 @@ impl op_fast {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -183,7 +196,7 @@ impl op_fast {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             let arg1 = args.get(1usize as i32);
@@ -197,12 +210,16 @@ impl op_fast {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1 as _;
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -216,11 +233,18 @@ impl op_fast {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(a: u32, b: u32) -> u32 {
@@ -252,7 +276,8 @@ impl<T: Trait> op_slow_generic<T> {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -271,7 +296,7 @@ impl<T: Trait> op_slow_generic<T> {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1 as _;
             let arg2 = args.get(1usize as i32);
@@ -284,13 +309,17 @@ impl<T: Trait> op_slow_generic<T> {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg2 = arg2 as _;
             let arg0 = &mut scope;
             Self::call(arg0, arg1, arg2)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -304,11 +333,18 @@ impl<T: Trait> op_slow_generic<T> {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_scope: &v8::HandleScope, a: u32, b: u32) -> u32 {
@@ -394,7 +430,8 @@ impl<T: Trait> op_fast_generic<T> {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -413,7 +450,7 @@ impl<T: Trait> op_fast_generic<T> {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             let arg1 = args.get(1usize as i32);
@@ -427,12 +464,16 @@ impl<T: Trait> op_fast_generic<T> {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1 as _;
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -446,11 +487,18 @@ impl<T: Trait> op_fast_generic<T> {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(a: u32, b: u32) -> u32 {
diff --git a/ops/op2/test_cases/sync/generics.out b/ops/op2/test_cases/sync/generics.out
index c4bb3de..361f371 100644
--- a/ops/op2/test_cases/sync/generics.out
+++ b/ops/op2/test_cases/sync/generics.out
@@ -66,12 +66,17 @@ impl<T: Trait> op_generics<T> {
         let result = { Self::call() };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
         let result = { Self::call() };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -85,11 +90,18 @@ impl<T: Trait> op_generics<T> {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call() {}
@@ -163,12 +175,17 @@ impl<T: Trait + 'static> op_generics_static<T> {
         let result = { Self::call() };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
         let result = { Self::call() };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -182,11 +199,18 @@ impl<T: Trait + 'static> op_generics_static<T> {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call() {}
@@ -260,12 +284,17 @@ impl<T: Trait + 'static> op_generics_static_where<T> {
         let result = { Self::call() };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
         let result = { Self::call() };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -279,11 +308,18 @@ impl<T: Trait + 'static> op_generics_static_where<T> {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call()
diff --git a/ops/op2/test_cases/sync/nofast.out b/ops/op2/test_cases/sync/nofast.out
index de475ab..384ba33 100644
--- a/ops/op2/test_cases/sync/nofast.out
+++ b/ops/op2/test_cases/sync/nofast.out
@@ -22,7 +22,8 @@ impl op_nofast {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -41,7 +42,7 @@ impl op_nofast {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             let arg1 = args.get(1usize as i32);
@@ -55,12 +56,16 @@ impl op_nofast {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1 as _;
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -74,11 +79,18 @@ impl op_nofast {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(a: u32, b: u32) -> u32 {
diff --git a/ops/op2/test_cases/sync/op_state_attr.out b/ops/op2/test_cases/sync/op_state_attr.out
index c7eb095..841ec00 100644
--- a/ops/op2/test_cases/sync/op_state_attr.out
+++ b/ops/op2/test_cases/sync/op_state_attr.out
@@ -82,7 +82,8 @@ impl op_state_rc {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -101,7 +102,11 @@ impl op_state_rc {
             let arg1 = arg1.try_borrow::<Something>();
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -115,11 +120,18 @@ impl op_state_rc {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_arg: &Something, _arg_opt: Option<&Something>) {}
diff --git a/ops/op2/test_cases/sync/op_state_rc.out b/ops/op2/test_cases/sync/op_state_rc.out
index d11c1a9..883f3cc 100644
--- a/ops/op2/test_cases/sync/op_state_rc.out
+++ b/ops/op2/test_cases/sync/op_state_rc.out
@@ -79,7 +79,8 @@ impl op_state_rc {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -95,7 +96,11 @@ impl op_state_rc {
             let arg0 = opstate.clone();
             Self::call(arg0)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -109,11 +114,18 @@ impl op_state_rc {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_state: Rc<RefCell<OpState>>) {}
diff --git a/ops/op2/test_cases/sync/op_state_ref.out b/ops/op2/test_cases/sync/op_state_ref.out
index c9bcbad..8c16072 100644
--- a/ops/op2/test_cases/sync/op_state_ref.out
+++ b/ops/op2/test_cases/sync/op_state_ref.out
@@ -79,7 +79,8 @@ impl op_state_ref {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -95,7 +96,11 @@ impl op_state_ref {
             let arg0 = &::std::cell::RefCell::borrow(&opstate);
             Self::call(arg0)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -109,11 +114,18 @@ impl op_state_ref {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_state: &OpState) {}
@@ -200,7 +212,8 @@ impl op_state_mut {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -216,7 +229,11 @@ impl op_state_mut {
             let arg0 = &mut ::std::cell::RefCell::borrow_mut(&opstate);
             Self::call(arg0)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -230,11 +247,18 @@ impl op_state_mut {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_state: &mut OpState) {}
@@ -264,7 +288,8 @@ impl op_state_and_v8 {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -291,13 +316,17 @@ impl op_state_and_v8 {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = ::deno_core::v8::Global::new(&mut scope, arg1);
             let arg0 = &mut ::std::cell::RefCell::borrow_mut(&opstate);
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -311,11 +340,18 @@ impl op_state_and_v8 {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_state: &mut OpState, _callback: v8::Global<v8::Function>) {}
@@ -411,7 +447,8 @@ impl op_state_and_v8_local {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -437,13 +474,17 @@ impl op_state_and_v8_local {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1;
             let arg0 = &mut ::std::cell::RefCell::borrow_mut(&opstate);
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -457,11 +498,18 @@ impl op_state_and_v8_local {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(_state: &mut OpState, _callback: v8::Local<v8::Function>) {}
diff --git a/ops/op2/test_cases/sync/result_external.out b/ops/op2/test_cases/sync/result_external.out
index 20f181c..f834837 100644
--- a/ops/op2/test_cases/sync/result_external.out
+++ b/ops/op2/test_cases/sync/result_external.out
@@ -86,7 +86,8 @@ impl op_external_with_result {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -110,7 +111,7 @@ impl op_external_with_result {
                 &err,
             );
             scope.throw_exception(exception);
-            return;
+            return 1;
         }
         let result = { Self::call() };
         match result {
@@ -126,9 +127,13 @@ impl op_external_with_result {
                     &err,
                 );
                 scope.throw_exception(exception);
-                return;
+                return 1;
             }
         };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -142,11 +147,18 @@ impl op_external_with_result {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call() -> Result<*mut std::ffi::c_void, AnyError> {
diff --git a/ops/op2/test_cases/sync/result_primitive.out b/ops/op2/test_cases/sync/result_primitive.out
index 76949b3..6b2e1d2 100644
--- a/ops/op2/test_cases/sync/result_primitive.out
+++ b/ops/op2/test_cases/sync/result_primitive.out
@@ -86,7 +86,8 @@ impl op_u32_with_result {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -109,7 +110,7 @@ impl op_u32_with_result {
                 &err,
             );
             scope.throw_exception(exception);
-            return;
+            return 1;
         }
         let result = { Self::call() };
         match result {
@@ -126,9 +127,13 @@ impl op_u32_with_result {
                     &err,
                 );
                 scope.throw_exception(exception);
-                return;
+                return 1;
             }
         };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -142,11 +147,18 @@ impl op_u32_with_result {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call() -> Result<u32, AnyError> {
diff --git a/ops/op2/test_cases/sync/result_scope.out b/ops/op2/test_cases/sync/result_scope.out
index a246ff7..cecbdcc 100644
--- a/ops/op2/test_cases/sync/result_scope.out
+++ b/ops/op2/test_cases/sync/result_scope.out
@@ -22,7 +22,8 @@ impl op_void_with_result {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -50,9 +51,13 @@ impl op_void_with_result {
                     &err,
                 );
                 scope.throw_exception(exception);
-                return;
+                return 1;
             }
         };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -66,11 +71,18 @@ impl op_void_with_result {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call(_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 442b282..845398c 100644
--- a/ops/op2/test_cases/sync/result_void.out
+++ b/ops/op2/test_cases/sync/result_void.out
@@ -86,7 +86,8 @@ impl op_void_with_result {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -109,7 +110,7 @@ impl op_void_with_result {
                 &err,
             );
             scope.throw_exception(exception);
-            return;
+            return 1;
         }
         let result = { Self::call() };
         match result {
@@ -126,9 +127,13 @@ impl op_void_with_result {
                     &err,
                 );
                 scope.throw_exception(exception);
-                return;
+                return 1;
             }
         };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -142,11 +147,18 @@ impl op_void_with_result {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call() -> Result<(), AnyError> {
diff --git a/ops/op2/test_cases/sync/serde_v8.out b/ops/op2/test_cases/sync/serde_v8.out
index b4739be..3036182 100644
--- a/ops/op2/test_cases/sync/serde_v8.out
+++ b/ops/op2/test_cases/sync/serde_v8.out
@@ -22,7 +22,8 @@ impl op_serde_v8 {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -42,7 +43,7 @@ impl op_serde_v8 {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             Self::call(arg0)
@@ -63,9 +64,13 @@ impl op_serde_v8 {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             }
-        }
+        };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -79,11 +84,18 @@ impl op_serde_v8 {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call(_input: Input) -> Output {
diff --git a/ops/op2/test_cases/sync/smi.out b/ops/op2/test_cases/sync/smi.out
index cac2bcc..8169263 100644
--- a/ops/op2/test_cases/sync/smi.out
+++ b/ops/op2/test_cases/sync/smi.out
@@ -89,7 +89,8 @@ impl op_smi_unsigned_return {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -108,7 +109,7 @@ impl op_smi_unsigned_return {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             let arg1 = args.get(1usize as i32);
@@ -122,7 +123,7 @@ impl op_smi_unsigned_return {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1 as _;
             let arg2 = args.get(2usize as i32);
@@ -136,7 +137,7 @@ impl op_smi_unsigned_return {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg2 = arg2 as _;
             let arg3 = args.get(3usize as i32);
@@ -150,7 +151,7 @@ impl op_smi_unsigned_return {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg3 = arg3 as _;
             Self::call(arg0, arg1, arg2, arg3)
@@ -161,7 +162,11 @@ impl op_smi_unsigned_return {
                 _,
             >::from(result),
             &mut rv,
-        )
+        );
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -175,11 +180,18 @@ impl op_smi_unsigned_return {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(a: Int16, b: Int32, c: Uint16, d: Uint32) -> Uint32 {
@@ -278,7 +290,8 @@ impl op_smi_signed_return {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -297,7 +310,7 @@ impl op_smi_signed_return {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0 as _;
             let arg1 = args.get(1usize as i32);
@@ -311,7 +324,7 @@ impl op_smi_signed_return {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1 as _;
             let arg2 = args.get(2usize as i32);
@@ -325,7 +338,7 @@ impl op_smi_signed_return {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg2 = arg2 as _;
             let arg3 = args.get(3usize as i32);
@@ -339,7 +352,7 @@ impl op_smi_signed_return {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg3 = arg3 as _;
             Self::call(arg0, arg1, arg2, arg3)
@@ -350,7 +363,11 @@ impl op_smi_signed_return {
                 _,
             >::from(result),
             &mut rv,
-        )
+        );
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -364,11 +381,18 @@ impl op_smi_signed_return {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(a: Int16, b: Int32, c: Uint16, d: Uint32) -> Int32 {
@@ -400,7 +424,8 @@ impl op_smi_option {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -424,14 +449,18 @@ impl op_smi_option {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 };
                 let arg0 = arg0 as _;
                 Some(arg0)
             };
             Self::call(arg0)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -445,11 +474,18 @@ impl op_smi_option {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(a: Option<Uint32>) -> Option<Uint32> {
diff --git a/ops/op2/test_cases/sync/string_cow.out b/ops/op2/test_cases/sync/string_cow.out
index 69c414f..ee98f1d 100644
--- a/ops/op2/test_cases/sync/string_cow.out
+++ b/ops/op2/test_cases/sync/string_cow.out
@@ -79,7 +79,8 @@ impl op_string_cow {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -99,7 +100,11 @@ impl op_string_cow {
             let arg0 = ::deno_core::_ops::to_str(&mut scope, &arg0, &mut arg0_temp);
             Self::call(arg0)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -113,11 +118,18 @@ impl op_string_cow {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(s: Cow<str>) -> u32 {
diff --git a/ops/op2/test_cases/sync/string_onebyte.out b/ops/op2/test_cases/sync/string_onebyte.out
index 7aedca6..f2f884b 100644
--- a/ops/op2/test_cases/sync/string_onebyte.out
+++ b/ops/op2/test_cases/sync/string_onebyte.out
@@ -73,7 +73,8 @@ impl op_string_onebyte {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -101,12 +102,16 @@ impl op_string_onebyte {
                         .unwrap();
                     let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                     scope.throw_exception(exc);
-                    return;
+                    return 1;
                 }
             };
             Self::call(arg0)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -120,11 +125,18 @@ impl op_string_onebyte {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(s: Cow<[u8]>) -> u32 {
diff --git a/ops/op2/test_cases/sync/string_option_return.out b/ops/op2/test_cases/sync/string_option_return.out
index 7693af7..b85a6d4 100644
--- a/ops/op2/test_cases/sync/string_option_return.out
+++ b/ops/op2/test_cases/sync/string_option_return.out
@@ -22,7 +22,8 @@ impl op_string_return {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -49,9 +50,13 @@ impl op_string_return {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             }
-        }
+        };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -65,11 +70,18 @@ impl op_string_return {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call(s: Option<String>) -> Option<String> {
diff --git a/ops/op2/test_cases/sync/string_owned.out b/ops/op2/test_cases/sync/string_owned.out
index 2e1c83f..42bad0f 100644
--- a/ops/op2/test_cases/sync/string_owned.out
+++ b/ops/op2/test_cases/sync/string_owned.out
@@ -73,7 +73,8 @@ impl op_string_owned {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -90,7 +91,11 @@ impl op_string_owned {
             let arg0 = ::deno_core::_ops::to_string(&mut scope, &arg0);
             Self::call(arg0)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -104,11 +109,18 @@ impl op_string_owned {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(s: String) -> u32 {
diff --git a/ops/op2/test_cases/sync/string_ref.out b/ops/op2/test_cases/sync/string_ref.out
index 7ef0535..c558dcb 100644
--- a/ops/op2/test_cases/sync/string_ref.out
+++ b/ops/op2/test_cases/sync/string_ref.out
@@ -79,7 +79,8 @@ impl op_string_owned {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -99,7 +100,11 @@ impl op_string_owned {
             let arg0 = &::deno_core::_ops::to_str(&mut scope, &arg0, &mut arg0_temp);
             Self::call(arg0)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -113,11 +118,18 @@ impl op_string_owned {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call(s: &str) -> u32 {
diff --git a/ops/op2/test_cases/sync/string_return.out b/ops/op2/test_cases/sync/string_return.out
index 6d5a727..6fe224f 100644
--- a/ops/op2/test_cases/sync/string_return.out
+++ b/ops/op2/test_cases/sync/string_return.out
@@ -22,7 +22,8 @@ impl op_string_return {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -38,9 +39,13 @@ impl op_string_return {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             }
-        }
+        };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -54,11 +59,18 @@ impl op_string_return {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call() -> String {
@@ -90,7 +102,8 @@ impl op_string_return_ref {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -106,9 +119,13 @@ impl op_string_return_ref {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             }
-        }
+        };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -122,11 +139,18 @@ impl op_string_return_ref {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call() -> &'static str {
@@ -158,7 +182,8 @@ impl op_string_return_cow {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -174,9 +199,13 @@ impl op_string_return_cow {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             }
-        }
+        };
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -190,11 +219,18 @@ impl op_string_return_cow {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call<'a>() -> Cow<'a, str> {
diff --git a/ops/op2/test_cases/sync/v8_global.out b/ops/op2/test_cases/sync/v8_global.out
index f1d325b..0be2910 100644
--- a/ops/op2/test_cases/sync/v8_global.out
+++ b/ops/op2/test_cases/sync/v8_global.out
@@ -22,7 +22,8 @@ impl op_global {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -44,12 +45,16 @@ impl op_global {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = ::deno_core::v8::Global::new(&mut scope, arg0);
             Self::call(arg0)
         };
-        rv.set(::deno_core::_ops::RustToV8::to_v8(result, &mut scope))
+        rv.set(::deno_core::_ops::RustToV8::to_v8(result, &mut scope));
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -63,11 +68,18 @@ impl op_global {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call(g: v8::Global<v8::String>) -> v8::Global<v8::String> {
diff --git a/ops/op2/test_cases/sync/v8_handlescope.out b/ops/op2/test_cases/sync/v8_handlescope.out
index e0d9988..8688815 100644
--- a/ops/op2/test_cases/sync/v8_handlescope.out
+++ b/ops/op2/test_cases/sync/v8_handlescope.out
@@ -22,7 +22,8 @@ impl op_handlescope {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut scope = unsafe { ::deno_core::v8::CallbackScope::new(&*info) };
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
@@ -43,13 +44,17 @@ impl op_handlescope {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1;
             let arg0 = &mut scope;
             Self::call(arg0, arg1)
         };
-        rv.set(::deno_core::_ops::RustToV8NoScope::to_v8(result))
+        rv.set(::deno_core::_ops::RustToV8NoScope::to_v8(result));
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -63,11 +68,18 @@ impl op_handlescope {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call<'a>(
diff --git a/ops/op2/test_cases/sync/v8_lifetime.out b/ops/op2/test_cases/sync/v8_lifetime.out
index 9b4fae0..820f141 100644
--- a/ops/op2/test_cases/sync/v8_lifetime.out
+++ b/ops/op2/test_cases/sync/v8_lifetime.out
@@ -22,7 +22,8 @@ impl op_v8_lifetime {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -43,12 +44,16 @@ impl op_v8_lifetime {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = arg0;
             Self::call(arg0)
         };
-        rv.set(::deno_core::_ops::RustToV8NoScope::to_v8(result))
+        rv.set(::deno_core::_ops::RustToV8NoScope::to_v8(result));
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -62,11 +67,18 @@ impl op_v8_lifetime {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call<'s>(_s: v8::Local<'s, v8::String>) -> v8::Local<'s, v8::String> {
diff --git a/ops/op2/test_cases/sync/v8_ref_option.out b/ops/op2/test_cases/sync/v8_ref_option.out
index 809e612..ffe9e02 100644
--- a/ops/op2/test_cases/sync/v8_ref_option.out
+++ b/ops/op2/test_cases/sync/v8_ref_option.out
@@ -96,7 +96,8 @@ impl op_v8_lifetime {
         };
         result as _
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -117,7 +118,7 @@ impl op_v8_lifetime {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = match &arg0 {
                 None => None,
@@ -136,7 +137,7 @@ impl op_v8_lifetime {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = match &arg1 {
                 None => None,
@@ -144,7 +145,11 @@ impl op_v8_lifetime {
             };
             Self::call(arg0, arg1)
         };
-        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
+        ::deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv);
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -158,11 +163,18 @@ impl op_v8_lifetime {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     pub fn call<'s>(_s: Option<&v8::String>, _s2: Option<&v8::String>) {}
diff --git a/ops/op2/test_cases/sync/v8_string.out b/ops/op2/test_cases/sync/v8_string.out
index 6e53fdc..6782b7d 100644
--- a/ops/op2/test_cases/sync/v8_string.out
+++ b/ops/op2/test_cases/sync/v8_string.out
@@ -22,7 +22,8 @@ impl op_v8_string {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
-    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+    #[inline(always)]
+    fn slow_function_impl(info: *const ::deno_core::v8::FunctionCallbackInfo) -> usize {
         let mut rv = ::deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
             &*info
         });
@@ -43,7 +44,7 @@ impl op_v8_string {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg0 = &arg0;
             let arg1 = args.get(1usize as i32);
@@ -59,12 +60,16 @@ impl op_v8_string {
                     .unwrap();
                 let exc = ::deno_core::v8::Exception::type_error(&mut scope, msg);
                 scope.throw_exception(exc);
-                return;
+                return 1;
             };
             let arg1 = arg1;
             Self::call(arg0, arg1)
         };
-        rv.set(::deno_core::_ops::RustToV8NoScope::to_v8(result))
+        rv.set(::deno_core::_ops::RustToV8NoScope::to_v8(result));
+        return 0;
+    }
+    extern "C" fn v8_fn_ptr(info: *const ::deno_core::v8::FunctionCallbackInfo) {
+        Self::slow_function_impl(info);
     }
     extern "C" fn v8_fn_ptr_metrics(info: *const ::deno_core::v8::FunctionCallbackInfo) {
         let args = ::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
@@ -78,11 +83,18 @@ impl op_v8_string {
             &opctx,
             ::deno_core::_ops::OpMetricsEvent::Dispatched,
         );
-        Self::v8_fn_ptr(info);
-        ::deno_core::_ops::dispatch_metrics_slow(
-            &opctx,
-            ::deno_core::_ops::OpMetricsEvent::Completed,
-        );
+        let res = Self::slow_function_impl(info);
+        if res == 0 {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Completed,
+            );
+        } else {
+            ::deno_core::_ops::dispatch_metrics_slow(
+                &opctx,
+                ::deno_core::_ops::OpMetricsEvent::Error,
+            );
+        }
     }
     #[inline(always)]
     fn call<'a>(