Repository: denoland/deno_core (rust)

Issue:
op2 buffer pointers support
Needed for ext/ffi.

```rust
#[op2(fast)]
pub fn op_ffi_ptr_of(
  #[buffer] buf: *const u8
) -> Result<*const c_void, AnyError> {
  return Ok(buf as *const c_void)
}
```

Pull request:
feat(ops): support *mut u8 and *const u8
Fixes #229. Note that we cannot pass the address of zero-length buffers because of a v8 bug.

Patch:
diff --git a/core/external.rs b/core/external.rs
new file mode 100644
index 0000000..5e0ee3e
--- /dev/null
+++ b/core/external.rs
@@ -0,0 +1,53 @@
+use std::{marker::PhantomData, rc::Rc, cell::RefCell};
+
+/// Define an external type.
+#[macro_export]
+macro_rules! external {
+  ($name:literal, $type:ident) => {
+      
+  };
+}
+pub use external;
+
+pub trait Externalizable {
+  fn marker() -> u64;
+}
+
+pub struct ExternalDefinition {
+  name: &'static str,
+}
+
+#[repr(C)]
+struct ExternalWithMarker<T> {
+  marker: u64,
+  external: RefCell<T>,
+}
+
+/// A strongly-typed external pointer.
+#[repr(transparent)]
+pub struct ExternalPointer<E: Externalizable> {
+  ptr: *mut ExternalWithMarker<E>,
+  _type: std::marker::PhantomData<E>,
+}
+
+impl <E: Externalizable> ExternalPointer<E> {
+  pub fn destroy(self) {
+
+  }
+}
+
+impl <E: Externalizable> TryFrom<*mut std::ffi::c_void> for ExternalPointer<E> {
+  type Error = &'static str;
+
+  #[inline(always)]
+  fn try_from(value: *mut std::ffi::c_void) -> Result<Self, Self::Error> {
+    let expected_marker = E::marker();
+    // SAFETY: we assume the pointer is valid. If it is not, we risk a crash but that's
+    // unfortunately not something we can easily test.
+    if value.align_offset(std::mem::align_of::<u64>()) != 0 
+    || unsafe { std::ptr::read::<u64>(value) } != expected_marker {
+      return Err("Invalid v8::External");
+    }
+    Ok(Self { ptr: value as _, _type: PhantomData })
+  }
+}
diff --git a/core/runtime/ops.rs b/core/runtime/ops.rs
index 9efd743..13b68dc 100644
--- a/core/runtime/ops.rs
+++ b/core/runtime/ops.rs
@@ -666,7 +666,9 @@ mod tests {
       op_state_mut_attr,
       op_state_multi_attr,
       op_buffer_slice,
+      op_buffer_ptr,
       op_buffer_slice_32,
+      op_buffer_ptr_32,
       op_buffer_slice_unsafe_callback,
       op_buffer_copy,
       op_buffer_bytesmut,
@@ -1515,6 +1517,19 @@ mod tests {
     }
   }
 
+  #[op2(core, fast)]
+  pub fn op_buffer_ptr(
+    #[buffer] input: *const u8,
+    #[number] inlen: usize,
+    #[buffer] output: *mut u8,
+    #[number] outlen: usize,
+  ) {
+    if inlen > 0 && outlen > 0 {
+      // SAFETY: for test
+      unsafe { std::ptr::write(output, std::ptr::read(input)) }
+    }
+  }
+
   #[op2(core, fast)]
   pub fn op_buffer_slice_32(
     #[buffer] input: &[u32],
@@ -1529,18 +1544,38 @@ mod tests {
     }
   }
 
