1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-25 00:29:09 -05:00

feat(ops): support raw pointer arguments (#16826)

See https://github.com/denoland/deno/pull/16814#discussion_r1032744083.
Allows nullable buffers in low-level ops like FFI:

```rust
fn op_ffi_ptr_of<FP>(
  state: &mut OpState,
  buf: *const u8,
  out: &mut [u32],
) 
where
  FP: FfiPermissions + 'static {
  // ..
}
```
This commit is contained in:
Divy Srivastava 2022-11-26 06:37:43 -08:00 committed by GitHub
parent 7e0c558187
commit fcdcc8c0c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 160 additions and 6 deletions

View file

@ -2065,7 +2065,7 @@ fn op_ffi_call_nonblocking<'scope>(
#[op(fast)] #[op(fast)]
fn op_ffi_ptr_of<FP>( fn op_ffi_ptr_of<FP>(
state: &mut deno_core::OpState, state: &mut deno_core::OpState,
buf: &[u8], buf: *const u8,
out: &mut [u32], out: &mut [u32],
) -> Result<(), AnyError> ) -> Result<(), AnyError>
where where
@ -2084,7 +2084,7 @@ where
// SAFETY: Out buffer was asserted to be at least large enough to hold a usize, and properly aligned. // SAFETY: Out buffer was asserted to be at least large enough to hold a usize, and properly aligned.
let out = unsafe { &mut *outptr }; let out = unsafe { &mut *outptr };
*out = buf.as_ptr() as usize; *out = buf as usize;
Ok(()) Ok(())
} }

View file

@ -432,6 +432,13 @@ fn codegen_arg(
}; };
} }
} }
// Fast path for `*const u8`
if is_ptr_u8(&**ty) {
let blk = codegen_u8_ptr(core, idx);
return quote! {
let #ident = #blk;
};
}
// Otherwise deserialize it via serde_v8 // Otherwise deserialize it via serde_v8
quote! { quote! {
let #ident = args.get(#idx as i32); let #ident = args.get(#idx as i32);
@ -450,7 +457,6 @@ fn codegen_u8_slice(core: &TokenStream2, idx: usize) -> TokenStream2 {
let value = args.get(#idx as i32); let value = args.get(#idx as i32);
match #core::v8::Local::<#core::v8::ArrayBuffer>::try_from(value) { match #core::v8::Local::<#core::v8::ArrayBuffer>::try_from(value) {
Ok(b) => { Ok(b) => {
// Handles detached buffers.
let byte_length = b.byte_length(); let byte_length = b.byte_length();
let store = b.data() as *mut u8; let store = b.data() as *mut u8;
// SAFETY: rust guarantees that lifetime of slice is no longer than the call. // SAFETY: rust guarantees that lifetime of slice is no longer than the call.
@ -477,6 +483,30 @@ fn codegen_u8_slice(core: &TokenStream2, idx: usize) -> TokenStream2 {
} }
} }
fn codegen_u8_ptr(core: &TokenStream2, idx: usize) -> TokenStream2 {
quote! {{
let value = args.get(#idx as i32);
match #core::v8::Local::<#core::v8::ArrayBuffer>::try_from(value) {
Ok(b) => b.data() as *const u8,
Err(_) => {
if let Ok(view) = #core::v8::Local::<#core::v8::ArrayBufferView>::try_from(value) {
let offset = view.byte_offset();
let buffer = match view.buffer(scope) {
Some(v) => v,
None => {
return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx));
}
};
let store = buffer.data() as *mut u8;
unsafe { store.add(offset) }
} else {
return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx));
}
}
}
}}
}
fn codegen_u32_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 { fn codegen_u32_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 {
quote! { quote! {
if let Ok(view) = #core::v8::Local::<#core::v8::Uint32Array>::try_from(args.get(#idx as i32)) { if let Ok(view) = #core::v8::Local::<#core::v8::Uint32Array>::try_from(args.get(#idx as i32)) {
@ -602,6 +632,10 @@ fn is_u32_slice_mut(ty: impl ToTokens) -> bool {
tokens(ty) == "& mut [u32]" tokens(ty) == "& mut [u32]"
} }
fn is_ptr_u8(ty: impl ToTokens) -> bool {
tokens(ty) == "* const u8"
}
fn is_optional_fast_callback_option(ty: impl ToTokens) -> bool { fn is_optional_fast_callback_option(ty: impl ToTokens) -> bool {
tokens(&ty).contains("Option < & mut FastApiCallbackOptions") tokens(&ty).contains("Option < & mut FastApiCallbackOptions")
} }

View file

@ -2,13 +2,13 @@
use crate::Op; use crate::Op;
use pmutil::{q, Quote}; use pmutil::{q, Quote};
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use std::collections::HashMap; use std::collections::BTreeMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::fmt::Formatter; use std::fmt::Formatter;
use syn::{ use syn::{
parse_quote, punctuated::Punctuated, token::Colon2, parse_quote, punctuated::Punctuated, token::Colon2,
AngleBracketedGenericArguments, FnArg, GenericArgument, PatType, Path, AngleBracketedGenericArguments, FnArg, GenericArgument, PatType, Path,
PathArguments, PathSegment, ReturnType, Signature, Type, TypePath, PathArguments, PathSegment, ReturnType, Signature, Type, TypePath, TypePtr,
TypeReference, TypeSlice, TypeReference, TypeSlice,
}; };
@ -25,6 +25,7 @@ enum TransformKind {
V8Value, V8Value,
SliceU32(bool), SliceU32(bool),
SliceU8(bool), SliceU8(bool),
PtrU8,
} }
impl Transform { impl Transform {
@ -48,6 +49,13 @@ impl Transform {
index, index,
} }
} }
fn u8_ptr(index: usize) -> Self {
Transform {
kind: TransformKind::PtrU8,
index,
}
}
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -116,6 +124,21 @@ impl Transform {
}; };
}) })
} }
// *const u8
TransformKind::PtrU8 => {
*ty =
parse_quote! { *const #core::v8::fast_api::FastApiTypedArray<u8> };
q!(Vars { var: &ident }, {
let var = match unsafe { &*var }.get_storage_if_aligned() {
Some(v) => v.as_ptr(),
None => {
unsafe { &mut *fast_api_callback_options }.fallback = true;
return Default::default();
}
};
})
}
} }
} }
} }
@ -178,7 +201,7 @@ pub(crate) struct Optimizer {
pub(crate) fast_result: Option<FastValue>, pub(crate) fast_result: Option<FastValue>,
pub(crate) fast_parameters: Vec<FastValue>, pub(crate) fast_parameters: Vec<FastValue>,
pub(crate) transforms: HashMap<usize, Transform>, pub(crate) transforms: BTreeMap<usize, Transform>,
pub(crate) fast_compatible: bool, pub(crate) fast_compatible: bool,
pub(crate) is_async: bool, pub(crate) is_async: bool,
@ -517,6 +540,32 @@ impl Optimizer {
}, },
_ => return Err(BailoutReason::FastUnsupportedParamType), _ => return Err(BailoutReason::FastUnsupportedParamType),
}, },
// *const T
Type::Ptr(TypePtr {
elem,
const_token: Some(_),
..
}) => match &**elem {
Type::Path(TypePath {
path: Path { segments, .. },
..
}) => {
let segment = single_segment(segments)?;
match segment {
// Is `T` a u8?
PathSegment { ident, .. } if ident == "u8" => {
self.has_fast_callback_option = true;
self.fast_parameters.push(FastValue::Uint8Array);
assert!(self
.transforms
.insert(index, Transform::u8_ptr(index))
.is_none());
}
_ => return Err(BailoutReason::FastUnsupportedParamType),
}
}
_ => return Err(BailoutReason::FastUnsupportedParamType),
},
_ => return Err(BailoutReason::FastUnsupportedParamType), _ => return Err(BailoutReason::FastUnsupportedParamType),
}, },
_ => return Err(BailoutReason::FastUnsupportedParamType), _ => return Err(BailoutReason::FastUnsupportedParamType),

