Repository: denoland/deno_core
Pull request #73 (2023-07-22 16:18:59)

feat(ops): op2 support for u64/i64 return values
We can support u64/i64 in the return value position, though due to an unknown bug in fastcalls (unsure if it is in rusty_v8 or deno_core), we cannot make them fast yet. In anticipation that this will be fixed at some point, we start using `FastCall::new_with_bigint`.

Using a 64-bit value requires `#[bigint]` to remind implementers that this is more than just a simple number and may have some performance implications.


Patch:
diff --git a/core/runtime/ops.rs b/core/runtime/ops.rs
index 9768562..4803fb8 100644
--- a/core/runtime/ops.rs
+++ b/core/runtime/ops.rs
@@ -609,6 +609,8 @@ mod tests {
       op_test_bool_result,
       op_test_float,
       op_test_float_result,
+      op_test_i64,
+      op_test_u64,
       op_test_string_owned,
       op_test_string_ref,
       op_test_string_cow,
@@ -979,6 +981,38 @@ mod tests {
     Ok(())
   }
 
+  #[op2(core)]
+  #[bigint]
+  pub fn op_test_u64(#[bigint] input: u64) -> u64 {
+    input
+  }
+
+  #[op2(core)]
+  #[bigint]
+  pub fn op_test_i64(#[bigint] input: i64) -> i64 {
+    input
+  }
+
+  #[tokio::test]
+  pub async fn test_op_64() -> Result<(), Box<dyn std::error::Error>> {
+    run_test2(
+      10,
+      "op_test_i64",
+      &format!("assert(op_test_i64({}n) == {}n)", i64::MAX, i64::MAX),
+    )?;
+    run_test2(
+      10,
+      "op_test_i64",
+      &format!("assert(op_test_i64({}n) == {}n)", i64::MIN, i64::MIN),
+    )?;
+    run_test2(
+      10,
+      "op_test_u64",
+      &format!("assert(op_test_u64({}n) == {}n)", u64::MAX, u64::MAX),
+    )?;
+    Ok(())
+  }
+
   #[op2(core, fast)]
   pub fn op_test_string_owned(#[string] s: String) -> u32 {
     s.len() as _
@@ -1338,9 +1372,9 @@ mod tests {
   #[op2(core, fast)]
   pub fn op_buffer_slice(
     #[buffer] input: &[u8],
-    inlen: usize,
+    #[bigint] inlen: usize,
     #[buffer] output: &mut [u8],
-    outlen: usize,
+    #[bigint] outlen: usize,
   ) {
     assert_eq!(inlen, input.len());
     assert_eq!(outlen, output.len());
diff --git a/ops/op2/README.md b/ops/op2/README.md
index dbff9e0..e5f4413 100644
--- a/ops/op2/README.md
+++ b/ops/op2/README.md
@@ -145,7 +145,7 @@ Uint32, Int32, Number, BigInt
 <td>
 
 ```rust
-i64
+#[bigint] i64
 ```
 
 </td><td>
@@ -159,7 +159,7 @@ Uint32, Int32, Number, BigInt
 <td>
 
 ```rust
-u64
+#[bigint] u64
 ```
 
 </td><td>
@@ -173,7 +173,7 @@ Uint32, Int32, Number, BigInt
 <td>
 
 ```rust
-isize
+#[bigint] isize
 ```
 
 </td><td>
@@ -187,7 +187,7 @@ Uint32, Int32, Number, BigInt
 <td>
 
 ```rust
-usize
+#[bigint] usize
 ```
 
 </td><td>
@@ -782,6 +782,62 @@ Uint32
 <tr>
 <td>
 
+```rust
+#[bigint] i64
+```
+
+</td><td>
+
+</td><td>
+BigInt
+</td><td>
+
+</td></tr>
+<tr>
+<td>
+
+```rust
+#[bigint] u64
+```
+
+</td><td>
+
+</td><td>
+BigInt
+</td><td>
+
+</td></tr>
+<tr>
+<td>
+
+```rust
+#[bigint] isize
+```
+
+</td><td>
+
+</td><td>
+BigInt
+</td><td>
+
+</td></tr>
+<tr>
+<td>
+
+```rust
+#[bigint] usize
+```
+
+</td><td>
+
+</td><td>
+BigInt
+</td><td>
+
+</td></tr>
+<tr>
+<td>
+
 ```rust
 f32
 ```
diff --git a/ops/op2/dispatch_fast.rs b/ops/op2/dispatch_fast.rs
index 681ecc8..0592664 100644
--- a/ops/op2/dispatch_fast.rs
+++ b/ops/op2/dispatch_fast.rs
@@ -258,7 +258,9 @@ pub fn generate_dispatch_fast(
   let fast_definition = quote! {
     use #deno_core::v8::fast_api::Type;
     use #deno_core::v8::fast_api::CType;
-    #deno_core::v8::fast_api::FastFunction::new(
+    // TODO(mmastrac): We're setting this up for future success but returning
+    // u64/i64 from fastcall functions does not work. Test again in the future.
+    #deno_core::v8::fast_api::FastFunction::new_with_bigint(
       &[ Type::V8Value, #( #input_types ),* ],
       #output_type,
       Self::#fast_function as *const ::std::ffi::c_void
@@ -473,10 +475,14 @@ fn map_retval_to_v8_fastcall_type(
     | Arg::Numeric(NumericArg::i16)
     | Arg::Numeric(NumericArg::i8) => V8FastCallType::I32,
     Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => {
-      V8FastCallType::U64
+      // TODO(mmastrac): In my testing, I was not able to get this working properly
+      // V8FastCallType::U64
+      return Ok(None);
     }
     Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
-      V8FastCallType::I64
+      // TODO(mmastrac): In my testing, I was not able to get this working properly
+      // V8FastCallType::I64
+      return Ok(None);
     }
     Arg::Numeric(NumericArg::f32) => V8FastCallType::F32,
     Arg::Numeric(NumericArg::f64) => V8FastCallType::F64,
diff --git a/ops/op2/dispatch_slow.rs b/ops/op2/dispatch_slow.rs
index c70bcb9..b559ad5 100644
--- a/ops/op2/dispatch_slow.rs
+++ b/ops/op2/dispatch_slow.rs
@@ -525,6 +525,16 @@ pub fn return_value_infallible(
     | Arg::Numeric(NumericArg::i32) => {
       quote!(#retval.set_int32(#result as i32);)
     }
+    Arg::Numeric(NumericArg::i64 | NumericArg::isize) => {
+      *needs_retval = true;
+      *needs_scope = true;
+      quote!(#retval.set(v8::BigInt::new_from_i64(&mut scope, #result as _).into());)
+    }
+    Arg::Numeric(NumericArg::u64 | NumericArg::usize) => {
+      *needs_retval = true;
+      *needs_scope = true;
+      quote!(#retval.set(v8::BigInt::new_from_u64(&mut #scope, #result as _).into());)
+    }
     Arg::Numeric(NumericArg::f32 | NumericArg::f64) => {
       quote!(#retval.set_double(#result as _);)
     }
diff --git a/ops/op2/signature.rs b/ops/op2/signature.rs
index 6871f40..1bfa790 100644
--- a/ops/op2/signature.rs
+++ b/ops/op2/signature.rs
@@ -165,47 +165,27 @@ pub enum Buffer {
 }
 
 impl Buffer {
-  fn is_valid_mode(&self, position: Position, mode: BufferMode) -> bool {
+  const fn valid_modes(
+    &self,
+    position: Position,
+  ) -> &'static [AttributeModifier] {
+    use AttributeModifier::Buffer as B;
+    use Buffer::*;
+    use BufferMode::*;
     match position {
       Position::Arg => match self {
-        Buffer::Bytes(..) => matches!(mode, BufferMode::Copy),
-        Buffer::JsBuffer(..) => matches!(
-          mode,
-          BufferMode::Copy | BufferMode::Detach | BufferMode::Default
-        ),
-        Buffer::V8Slice(..) => matches!(
-          mode,
-          BufferMode::Copy | BufferMode::Detach | BufferMode::Default
-        ),
-        Buffer::Vec(..) => matches!(mode, BufferMode::Copy),
-        Buffer::BoxSlice(..) => matches!(mode, BufferMode::Copy),
-        Buffer::Slice(..) => {
-          matches!(mode, BufferMode::Detach | BufferMode::Default)
-        }
-        Buffer::Ptr(..) => {
-          matches!(mode, BufferMode::Detach | BufferMode::Default)
-        }
+        Bytes(..) | Vec(..) | BoxSlice(..) => &[B(Copy)],
+        JsBuffer(..) | V8Slice(..) => &[B(Copy), B(Detach), B(Default)],
+        Slice(..) | Ptr(..) => &[B(Default)],
       },
       Position::RetVal => match self {
-        Buffer::Bytes(..) => matches!(mode, BufferMode::Default),
-        Buffer::JsBuffer(..) => matches!(mode, BufferMode::Default),
-        Buffer::V8Slice(..) => matches!(mode, BufferMode::Default),
-        Buffer::Vec(..) => matches!(mode, BufferMode::Default),
-        Buffer::BoxSlice(..) => matches!(mode, BufferMode::Default),
-        Buffer::Slice(..) => false,
-        Buffer::Ptr(..) => false,
+        Bytes(..) | JsBuffer(..) | V8Slice(..) | Vec(..) | BoxSlice(..) => {
+          &[B(Default)]
+        }
+        Slice(..) | Ptr(..) => &([]),
       },
     }
   }
-
-  fn update_mode(&mut self, new_mode: BufferMode) {
-    match self {
-      Buffer::Bytes(ref mut mode) => *mode = new_mode,
-      Buffer::JsBuffer(ref mut mode) => *mode = new_mode,
-      Buffer::V8Slice(ref mut mode) => *mode = new_mode,
-      _ => {}
-    }
-  }
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -319,6 +299,30 @@ pub enum ParsedType {
   TNumeric(NumericArg),
 }
 
+impl ParsedType {
+  /// Returns the valid attributes for this particular type, `None` if no attributes are valid and
+  /// `Some([])` if the type is not valid in this position.
+  fn required_attributes(
+    &self,
+    position: Position,
+  ) -> Option<&'static [AttributeModifier]> {
+    use ParsedType::*;
+    match self {
+      TNumeric(
+        NumericArg::u64
+        | NumericArg::i64
+        | NumericArg::usize
+        | NumericArg::isize,
+      ) => Some(&[AttributeModifier::Bigint]),
+      TBuffer(buffer) => Some(buffer.valid_modes(position)),
+      TSpecial(Special::CowStr | Special::RefStr | Special::String) => {
+        Some(&[AttributeModifier::String])
+      }
+      _ => None,
+    }
+  }
+}
+
 pub enum ParsedTypeContainer {
   CBare(ParsedType),
   COption(ParsedType),
@@ -329,6 +333,63 @@ pub enum ParsedTypeContainer {
   CV8Global(ParsedType),
 }
 
+impl ParsedTypeContainer {
+  /// Returns the valid attributes for this particular type, `None` if no attributes are valid and
+  /// `Some([])` if the type is not valid in this position.
+  pub fn required_attributes(
+    &self,
+    position: Position,
+  ) -> Option<&'static [AttributeModifier]> {
+    use ParsedTypeContainer::*;
+    match self {
+      CV8Local(_) | COptionV8Local(_) => None,
+      CV8Global(_) | COptionV8Global(_) => Some(&[AttributeModifier::Global]),
+      CBare(t) | COption(t) | CRcRefCell(t) => t.required_attributes(position),
+    }
+  }
+
+  fn validate_attributes(
+    &self,
+    position: Position,
+    attrs: Attributes,
+    tp: &impl ToTokens,
+  ) -> Result<(), ArgError> {
+    match self.required_attributes(position) {
+      None => match attrs.primary {
+        None => {}
+        Some(attr) => {
+          return Err(ArgError::InvalidAttributeType(
+            attr.name(),
+            stringify_token(tp),
+          ))
+        }
+      },
+      Some(attr) => {
+        if attr.is_empty() {
+          return Err(ArgError::NotAllowedInThisPosition(stringify_token(tp)));
+        }
+        match attrs.primary {
+          None => {
+            return Err(ArgError::MissingAttribute(
+              attr[0].name(),
+              stringify_token(tp),
+            ))
+          }
+          Some(primary) => {
+            if !attr.contains(&primary) {
+              return Err(ArgError::MissingAttribute(
+                attr[0].name(),
+                stringify_token(tp),
+              ));
+            }
+          }
+        }
+      }
+    };
+    Ok(())
+  }
+}
+
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum RetVal {
   /// An op that can never fail.
@@ -403,6 +464,22 @@ pub enum AttributeModifier {
   Buffer(BufferMode),
   /// #[global], for [`v8::Global`]s
   Global,
+  /// #[bigint], for u64/usize/i64/isize
+  Bigint,
+}
+
+impl AttributeModifier {
+  fn name(&self) -> &'static str {
+    match self {
+      AttributeModifier::Bigint => "bigint",
+      AttributeModifier::Buffer(_) => "buffer",
+      AttributeModifier::Smi => "smi",
+      AttributeModifier::Serde => "serde",
+      AttributeModifier::String => "string",
+      AttributeModifier::State => "state",
+      AttributeModifier::Global => "global",
+    }
+  }
 }
 
 #[derive(Error, Debug)]
@@ -455,30 +532,22 @@ pub enum ArgError {
   MissingReference(String),
   #[error("Invalid or deprecated #[serde] type '{0}': {1}")]
   InvalidSerdeType(String, &'static str),
-  #[error("Invalid #[string] type: {0}")]
-  InvalidStringType(String),
-  #[error("Invalid #[buffer] type: {0}")]
-  InvalidBufferType(String),
-  #[error("Invalid #[buffer] mode {0} for {1}")]
-  InvalidBufferMode(String, String),
+  #[error("Invalid #[{0}] for type: {1}")]
+  InvalidAttributeType(&'static str, String),
   #[error("Cannot use #[serde] for type: {0}")]
   InvalidSerdeAttributeType(String),
-  #[error("Invalid #[global] type: {0}")]
-  InvalidGlobalType(String),
   #[error("Invalid v8 type: {0}")]
   InvalidV8Type(String),
   #[error("Internal error: {0}")]
   InternalError(String),
-  #[error("Missing a #[string] attribute")]
-  MissingStringAttribute,
-  #[error("Missing a #[buffer] attribute")]
-  MissingBufferAttribute,
-  #[error("Missing a #[global] attribute")]
-  MissingGlobalAttribute,
+  #[error("Missing a #[{0}] attribute for type: {1}")]
+  MissingAttribute(&'static str, String),
   #[error("Invalid #[state] type '{0}'")]
   InvalidStateType(String),
   #[error("Argument attribute error")]
   AttributeError(#[from] AttributeError),
+  #[error("The type '{0}' is not allowed in this position")]
+  NotAllowedInThisPosition(String),
 }
 
 #[derive(Error, Debug)]
@@ -709,6 +778,7 @@ fn parse_attribute(
   let res = std::panic::catch_unwind(|| {
     use syn2 as syn;
     rules!(tokens => {
+      (#[bigint]) => Some(AttributeModifier::Bigint),
       (#[serde]) => Some(AttributeModifier::Serde),
       (#[smi]) => Some(AttributeModifier::Smi),
       (#[string]) => Some(AttributeModifier::String),
@@ -735,7 +805,14 @@ fn parse_numeric_type(tp: &Path) -> Result<NumericArg, ArgError> {
     }
   }
 
-  Err(ArgError::InvalidNumericType(stringify_token(tp)))
+  let res = std::panic::catch_unwind(|| {
+    rules!(tp.into_token_stream() => {
+      ( $( std :: ffi :: )? c_void ) => NumericArg::__VOID__,
+    })
+  })
+  .map_err(|_| ArgError::InvalidNumericType(stringify_token(tp)))?;
+
+  Ok(res)
 }
 
 /// Parse a raw type into a container + type, allowing us to simplify the typechecks elsewhere in
@@ -751,8 +828,13 @@ fn parse_type_path(
 
   use syn2 as syn;
 
+  let buffer_mode = || match attrs.primary {
+    Some(AttributeModifier::Buffer(mode)) => Ok(mode),
+    _ => Err(ArgError::MissingAttribute("buffer", stringify_token(tp))),
+  };
+
   let tokens = tp.clone().into_token_stream();
-  let mut res = if let Ok(numeric) = parse_numeric_type(&tp.path) {
+  let res = if let Ok(numeric) = parse_numeric_type(&tp.path) {
     CBare(TNumeric(numeric))
   } else {
     std::panic::catch_unwind(|| {
@@ -774,15 +856,14 @@ fn parse_type_path(
         Ok(CBare(TBuffer(Buffer::BoxSlice(parse_numeric_type(&ty)?))))
       }
       ( $( serde_v8 :: )? V8Slice ) => {
-        Ok(CBare(TBuffer(Buffer::V8Slice(BufferMode::Default))))
+        Ok(CBare(TBuffer(Buffer::V8Slice(buffer_mode()?))))
       }
       ( $( serde_v8 :: )? JsBuffer ) => {
-        Ok(CBare(TBuffer(Buffer::JsBuffer(BufferMode::Default))))
+        Ok(CBare(TBuffer(Buffer::JsBuffer(buffer_mode()?))))
       }
       ( $( bytes :: )? Bytes ) => {
-        Ok(CBare(TBuffer(Buffer::Bytes(BufferMode::Default))))
+        Ok(CBare(TBuffer(Buffer::Bytes(buffer_mode()?))))
       }
-      ( $( std :: ffi :: )? c_void ) => Ok(CBare(TNumeric(NumericArg::__VOID__))),
       ( OpState ) => Ok(CBare(TSpecial(Special::OpState))),
       ( v8 :: HandleScope $( < $_scope:lifetime >)? ) => Ok(CBare(TSpecial(Special::HandleScope))),
       ( v8 :: FastApiCallbackOptions ) => Ok(CBare(TSpecial(Special::FastApiCallbackOptions))),
@@ -824,48 +905,7 @@ fn parse_type_path(
     }
   }
 
-  match res {
-    CV8Global(_) => {
-      if attrs.primary != Some(AttributeModifier::Global) {
-        return Err(ArgError::MissingGlobalAttribute);
-      }
-    }
-    CBare(TSpecial(Special::RefStr | Special::CowStr | Special::String)) => {
-      if attrs.primary != Some(AttributeModifier::String) {
-        return Err(ArgError::MissingStringAttribute);
-      }
-    }
-    CBare(TBuffer(ref mut buffer)) => {
-      if let Some(AttributeModifier::Buffer(mode)) = attrs.primary {
-        if !buffer.is_valid_mode(position, mode) {
-          return Err(ArgError::InvalidBufferMode(
-            format!("{mode:?}"),
-            stringify_token(tp),
-          ));
-        }
-        buffer.update_mode(mode)
-      } else {
-        return Err(ArgError::MissingBufferAttribute);
-      }
-    }
-    CBare(_) => {
-      if attrs.primary == Some(AttributeModifier::String) {
-        return Err(ArgError::InvalidStringType(stringify_token(tp)));
-      }
-      if let Some(AttributeModifier::Buffer(_)) = attrs.primary {
-        return Err(ArgError::InvalidBufferType(stringify_token(tp)));
-      }
-      if attrs.primary == Some(AttributeModifier::Global) {
-        return Err(ArgError::InvalidGlobalType(stringify_token(tp)));
-      }
-    }
-    _ => {
-      // Ignore for other containers
-      if attrs.primary == Some(AttributeModifier::Global) {
-        return Err(ArgError::InvalidGlobalType(stringify_token(tp)));
-      }
-    }
-  }
+  res.validate_attributes(position, attrs, &tp)?;
 
   Ok(res)
 }
@@ -967,6 +1007,7 @@ pub(crate) fn parse_type(
       }
       AttributeModifier::String
       | AttributeModifier::Buffer(_)
+      | AttributeModifier::Bigint
       | AttributeModifier::Global => {
         // We handle this as part of the normal parsing process
       }
@@ -999,18 +1040,10 @@ pub(crate) fn parse_type(
                 Ok(Arg::External(External::Ptr(mut_type)))
               }
               numeric => {
-                if let Some(AttributeModifier::Buffer(mode)) = attrs.primary {
-                  let buffer = Buffer::Slice(mut_type, numeric);
-                  if !buffer.is_valid_mode(position, mode) {
-                    return Err(ArgError::InvalidBufferMode(
-                      format!("{mode:?}"),
-                      stringify_token(ty),
-                    ));
-                  }
-                  Ok(Arg::Buffer(buffer))
-                } else {
-                  Err(ArgError::InvalidBufferType(stringify_token(ty)))
-                }
+                let buffer = Buffer::Slice(mut_type, numeric);
+                let res = CBare(TBuffer(buffer));
+                res.validate_attributes(position, attrs, &of)?;
+                Ok(Arg::Buffer(buffer))
               }
             }
           } else {
@@ -1036,25 +1069,14 @@ pub(crate) fn parse_type(
         RefType::Ref
       };
       match &*of.elem {
-        Type::Path(of) => match parse_type_path(position, attrs, false, of)? {
-          CBare(TNumeric(numeric)) if numeric == NumericArg::__VOID__ => {
-            Ok(Arg::External(External::Ptr(mut_type)))
+        Type::Path(of) => match parse_numeric_type(&of.path)? {
+          NumericArg::__VOID__ => Ok(Arg::External(External::Ptr(mut_type))),
+          numeric => {
+            let buffer = Buffer::Ptr(mut_type, numeric);
+            let res = CBare(TBuffer(buffer));
+            res.validate_attributes(position, attrs, &of)?;
+            Ok(Arg::Buffer(buffer))
           }
-          CBare(TNumeric(numeric)) => {
-            if let Some(AttributeModifier::Buffer(mode)) = attrs.primary {
-              let buffer = Buffer::Ptr(mut_type, numeric);
-              if !buffer.is_valid_mode(position, mode) {
-                return Err(ArgError::InvalidBufferMode(
-                  format!("{mode:?}"),
-                  stringify_token(ty),
-                ));
-              }
-              Ok(Arg::Buffer(buffer))
-            } else {
-              Err(ArgError::InvalidBufferType(stringify_token(ty)))
-            }
-          }
-          _ => Err(ArgError::InvalidType(stringify_token(ty))),
         },
         _ => Err(ArgError::InvalidType(stringify_token(ty))),
       }
@@ -1214,7 +1236,7 @@ mod tests {
     (Ref(Mut, OpState)) -> Result(OptionNumeric(u32))
   );
   test!(
-    fn op_ffi_read_f64(state: &mut OpState, ptr: * mut c_void, offset: isize) -> Result <f64, AnyError>;
+    fn op_ffi_read_f64(state: &mut OpState, ptr: * mut c_void, #[bigint] offset: isize) -> Result <f64, AnyError>;
     (Ref(Mut, OpState), External(Ptr(Mut)), Numeric(isize)) -> Result(Numeric(f64))
   );
   test!(
@@ -1281,27 +1303,27 @@ mod tests {
 
   expect_fail!(
     op_with_bad_string1,
-    ArgError("s", MissingStringAttribute),
+    ArgError("s", MissingAttribute("string", "str")),
     fn f(s: &str) {}
   );
   expect_fail!(
     op_with_bad_string2,
-    ArgError("s", MissingStringAttribute),
+    ArgError("s", MissingAttribute("string", "String")),
     fn f(s: String) {}
   );
   expect_fail!(
     op_with_bad_string3,
-    ArgError("s", MissingStringAttribute),
+    ArgError("s", MissingAttribute("string", "Cow<str>")),
     fn f(s: Cow<str>) {}
   );
   expect_fail!(
     op_with_invalid_string,
-    ArgError("x", InvalidStringType("u32")),
+    ArgError("x", InvalidAttributeType("string", "u32")),
     fn f(#[string] x: u32) {}
   );
   expect_fail!(
     op_with_invalid_buffer,
-    ArgError("x", InvalidBufferType("u32")),
+    ArgError("x", InvalidAttributeType("buffer", "u32")),
     fn f(#[buffer] x: u32) {}
   );
   expect_fail!(
@@ -1317,12 +1339,12 @@ mod tests {
   );
   expect_fail!(
     op_with_missing_global,
-    ArgError("g", MissingGlobalAttribute),
+    ArgError("g", MissingAttribute("global", "v8::Global<v8::String>")),
     fn f(g: v8::Global<v8::String>) {}
   );
   expect_fail!(
     op_with_invalid_global,
-    ArgError("l", InvalidGlobalType("v8::Local<v8::String>")),
+    ArgError("l", InvalidAttributeType("global", "v8::Local<v8::String>")),
     fn f(#[global] l: v8::Local<v8::String>) {}
   );
 
diff --git a/ops/op2/valid_args.md b/ops/op2/valid_args.md
index 9e3e7a7..0131155 100644
--- a/ops/op2/valid_args.md
+++ b/ops/op2/valid_args.md
@@ -7,10 +7,10 @@
 | X         | u16                              | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
 | X         | i32                              | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
 | X         | u32                              | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | i64                              | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | u64                              | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | isize                            | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
-| X         | usize                            | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | #[bigint] i64                    | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | #[bigint] u64                    | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | #[bigint] isize                  | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
+| X         | #[bigint] usize                  | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
 | X         | f32                              | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
 | X         | f64                              | X        | Uint32, Int32, Number, BigInt                       |                                                                                                                                                                                                                |
 | X         | #[string] String                 | X        | String                                              | Fastcall available only if string is Latin-1. Will always create an allocated, UTF-8 copy of the String data.                                                                                                  |
diff --git a/ops/op2/valid_retvals.md b/ops/op2/valid_retvals.md
index f96c728..bdac9e5 100644
--- a/ops/op2/valid_retvals.md
+++ b/ops/op2/valid_retvals.md
@@ -1,24 +1,24 @@
-| Supported | Rust                             | Fastcall | V8                            | Notes |
-| --------- | -------------------------------- | -------- | ----------------------------- | ----- |
-| X         | bool                             | X        | Bool                          |       |
-| X         | i8                               | X        | Int32                         |       |
-| X         | u8                               | X        | Uint32                        |       |
-| X         | i16                              | X        | Int32                         |       |
-| X         | u16                              | X        | Uint32                        |       |
-| X         | i32                              | X        | Int32                         |       |
-| X         | u32                              | X        | Uint32                        |       |
-|           | i64                              | X        | Uint32, Int32, Number, BigInt |       |
-|           | u64                              | X        | Uint32, Int32, Number, BigInt |       |
-|           | isize                            | X        | Uint32, Int32, Number, BigInt |       |
-|           | usize                            | X        | Uint32, Int32, Number, BigInt |       |
-| X         | f32                              | X        | Number                        |       |
-| X         | f64                              | X        | Number                        |       |
-| X         | #[string] String                 |          | String                        |       |
-|           | #[string] &str                   |          | String                        |       |
-|           | #[string] Cow<str>               |          | String                        |       |
-| X         | v8::Local<v8::Value>             |          | any                           |       |
-| X         | v8::Local<v8::**V8**>            |          | **V8**                        |       |
-|           | #[global] v8::Global<v8::Value>  |          | any                           |       |
-|           | #[global] v8::Global<v8::**V8**> |          | **V8**                        |       |
-| X         | #[serde] SerdeType               |          | any                           |       |
-| X         | #[serde] (Tuple, Tuple)          |          | any                           |       |
+| Supported | Rust                             | Fastcall | V8     | Notes |
+| --------- | -------------------------------- | -------- | ------ | ----- |
+| X         | bool                             | X        | Bool   |       |
+| X         | i8                               | X        | Int32  |       |
+| X         | u8                               | X        | Uint32 |       |
+| X         | i16                              | X        | Int32  |       |
+| X         | u16                              | X        | Uint32 |       |
+| X         | i32                              | X        | Int32  |       |
+| X         | u32                              | X        | Uint32 |       |
+| X         | #[bigint] i64                    |          | BigInt |       |
+| X         | #[bigint] u64                    |          | BigInt |       |
+| X         | #[bigint] isize                  |          | BigInt |       |
+| X         | #[bigint] usize                  |          | BigInt |       |
+| X         | f32                              | X        | Number |       |
+| X         | f64                              | X        | Number |       |
+| X         | #[string] String                 |          | String |       |
+|           | #[string] &str                   |          | String |       |
+|           | #[string] Cow<str>               |          | String |       |
+| X         | v8::Local<v8::Value>             |          | any    |       |
+| X         | v8::Local<v8::**V8**>            |          | **V8** |       |
+|           | #[global] v8::Global<v8::Value>  |          | any    |       |
+|           | #[global] v8::Global<v8::**V8**> |          | **V8** |       |
+| X         | #[serde] SerdeType               |          | any    |       |
+| X         | #[serde] (Tuple, Tuple)          |          | any    |       |


Test patch:
diff --git a/ops/op2/test_cases/sync/add.out b/ops/op2/test_cases/sync/add.out
index d853684..a0e0e13 100644
--- a/ops/op2/test_cases/sync/add.out
+++ b/ops/op2/test_cases/sync/add.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_add {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::Uint32, Type::Uint32],
                 CType::Uint32,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/bool.out b/ops/op2/test_cases/sync/bool.out
index 255d85f..27fd718 100644
--- a/ops/op2/test_cases/sync/bool.out
+++ b/ops/op2/test_cases/sync/bool.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_bool {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::Bool],
                 CType::Bool,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/bool_result.out b/ops/op2/test_cases/sync/bool_result.out
index 7a4c2d2..2feae60 100644
--- a/ops/op2/test_cases/sync/bool_result.out
+++ b/ops/op2/test_cases/sync/bool_result.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_bool {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::Bool, Type::CallbackOptions],
                 CType::Bool,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/buffers.out b/ops/op2/test_cases/sync/buffers.out
index 154086a..24feb7a 100644
--- a/ops/op2/test_cases/sync/buffers.out
+++ b/ops/op2/test_cases/sync/buffers.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_buffers {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[
                     Type::V8Value,
                     Type::TypedArray(CType::Uint8),
diff --git a/ops/op2/test_cases/sync/buffers_copy.out b/ops/op2/test_cases/sync/buffers_copy.out
index d66c873..1f4e282 100644
--- a/ops/op2/test_cases/sync/buffers_copy.out
+++ b/ops/op2/test_cases/sync/buffers_copy.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_buffers {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[
                     Type::V8Value,
                     Type::TypedArray(CType::Uint8),
diff --git a/ops/op2/test_cases/sync/clippy_allow.out b/ops/op2/test_cases/sync/clippy_allow.out
index 977bcef..804ce31 100644
--- a/ops/op2/test_cases/sync/clippy_allow.out
+++ b/ops/op2/test_cases/sync/clippy_allow.out
@@ -16,7 +16,7 @@ impl deno_core::_ops::Op for op_extra_annotation {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value],
                 CType::Void,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/doc_comment.out b/ops/op2/test_cases/sync/doc_comment.out
index 0285eee..d4d982a 100644
--- a/ops/op2/test_cases/sync/doc_comment.out
+++ b/ops/op2/test_cases/sync/doc_comment.out
@@ -15,7 +15,7 @@ impl deno_core::_ops::Op for op_has_doc_comment {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value],
                 CType::Void,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/generics.out b/ops/op2/test_cases/sync/generics.out
index 0aa262f..6d769fe 100644
--- a/ops/op2/test_cases/sync/generics.out
+++ b/ops/op2/test_cases/sync/generics.out
@@ -14,7 +14,7 @@ impl<T: Trait> deno_core::_ops::Op for op_generics<T> {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value],
                 CType::Void,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/op_state_attr.out b/ops/op2/test_cases/sync/op_state_attr.out
index fafd7ba..921c052 100644
--- a/ops/op2/test_cases/sync/op_state_attr.out
+++ b/ops/op2/test_cases/sync/op_state_attr.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_state_rc {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::CallbackOptions],
                 CType::Void,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/op_state_rc.out b/ops/op2/test_cases/sync/op_state_rc.out
index f43656c..b8c70f4 100644
--- a/ops/op2/test_cases/sync/op_state_rc.out
+++ b/ops/op2/test_cases/sync/op_state_rc.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_state_rc {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::CallbackOptions],
                 CType::Void,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/op_state_ref.out b/ops/op2/test_cases/sync/op_state_ref.out
index 3581a63..f279542 100644
--- a/ops/op2/test_cases/sync/op_state_ref.out
+++ b/ops/op2/test_cases/sync/op_state_ref.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_state_rc {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::CallbackOptions],
                 CType::Void,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/result_primitive.out b/ops/op2/test_cases/sync/result_primitive.out
index f919839..078768f 100644
--- a/ops/op2/test_cases/sync/result_primitive.out
+++ b/ops/op2/test_cases/sync/result_primitive.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_u32_with_result {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::CallbackOptions],
                 CType::Uint32,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/result_void.out b/ops/op2/test_cases/sync/result_void.out
index a627f06..ca1f29e 100644
--- a/ops/op2/test_cases/sync/result_void.out
+++ b/ops/op2/test_cases/sync/result_void.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_void_with_result {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::CallbackOptions],
                 CType::Void,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/smi.out b/ops/op2/test_cases/sync/smi.out
index c0681e7..a61cfca 100644
--- a/ops/op2/test_cases/sync/smi.out
+++ b/ops/op2/test_cases/sync/smi.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_add {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::Int32, Type::Uint32],
                 CType::Uint32,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/string_cow.out b/ops/op2/test_cases/sync/string_cow.out
index cbf686f..8a8e791 100644
--- a/ops/op2/test_cases/sync/string_cow.out
+++ b/ops/op2/test_cases/sync/string_cow.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_string_cow {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::SeqOneByteString],
                 CType::Uint32,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/string_owned.out b/ops/op2/test_cases/sync/string_owned.out
index 61edf72..4094398 100644
--- a/ops/op2/test_cases/sync/string_owned.out
+++ b/ops/op2/test_cases/sync/string_owned.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_string_owned {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::SeqOneByteString],
                 CType::Uint32,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/string_ref.out b/ops/op2/test_cases/sync/string_ref.out
index 4869591..abfb54e 100644
--- a/ops/op2/test_cases/sync/string_ref.out
+++ b/ops/op2/test_cases/sync/string_ref.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_string_owned {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::SeqOneByteString],
                 CType::Uint32,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
diff --git a/ops/op2/test_cases/sync/v8_ref_option.out b/ops/op2/test_cases/sync/v8_ref_option.out
index 22e6e79..6972d8b 100644
--- a/ops/op2/test_cases/sync/v8_ref_option.out
+++ b/ops/op2/test_cases/sync/v8_ref_option.out
@@ -14,7 +14,7 @@ impl deno_core::_ops::Op for op_v8_lifetime {
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
-            deno_core::v8::fast_api::FastFunction::new(
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
                 &[Type::V8Value, Type::V8Value, Type::V8Value, Type::CallbackOptions],
                 CType::Void,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,