+  #[op2(core, fast)]
+  pub fn op_buffer_ptr_32(
+    #[buffer] input: *const u32,
+    #[number] inlen: usize,
+    #[buffer] output: *mut u32,
+    #[number] outlen: usize,
+  ) {
+    if inlen > 0 && outlen > 0 {
+      // SAFETY: for test
+      unsafe { std::ptr::write(output, std::ptr::read(input)) }
+    }
+  }
+
   #[tokio::test]
   pub async fn test_op_buffer_slice() -> Result<(), Box<dyn std::error::Error>>
   {
-    for (op, arr, size) in [
-      ("op_buffer_slice", "Uint8Array", 1),
-      ("op_buffer_slice_32", "Uint32Array", 4),
+    for (op, op_ptr, arr, size) in [
+      ("op_buffer_slice", "op_buffer_ptr", "Uint8Array", 1),
+      ("op_buffer_slice_32", "op_buffer_ptr_32", "Uint32Array", 4),
     ] {
+      // Zero-length buffers
       run_test2(
         10000,
         op,
         &format!("{op}(new {arr}(0), 0, new {arr}(0), 0);"),
       )?;
+      // Zero-length ptrs
+      run_test2(
+        10000,
+        op_ptr,
+        &format!("{op_ptr}(new {arr}(0), 0, new {arr}(0), 0);"),
+      )?;
       // UintXArray -> UintXArray
       run_test2(
         10000,
@@ -1552,6 +1587,17 @@ mod tests {
         assert(out[0] == 1);"
         ),
       )?;
+      // UintXArray -> UintXArray
+      run_test2(
+        10000,
+        op_ptr,
+        &format!(
+          r"
+        let out = new {arr}(10);
+        {op_ptr}(new {arr}([1,2,3]), 3, out, 10);
+        assert(out[0] == 1);"
+        ),
+      )?;
       // UintXArray(ArrayBuffer) -> UintXArray(ArrayBuffer)
       run_test2(
         10000,
diff --git a/ops/op2/README.md b/ops/op2/README.md
index c46cfb7..5571701 100644
--- a/ops/op2/README.md
+++ b/ops/op2/README.md
@@ -564,6 +564,34 @@ UInt8Array (resizable=true,false)
 <tr>
 <td>
 
+```text
+#[buffer] *mut u8
+```
+
+</td><td>
+✅
+</td><td>
+UInt8Array (resizable=true,false)
+</td><td>
+⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.
+</td></tr>
+<tr>
+<td>
+
+```text
+#[buffer] *const u8
+```
+
+</td><td>
+✅
+</td><td>
+UInt8Array (resizable=true,false)
+</td><td>
+⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.
+</td></tr>
+<tr>
+<td>
+
 ```text
 #[buffer(copy)] Vec<u8>
 ```
diff --git a/ops/op2/dispatch_fast.rs b/ops/op2/dispatch_fast.rs
index cab6cb8..29748e9 100644
--- a/ops/op2/dispatch_fast.rs
+++ b/ops/op2/dispatch_fast.rs
@@ -278,7 +278,10 @@ pub fn generate_dispatch_fast(
   debug_assert!(fastcall_types.len() == input_types.len());
 
   let output_type = output.quote_rust_type(deno_core);
+  // We don't want clippy to trigger warnings on number of arguments of the fastcall
+  // function -- these will still trigger on our normal call function, however.
   let fast_fn = quote!(
+    #[allow(clippy::too_many_arguments)]
     fn #fast_function(
       _: #deno_core::v8::Local<#deno_core::v8::Object>,
       #( #fastcall_names: #fastcall_types, )*
@@ -326,6 +329,9 @@ fn map_v8_fastcall_arg_to_arg(
     Arg::Buffer(Buffer::Slice(_, NumericArg::u8 | NumericArg::u32)) => {
       quote!(let #arg_ident = #buf;)
     }
+    Arg::Buffer(Buffer::Ptr(_, NumericArg::u8 | NumericArg::u32)) => {
+      quote!(let #arg_ident = if #buf.len() == 0 { ::std::ptr::null_mut() } else { #buf.as_mut_ptr() as _ };)
+    }
     Arg::Buffer(Buffer::Vec(NumericArg::u8 | NumericArg::u32)) => {
       quote!(let #arg_ident = #buf.to_vec();)
     }
@@ -449,12 +455,14 @@ fn map_arg_to_v8_fastcall_type(
   let rv = match arg {
     Arg::Buffer(
       Buffer::Slice(_, NumericArg::u8)
+      | Buffer::Ptr(_, NumericArg::u8)
       | Buffer::Vec(NumericArg::u8)
       | Buffer::BoxSlice(NumericArg::u8)
       | Buffer::Bytes(BufferMode::Copy),
     ) => V8FastCallType::Uint8Array,
     Arg::Buffer(
       Buffer::Slice(_, NumericArg::u32)
+      | Buffer::Ptr(_, NumericArg::u32)
       | Buffer::Vec(NumericArg::u32)
       | Buffer::BoxSlice(NumericArg::u32),
     ) => V8FastCallType::Uint32Array,
diff --git a/ops/op2/dispatch_slow.rs b/ops/op2/dispatch_slow.rs
index af61607..0208dbb 100644
--- a/ops/op2/dispatch_slow.rs
+++ b/ops/op2/dispatch_slow.rs
@@ -518,6 +518,12 @@ pub fn from_arg_buffer(
     Buffer::Slice(RefType::Mut, NumericArg::u8 | NumericArg::u32) => {
       quote!(let #arg_ident = #temp.as_mut();)
     }
+    Buffer::Ptr(RefType::Ref, NumericArg::u8 | NumericArg::u32) => {
+      quote!(let #arg_ident = if #temp.len() == 0 { std::ptr::null() } else { #temp.as_ref().as_ptr() };)
+    }
+    Buffer::Ptr(RefType::Mut, NumericArg::u8 | NumericArg::u32) => {
+      quote!(let #arg_ident = if #temp.len() == 0 { std::ptr::null_mut() } else { #temp.as_mut().as_mut_ptr() };)
+    }
     Buffer::Vec(NumericArg::u8 | NumericArg::u32) => {
       quote!(let #arg_ident = #temp.to_vec();)
     }
diff --git a/ops/op2/signature.rs b/ops/op2/signature.rs
index 8d53a1f..0e74b28 100644
--- a/ops/op2/signature.rs
+++ b/ops/op2/signature.rs
@@ -1526,6 +1526,10 @@ mod tests {
     fn op_slices(#[buffer] r#in: &[u8], #[buffer] out: &mut [u8]);
     (Buffer(Slice(Ref, u8)), Buffer(Slice(Mut, u8))) -> Infallible(Void)
   );
+  test!(
+    fn op_pointers(#[buffer] r#in: *const u8, #[buffer] out: *mut u8);
+    (Buffer(Ptr(Ref, u8)), Buffer(Ptr(Mut, u8))) -> Infallible(Void)
+  );
   test!(
     #[serde] fn op_serde(#[serde] input: package::SerdeInputType) -> Result<package::SerdeReturnType, Error>;
     (SerdeV8(package::SerdeInputType)) -> Result(SerdeV8(package::SerdeReturnType))
diff --git a/ops/op2/valid_args.md b/ops/op2/valid_args.md
index ae5a5c7..e5ab701 100644
--- a/ops/op2/valid_args.md
+++ b/ops/op2/valid_args.md
@@ -28,6 +28,8 @@
 | X         | #[serde] (Tuple, Tuple)          |          | any                                                 | ⚠️ May be slow.                                                                                                                                                                                                 |
 | X         | #[buffer] &mut [u8]              | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
 | X         | #[buffer] &[u8]                  | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.                                                                                                                                        |
+| X         | #[buffer] *mut u8                | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
+| X         | #[buffer] *const u8              | X        | UInt8Array (resizable=true,false)                   | ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.                                                |
 | X         | #[buffer(copy)] Vec<u8>          | X        | UInt8Array (resizable=true,false)                   | Safe, but forces a copy.                                                                                                                                                                                       |
 | X         | #[buffer(copy)] Box<[u8]>        | X        | UInt8Array (resizable=true,false)                   | Safe, but forces a copy.                                                                                                                                                                                       |
 | X         | #[buffer(copy)] bytes::Bytes     | X        | UInt8Array (resizable=true,false)                   | Safe, but forces a copy.                                                                                                                                                                                       |


Test patch:
diff --git a/ops/op2/test_cases/sync/add.out b/ops/op2/test_cases/sync/add.out
index b39cb49..0913716 100644
--- a/ops/op2/test_cases/sync/add.out
+++ b/ops/op2/test_cases/sync/add.out
@@ -30,6 +30,7 @@ impl op_add {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: u32,
diff --git a/ops/op2/test_cases/sync/bool.out b/ops/op2/test_cases/sync/bool.out
index e8a8231..fa1e1dc 100644
--- a/ops/op2/test_cases/sync/bool.out
+++ b/ops/op2/test_cases/sync/bool.out
@@ -30,6 +30,7 @@ impl op_bool {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: bool,
diff --git a/ops/op2/test_cases/sync/bool_result.out b/ops/op2/test_cases/sync/bool_result.out
index f9a977b..8ebff34 100644
--- a/ops/op2/test_cases/sync/bool_result.out
+++ b/ops/op2/test_cases/sync/bool_result.out
@@ -30,6 +30,7 @@ impl op_bool {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: bool,
diff --git a/ops/op2/test_cases/sync/buffers.out b/ops/op2/test_cases/sync/buffers.out
index a7dc06e..12eb1b6 100644
--- a/ops/op2/test_cases/sync/buffers.out
+++ b/ops/op2/test_cases/sync/buffers.out
@@ -9,7 +9,7 @@ impl deno_core::_ops::Op for op_buffers {
         false,
         false,
         false,
-        2usize as u8,
+        4usize as u8,
         Self::v8_fn_ptr as _,
         Some({
             use deno_core::v8::fast_api::Type;
@@ -19,6 +19,8 @@ impl deno_core::_ops::Op for op_buffers {
                     Type::V8Value,
                     Type::TypedArray(CType::Uint8),
                     Type::TypedArray(CType::Uint8),
+                    Type::TypedArray(CType::Uint8),
+                    Type::TypedArray(CType::Uint8),
                 ],
                 CType::Void,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
@@ -34,10 +36,13 @@ impl op_buffers {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
         arg1: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
+        arg2: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
+        arg3: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
     ) -> () {
         let result = {
             let arg0 = (unsafe {
@@ -52,7 +57,43 @@ impl op_buffers {
                 )
             }
                 .expect("Invalid buffer"));
-            Self::call(arg0, arg1)
+            let arg2 = if (unsafe {
+                deno_core::v8::fast_api::FastApiTypedArray::get_storage_from_pointer_if_aligned(
+                    arg2,
+                )
+            }
+                .expect("Invalid buffer"))
+                .len() == 0
+            {
+                ::std::ptr::null_mut()
+            } else {
+                (unsafe {
+                    deno_core::v8::fast_api::FastApiTypedArray::get_storage_from_pointer_if_aligned(
+                        arg2,
+                    )
+                }
+                    .expect("Invalid buffer"))
+                    .as_mut_ptr() as _
+            };
+            let arg3 = if (unsafe {
+                deno_core::v8::fast_api::FastApiTypedArray::get_storage_from_pointer_if_aligned(
+                    arg3,
+                )
+            }
+                .expect("Invalid buffer"))
+                .len() == 0
+            {
+                ::std::ptr::null_mut()
+            } else {
+                (unsafe {
+                    deno_core::v8::fast_api::FastApiTypedArray::get_storage_from_pointer_if_aligned(
+                        arg3,
+                    )
+                }
+                    .expect("Invalid buffer"))
+                    .as_mut_ptr() as _
+            };
+            Self::call(arg0, arg1, arg2, arg3)
         };
         result as _
     }
@@ -104,12 +145,58 @@ impl op_buffers {
                 }
             };
             let arg1 = arg1_temp.as_mut();
-            Self::call(arg0, arg1)
+            let arg2 = args.get(2usize as i32);
+            let mut arg2_temp;
+            arg2_temp = match unsafe {
+                deno_core::_ops::to_v8_slice::<u8>(&mut scope, arg2)
+            } {
+                Ok(arg2) => arg2,
+                Err(arg2_err) => {
+                    let msg = deno_core::v8::String::new_from_one_byte(
+                            &mut scope,
+                            arg2_err.as_bytes(),
+                            deno_core::v8::NewStringType::Normal,
+                        )
+                        .unwrap();
+                    let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                    scope.throw_exception(exc);
+                    return;
+                }
+            };
+            let arg2 = if arg2_temp.len() == 0 {
+                std::ptr::null()
+            } else {
+                arg2_temp.as_ref().as_ptr()
+            };
+            let arg3 = args.get(3usize as i32);
+            let mut arg3_temp;
+            arg3_temp = match unsafe {
+                deno_core::_ops::to_v8_slice::<u8>(&mut scope, arg3)
+            } {
+                Ok(arg3) => arg3,
+                Err(arg3_err) => {
+                    let msg = deno_core::v8::String::new_from_one_byte(
+                            &mut scope,
+                            arg3_err.as_bytes(),
+                            deno_core::v8::NewStringType::Normal,
+                        )
+                        .unwrap();
+                    let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                    scope.throw_exception(exc);
+                    return;
+                }
+            };
+            let arg3 = if arg3_temp.len() == 0 {
+                std::ptr::null_mut()
+            } else {
+                arg3_temp.as_mut().as_mut_ptr()
+            };
+            Self::call(arg0, arg1, arg2, arg3)
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
     #[inline(always)]
-    fn call(_a: &[u8], _b: &mut [u8]) {}
+    fn call(_a: &[u8], _b: &mut [u8], _c: *const u8, _d: *mut u8) {}
 }
 
 #[allow(non_camel_case_types)]
@@ -123,7 +210,7 @@ impl deno_core::_ops::Op for op_buffers_32 {
         false,
         false,
         false,
-        2usize as u8,
+        4usize as u8,
         Self::v8_fn_ptr as _,
         Some({
             use deno_core::v8::fast_api::Type;
@@ -133,6 +220,8 @@ impl deno_core::_ops::Op for op_buffers_32 {
                     Type::V8Value,
                     Type::TypedArray(CType::Uint32),
                     Type::TypedArray(CType::Uint32),
+                    Type::TypedArray(CType::Uint32),
+                    Type::TypedArray(CType::Uint32),
                 ],
                 CType::Void,
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
@@ -148,10 +237,13 @@ impl op_buffers_32 {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
         arg1: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
+        arg2: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
+        arg3: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
     ) -> () {
         let result = {
             let arg0 = (unsafe {
@@ -166,7 +258,43 @@ impl op_buffers_32 {
                 )
             }
                 .expect("Invalid buffer"));
-            Self::call(arg0, arg1)
+            let arg2 = if (unsafe {
+                deno_core::v8::fast_api::FastApiTypedArray::get_storage_from_pointer_if_aligned(
+                    arg2,
+                )
+            }
+                .expect("Invalid buffer"))
+                .len() == 0
+            {
+                ::std::ptr::null_mut()
+            } else {
+                (unsafe {
+                    deno_core::v8::fast_api::FastApiTypedArray::get_storage_from_pointer_if_aligned(
+                        arg2,
+                    )
+                }
+                    .expect("Invalid buffer"))
+                    .as_mut_ptr() as _
+            };
+            let arg3 = if (unsafe {
+                deno_core::v8::fast_api::FastApiTypedArray::get_storage_from_pointer_if_aligned(
+                    arg3,
+                )
+            }
+                .expect("Invalid buffer"))
+                .len() == 0
+            {
+                ::std::ptr::null_mut()
+            } else {
+                (unsafe {
+                    deno_core::v8::fast_api::FastApiTypedArray::get_storage_from_pointer_if_aligned(
+                        arg3,
+                    )
+                }
+                    .expect("Invalid buffer"))
+                    .as_mut_ptr() as _
+            };
+            Self::call(arg0, arg1, arg2, arg3)
         };
         result as _
     }
@@ -218,12 +346,58 @@ impl op_buffers_32 {
                 }
             };
             let arg1 = arg1_temp.as_mut();
-            Self::call(arg0, arg1)
+            let arg2 = args.get(2usize as i32);
+            let mut arg2_temp;
+            arg2_temp = match unsafe {
+                deno_core::_ops::to_v8_slice::<u32>(&mut scope, arg2)
+            } {
+                Ok(arg2) => arg2,
+                Err(arg2_err) => {
+                    let msg = deno_core::v8::String::new_from_one_byte(
+                            &mut scope,
+                            arg2_err.as_bytes(),
+                            deno_core::v8::NewStringType::Normal,
+                        )
+                        .unwrap();
+                    let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                    scope.throw_exception(exc);
+                    return;
+                }
+            };
+            let arg2 = if arg2_temp.len() == 0 {
+                std::ptr::null()
+            } else {
+                arg2_temp.as_ref().as_ptr()
+            };
+            let arg3 = args.get(3usize as i32);
+            let mut arg3_temp;
+            arg3_temp = match unsafe {
+                deno_core::_ops::to_v8_slice::<u32>(&mut scope, arg3)
+            } {
+                Ok(arg3) => arg3,
+                Err(arg3_err) => {
+                    let msg = deno_core::v8::String::new_from_one_byte(
+                            &mut scope,
+                            arg3_err.as_bytes(),
+                            deno_core::v8::NewStringType::Normal,
+                        )
+                        .unwrap();
+                    let exc = deno_core::v8::Exception::type_error(&mut scope, msg);
+                    scope.throw_exception(exc);
+                    return;
+                }
+            };
+            let arg3 = if arg3_temp.len() == 0 {
+                std::ptr::null_mut()
+            } else {
+                arg3_temp.as_mut().as_mut_ptr()
+            };
+            Self::call(arg0, arg1, arg2, arg3)
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
     #[inline(always)]
-    fn call(_a: &[u32], _b: &mut [u32]) {}
+    fn call(_a: &[u32], _b: &mut [u32], _c: *const u32, _d: *mut u32) {}
 }
 
 #[allow(non_camel_case_types)]
diff --git a/ops/op2/test_cases/sync/buffers.rs b/ops/op2/test_cases/sync/buffers.rs
index a062758..5e9305b 100644
--- a/ops/op2/test_cases/sync/buffers.rs
+++ b/ops/op2/test_cases/sync/buffers.rs
@@ -5,11 +5,11 @@ deno_ops_compile_test_runner::prelude!();
 use deno_core::JsBuffer;
 
 #[op2(fast)]
-fn op_buffers(#[buffer] _a: &[u8], #[buffer] _b: &mut [u8]) {
+fn op_buffers(#[buffer] _a: &[u8], #[buffer] _b: &mut [u8], #[buffer] _c: *const u8, #[buffer] _d: *mut u8) {
 }
 
 #[op2(fast)]
-fn op_buffers_32(#[buffer] _a: &[u32], #[buffer] _b: &mut [u32]) {
+fn op_buffers_32(#[buffer] _a: &[u32], #[buffer] _b: &mut [u32], #[buffer] _c: *const u32, #[buffer] _d: *mut u32) {
 }
 
 #[op2]
diff --git a/ops/op2/test_cases/sync/buffers_copy.out b/ops/op2/test_cases/sync/buffers_copy.out
index 6245a48..a68de40 100644
--- a/ops/op2/test_cases/sync/buffers_copy.out
+++ b/ops/op2/test_cases/sync/buffers_copy.out
@@ -35,6 +35,7 @@ impl op_buffers {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
@@ -180,6 +181,7 @@ impl op_buffers_32 {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
diff --git a/ops/op2/test_cases/sync/cfg.out b/ops/op2/test_cases/sync/cfg.out
index 3bf7aa2..0e14384 100644
--- a/ops/op2/test_cases/sync/cfg.out
+++ b/ops/op2/test_cases/sync/cfg.out
@@ -32,6 +32,7 @@ impl op_maybe_windows {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -83,6 +84,7 @@ impl op_maybe_windows {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
diff --git a/ops/op2/test_cases/sync/clippy_allow.out b/ops/op2/test_cases/sync/clippy_allow.out
index 4c23b35..6899ae9 100644
--- a/ops/op2/test_cases/sync/clippy_allow.out
+++ b/ops/op2/test_cases/sync/clippy_allow.out
@@ -32,6 +32,7 @@ impl op_extra_annotation {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -81,6 +82,7 @@ impl op_clippy_internal {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
diff --git a/ops/op2/test_cases/sync/clippy_allow.rs b/ops/op2/test_cases/sync/clippy_allow.rs
index 2b5aec7..dc91e86 100644
--- a/ops/op2/test_cases/sync/clippy_allow.rs
+++ b/ops/op2/test_cases/sync/clippy_allow.rs
@@ -12,4 +12,4 @@ pub fn op_clippy_internal() -> () {
   {
     #![allow(clippy::await_holding_refcell_ref)]
   }
-}
\ No newline at end of file
+}
diff --git a/ops/op2/test_cases/sync/doc_comment.out b/ops/op2/test_cases/sync/doc_comment.out
index 4e8023c..f326403 100644
--- a/ops/op2/test_cases/sync/doc_comment.out
+++ b/ops/op2/test_cases/sync/doc_comment.out
@@ -31,6 +31,7 @@ impl op_has_doc_comment {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
diff --git a/ops/op2/test_cases/sync/generics.out b/ops/op2/test_cases/sync/generics.out
index f645c0b..d20be2b 100644
--- a/ops/op2/test_cases/sync/generics.out
+++ b/ops/op2/test_cases/sync/generics.out
@@ -30,6 +30,7 @@ impl<T: Trait> op_generics<T> {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -77,6 +78,7 @@ impl<T: Trait + 'static> op_generics_static<T> {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -124,6 +126,7 @@ impl<T: Trait + 'static> op_generics_static_where<T> {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
diff --git a/ops/op2/test_cases/sync/op_state_attr.out b/ops/op2/test_cases/sync/op_state_attr.out
index 8f32e0c..8356fb2 100644
--- a/ops/op2/test_cases/sync/op_state_attr.out
+++ b/ops/op2/test_cases/sync/op_state_attr.out
@@ -30,6 +30,7 @@ impl op_state_rc {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
diff --git a/ops/op2/test_cases/sync/op_state_rc.out b/ops/op2/test_cases/sync/op_state_rc.out
index 62e120c..acd4bf2 100644
--- a/ops/op2/test_cases/sync/op_state_rc.out
+++ b/ops/op2/test_cases/sync/op_state_rc.out
@@ -30,6 +30,7 @@ impl op_state_rc {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
diff --git a/ops/op2/test_cases/sync/op_state_ref.out b/ops/op2/test_cases/sync/op_state_ref.out
index 077c7a9..c9c98e4 100644
--- a/ops/op2/test_cases/sync/op_state_ref.out
+++ b/ops/op2/test_cases/sync/op_state_ref.out
@@ -30,6 +30,7 @@ impl op_state_ref {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
@@ -101,6 +102,7 @@ impl op_state_mut {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
diff --git a/ops/op2/test_cases/sync/result_external.out b/ops/op2/test_cases/sync/result_external.out
index 6ccf984..c18f276 100644
--- a/ops/op2/test_cases/sync/result_external.out
+++ b/ops/op2/test_cases/sync/result_external.out
@@ -30,6 +30,7 @@ impl op_external_with_result {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
diff --git a/ops/op2/test_cases/sync/result_primitive.out b/ops/op2/test_cases/sync/result_primitive.out
index 620a8b0..e8e5270 100644
--- a/ops/op2/test_cases/sync/result_primitive.out
+++ b/ops/op2/test_cases/sync/result_primitive.out
@@ -30,6 +30,7 @@ impl op_u32_with_result {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
diff --git a/ops/op2/test_cases/sync/result_void.out b/ops/op2/test_cases/sync/result_void.out
index 1b638d4..3ff0f62 100644
--- a/ops/op2/test_cases/sync/result_void.out
+++ b/ops/op2/test_cases/sync/result_void.out
@@ -30,6 +30,7 @@ impl op_void_with_result {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
diff --git a/ops/op2/test_cases/sync/smi.out b/ops/op2/test_cases/sync/smi.out
index 339b773..9529eaa 100644
--- a/ops/op2/test_cases/sync/smi.out
+++ b/ops/op2/test_cases/sync/smi.out
@@ -30,6 +30,7 @@ impl op_smi_unsigned_return {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: i32,
@@ -158,6 +159,7 @@ impl op_smi_signed_return {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: i32,
diff --git a/ops/op2/test_cases/sync/string_cow.out b/ops/op2/test_cases/sync/string_cow.out
index 4f839d0..0e25bba 100644
--- a/ops/op2/test_cases/sync/string_cow.out
+++ b/ops/op2/test_cases/sync/string_cow.out
@@ -30,6 +30,7 @@ impl op_string_cow {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
diff --git a/ops/op2/test_cases/sync/string_onebyte.out b/ops/op2/test_cases/sync/string_onebyte.out
index 7c00236..1bc5d96 100644
--- a/ops/op2/test_cases/sync/string_onebyte.out
+++ b/ops/op2/test_cases/sync/string_onebyte.out
@@ -30,6 +30,7 @@ impl op_string_onebyte {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
diff --git a/ops/op2/test_cases/sync/string_owned.out b/ops/op2/test_cases/sync/string_owned.out
index 670840e..ea265df 100644
--- a/ops/op2/test_cases/sync/string_owned.out
+++ b/ops/op2/test_cases/sync/string_owned.out
@@ -30,6 +30,7 @@ impl op_string_owned {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
diff --git a/ops/op2/test_cases/sync/string_ref.out b/ops/op2/test_cases/sync/string_ref.out
index 5c334de..529224d 100644
--- a/ops/op2/test_cases/sync/string_ref.out
+++ b/ops/op2/test_cases/sync/string_ref.out
@@ -30,6 +30,7 @@ impl op_string_owned {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
diff --git a/ops/op2/test_cases/sync/v8_ref_option.out b/ops/op2/test_cases/sync/v8_ref_option.out
index 48d4708..12d1bd8 100644
--- a/ops/op2/test_cases/sync/v8_ref_option.out
+++ b/ops/op2/test_cases/sync/v8_ref_option.out
@@ -30,6 +30,7 @@ impl op_v8_lifetime {
     pub const fn decl() -> deno_core::_ops::OpDecl {
         <Self as deno_core::_ops::Op>::DECL
     }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: deno_core::v8::Local<deno_core::v8::Value>,