View file

@ -0,0 +1,10 @@
=== Optimizer Dump ===
returns_result: false
has_ref_opstate: true
has_rc_opstate: false
has_fast_callback_option: true
fast_result: Some(Void)
fast_parameters: [V8Value, Uint8Array, Uint32Array]
transforms: {1: Transform { kind: PtrU8, index: 1 }, 2: Transform { kind: SliceU32(true), index: 2 }}
is_async: false
fast_compatible: true

View file

@ -0,0 +1,55 @@
struct op_ffi_ptr_of_fast<FP> {
_phantom: ::std::marker::PhantomData<FP>,
}
impl<'scope, FP> deno_core::v8::fast_api::FastFunction for op_ffi_ptr_of_fast<FP>
where
FP: FfiPermissions + 'static,
{
fn function(&self) -> *const ::std::ffi::c_void {
op_ffi_ptr_of_fast_fn::<FP> as *const ::std::ffi::c_void
}
fn args(&self) -> &'static [deno_core::v8::fast_api::Type] {
use deno_core::v8::fast_api::Type::*;
use deno_core::v8::fast_api::CType;
&[V8Value, TypedArray(CType::Uint8), TypedArray(CType::Uint32), CallbackOptions]
}
fn return_type(&self) -> deno_core::v8::fast_api::CType {
deno_core::v8::fast_api::CType::Void
}
}
fn op_ffi_ptr_of_fast_fn<'scope, FP>(
_: deno_core::v8::Local<deno_core::v8::Object>,
buf: *const deno_core::v8::fast_api::FastApiTypedArray<u8>,
out: *const deno_core::v8::fast_api::FastApiTypedArray<u32>,
fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
) -> ()
where
FP: FfiPermissions + 'static,
{
use deno_core::v8;
use deno_core::_ops;
let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe {
&mut *fast_api_callback_options
};
let __ctx = unsafe {
&*(v8::Local::<v8::External>::cast(unsafe { __opts.data.data }).value()
as *const _ops::OpCtx)
};
let state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state);
let buf = match unsafe { &*buf }.get_storage_if_aligned() {
Some(v) => v.as_ptr(),
None => {
unsafe { &mut *fast_api_callback_options }.fallback = true;
return Default::default();
}
};
let out = match unsafe { &*out }.get_storage_if_aligned() {
Some(v) => v,
None => {
unsafe { &mut *fast_api_callback_options }.fallback = true;
return Default::default();
}
};
let result = op_ffi_ptr_of::call::<FP>(state, buf, out);
result
}

View file

@ -0,0 +1,6 @@
fn op_ffi_ptr_of<FP>(state: &mut OpState, buf: *const u8, out: &mut [u32])
where
FP: FfiPermissions + 'static,
{
// ..
}