Skip to content

Commit bcbcc72

Browse files
littledivykt3k
authored andcommitted
feat(ops): support raw pointer arguments (#16826)
See #16814 (comment). 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 { // .. } ```
1 parent 73ff200 commit bcbcc72

File tree

6 files changed

+160
-6
lines changed

6 files changed

+160
-6
lines changed

ext/ffi/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2065,7 +2065,7 @@ fn op_ffi_call_nonblocking<'scope>(
20652065
#[op(fast)]
20662066
fn op_ffi_ptr_of<FP>(
20672067
state: &mut deno_core::OpState,
2068-
buf: &[u8],
2068+
buf: *const u8,
20692069
out: &mut [u32],
20702070
) -> Result<(), AnyError>
20712071
where
@@ -2084,7 +2084,7 @@ where
20842084

20852085
// SAFETY: Out buffer was asserted to be at least large enough to hold a usize, and properly aligned.
20862086
let out = unsafe { &mut *outptr };
2087-
*out = buf.as_ptr() as usize;
2087+
*out = buf as usize;
20882088

20892089
Ok(())
20902090
}

ops/lib.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,13 @@ fn codegen_arg(
432432
};
433433
}
434434
}
435+
// Fast path for `*const u8`
436+
if is_ptr_u8(&**ty) {
437+
let blk = codegen_u8_ptr(core, idx);
438+
return quote! {
439+
let #ident = #blk;
440+
};
441+
}
435442
// Otherwise deserialize it via serde_v8
436443
quote! {
437444
let #ident = args.get(#idx as i32);
@@ -450,7 +457,6 @@ fn codegen_u8_slice(core: &TokenStream2, idx: usize) -> TokenStream2 {
450457
let value = args.get(#idx as i32);
451458
match #core::v8::Local::<#core::v8::ArrayBuffer>::try_from(value) {
452459
Ok(b) => {
453-
// Handles detached buffers.
454460
let byte_length = b.byte_length();
455461
let store = b.data() as *mut u8;
456462
// 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 {
477483
}
478484
}
479485

486+
fn codegen_u8_ptr(core: &TokenStream2, idx: usize) -> TokenStream2 {
487+
quote! {{
488+
let value = args.get(#idx as i32);
489+
match #core::v8::Local::<#core::v8::ArrayBuffer>::try_from(value) {
490+
Ok(b) => b.data() as *const u8,
491+
Err(_) => {
492+
if let Ok(view) = #core::v8::Local::<#core::v8::ArrayBufferView>::try_from(value) {
493+
let offset = view.byte_offset();
494+
let buffer = match view.buffer(scope) {
495+
Some(v) => v,
496+
None => {
497+
return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx));
498+
}
499+
};
500+
let store = buffer.data() as *mut u8;
501+
unsafe { store.add(offset) }
502+
} else {
503+
return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx));
504+
}
505+
}
506+
}
507+
}}
508+
}
509+
480510
fn codegen_u32_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 {
481511
quote! {
482512
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 {
602632
tokens(ty) == "& mut [u32]"
603633
}
604634

635+
fn is_ptr_u8(ty: impl ToTokens) -> bool {
636+
tokens(ty) == "* const u8"
637+
}
638+
605639
fn is_optional_fast_callback_option(ty: impl ToTokens) -> bool {
606640
tokens(&ty).contains("Option < & mut FastApiCallbackOptions")
607641
}

ops/optimizer.rs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
use crate::Op;
33
use pmutil::{q, Quote};
44
use proc_macro2::TokenStream;
5-
use std::collections::HashMap;
5+
use std::collections::BTreeMap;
66
use std::fmt::Debug;
77
use std::fmt::Formatter;
88
use syn::{
99
parse_quote, punctuated::Punctuated, token::Colon2,
1010
AngleBracketedGenericArguments, FnArg, GenericArgument, PatType, Path,
11-
PathArguments, PathSegment, ReturnType, Signature, Type, TypePath,
11+
PathArguments, PathSegment, ReturnType, Signature, Type, TypePath, TypePtr,
1212
TypeReference, TypeSlice,
1313
};
1414

@@ -25,6 +25,7 @@ enum TransformKind {
2525
V8Value,
2626
SliceU32(bool),
2727
SliceU8(bool),
28+
PtrU8,
2829
}
2930

3031
impl Transform {
@@ -48,6 +49,13 @@ impl Transform {
4849
index,
4950
}
5051
}
52+
53+
fn u8_ptr(index: usize) -> Self {
54+
Transform {
55+
kind: TransformKind::PtrU8,
56+
index,
57+
}
58+
}
5159
}
5260

5361
#[derive(Debug, PartialEq)]
@@ -116,6 +124,21 @@ impl Transform {
116124
};
117125
})
118126
}
127+
// *const u8
128+
TransformKind::PtrU8 => {
129+
*ty =
130+
parse_quote! { *const #core::v8::fast_api::FastApiTypedArray<u8> };
131+
132+
q!(Vars { var: &ident }, {
133+
let var = match unsafe { &*var }.get_storage_if_aligned() {
134+
Some(v) => v.as_ptr(),
135+
None => {
136+
unsafe { &mut *fast_api_callback_options }.fallback = true;
137+
return Default::default();
138+
}
139+
};
140+
})
141+
}
119142
}
120143
}
121144
}
@@ -178,7 +201,7 @@ pub(crate) struct Optimizer {
178201
pub(crate) fast_result: Option<FastValue>,
179202
pub(crate) fast_parameters: Vec<FastValue>,
180203

181-
pub(crate) transforms: HashMap<usize, Transform>,
204+
pub(crate) transforms: BTreeMap<usize, Transform>,
182205
pub(crate) fast_compatible: bool,
183206

184207
pub(crate) is_async: bool,
@@ -517,6 +540,32 @@ impl Optimizer {
517540
},
518541
_ => return Err(BailoutReason::FastUnsupportedParamType),
519542
},
543+
// *const T
544+
Type::Ptr(TypePtr {
545+
elem,
546+
const_token: Some(_),
547+
..
548+
}) => match &**elem {
549+
Type::Path(TypePath {
550+
path: Path { segments, .. },
551+
..
552+
}) => {
553+
let segment = single_segment(segments)?;
554+
match segment {
555+
// Is `T` a u8?
556+
PathSegment { ident, .. } if ident == "u8" => {
557+
self.has_fast_callback_option = true;
558+
self.fast_parameters.push(FastValue::Uint8Array);
559+
assert!(self
560+
.transforms
561+
.insert(index, Transform::u8_ptr(index))
562+
.is_none());
563+
}
564+
_ => return Err(BailoutReason::FastUnsupportedParamType),
565+
}
566+
}
567+
_ => return Err(BailoutReason::FastUnsupportedParamType),
568+
},
520569
_ => return Err(BailoutReason::FastUnsupportedParamType),
521570
},
522571
_ => return Err(BailoutReason::FastUnsupportedParamType),
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== Optimizer Dump ===
2+
returns_result: false
3+
has_ref_opstate: true
4+
has_rc_opstate: false
5+
has_fast_callback_option: true
6+
fast_result: Some(Void)
7+
fast_parameters: [V8Value, Uint8Array, Uint32Array]
8+
transforms: {1: Transform { kind: PtrU8, index: 1 }, 2: Transform { kind: SliceU32(true), index: 2 }}
9+
is_async: false
10+
fast_compatible: true

ops/optimizer_tests/raw_ptr.out

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
struct op_ffi_ptr_of_fast<FP> {
2+
_phantom: ::std::marker::PhantomData<FP>,
3+
}
4+
impl<'scope, FP> deno_core::v8::fast_api::FastFunction for op_ffi_ptr_of_fast<FP>
5+
where
6+
FP: FfiPermissions + 'static,
7+
{
8+
fn function(&self) -> *const ::std::ffi::c_void {
9+
op_ffi_ptr_of_fast_fn::<FP> as *const ::std::ffi::c_void
10+
}
11+
fn args(&self) -> &'static [deno_core::v8::fast_api::Type] {
12+
use deno_core::v8::fast_api::Type::*;
13+
use deno_core::v8::fast_api::CType;
14+
&[V8Value, TypedArray(CType::Uint8), TypedArray(CType::Uint32), CallbackOptions]
15+
}
16+
fn return_type(&self) -> deno_core::v8::fast_api::CType {
17+
deno_core::v8::fast_api::CType::Void
18+
}
19+
}
20+
fn op_ffi_ptr_of_fast_fn<'scope, FP>(
21+
_: deno_core::v8::Local<deno_core::v8::Object>,
22+
buf: *const deno_core::v8::fast_api::FastApiTypedArray<u8>,
23+
out: *const deno_core::v8::fast_api::FastApiTypedArray<u32>,
24+
fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
25+
) -> ()
26+
where
27+
FP: FfiPermissions + 'static,
28+
{
29+
use deno_core::v8;
30+
use deno_core::_ops;
31+
let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe {
32+
&mut *fast_api_callback_options
33+
};
34+
let __ctx = unsafe {
35+
&*(v8::Local::<v8::External>::cast(unsafe { __opts.data.data }).value()
36+
as *const _ops::OpCtx)
37+
};
38+
let state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state);
39+
let buf = match unsafe { &*buf }.get_storage_if_aligned() {
40+
Some(v) => v.as_ptr(),
41+
None => {
42+
unsafe { &mut *fast_api_callback_options }.fallback = true;
43+
return Default::default();
44+
}
45+
};
46+
let out = match unsafe { &*out }.get_storage_if_aligned() {
47+
Some(v) => v,
48+
None => {
49+
unsafe { &mut *fast_api_callback_options }.fallback = true;
50+
return Default::default();
51+
}
52+
};
53+
let result = op_ffi_ptr_of::call::<FP>(state, buf, out);
54+
result
55+
}

ops/optimizer_tests/raw_ptr.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn op_ffi_ptr_of<FP>(state: &mut OpState, buf: *const u8, out: &mut [u32])
2+
where
3+
FP: FfiPermissions + 'static,
4+
{
5+
// ..
6+
}

0 commit comments

Comments
 (0)