summaryrefslogtreecommitdiff
path: root/ops/op2/dispatch_fast.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ops/op2/dispatch_fast.rs')
-rw-r--r--ops/op2/dispatch_fast.rs293
1 files changed, 189 insertions, 104 deletions
diff --git a/ops/op2/dispatch_fast.rs b/ops/op2/dispatch_fast.rs
index 94140dbf6..5262196f4 100644
--- a/ops/op2/dispatch_fast.rs
+++ b/ops/op2/dispatch_fast.rs
@@ -11,7 +11,7 @@ use quote::quote;
#[allow(unused)]
#[derive(Debug, Default, PartialEq, Clone)]
-pub(crate) enum FastValue {
+pub(crate) enum V8FastCallType {
#[default]
Void,
Bool,
@@ -27,66 +27,74 @@ pub(crate) enum FastValue {
Uint32Array,
Float64Array,
SeqOneByteString,
+ CallbackOptions,
}
-impl FastValue {
+impl V8FastCallType {
/// Quote fast value type.
- fn quote_rust_type(&self) -> TokenStream {
+ fn quote_rust_type(&self, deno_core: &TokenStream) -> TokenStream {
match self {
- FastValue::Void => quote!(()),
- FastValue::Bool => quote!(bool),
- FastValue::U32 => quote!(u32),
- FastValue::I32 => quote!(i32),
- FastValue::U64 => quote!(u64),
- FastValue::I64 => quote!(i64),
- FastValue::F32 => quote!(f32),
- FastValue::F64 => quote!(f64),
- FastValue::Pointer => quote!(*mut ::std::ffi::c_void),
- FastValue::V8Value => unimplemented!("v8::Local<v8::Value>"),
- FastValue::Uint8Array
- | FastValue::Uint32Array
- | FastValue::Float64Array
- | FastValue::SeqOneByteString => unreachable!(),
+ V8FastCallType::Void => quote!(()),
+ V8FastCallType::Bool => quote!(bool),
+ V8FastCallType::U32 => quote!(u32),
+ V8FastCallType::I32 => quote!(i32),
+ V8FastCallType::U64 => quote!(u64),
+ V8FastCallType::I64 => quote!(i64),
+ V8FastCallType::F32 => quote!(f32),
+ V8FastCallType::F64 => quote!(f64),
+ V8FastCallType::Pointer => quote!(*mut ::std::ffi::c_void),
+ V8FastCallType::V8Value => {
+ quote!(#deno_core::v8::Local<#deno_core::v8::Value>)
+ }
+ V8FastCallType::CallbackOptions => {
+ quote!(*mut #deno_core::v8::fast_api::FastApiCallbackOptions)
+ }
+ V8FastCallType::Uint8Array
+ | V8FastCallType::Uint32Array
+ | V8FastCallType::Float64Array
+ | V8FastCallType::SeqOneByteString => unreachable!(),
}
}
/// Quote fast value type's variant.
fn quote_ctype(&self) -> TokenStream {
match &self {
- FastValue::Void => quote!(CType::Void),
- FastValue::Bool => quote!(CType::Bool),
- FastValue::U32 => quote!(CType::Uint32),
- FastValue::I32 => quote!(CType::Int32),
- FastValue::U64 => quote!(CType::Uint64),
- FastValue::I64 => quote!(CType::Int64),
- FastValue::F32 => quote!(CType::Float32),
- FastValue::F64 => quote!(CType::Float64),
- FastValue::Pointer => quote!(CType::Pointer),
- FastValue::V8Value => quote!(CType::V8Value),
- FastValue::Uint8Array => unreachable!(),
- FastValue::Uint32Array => unreachable!(),
- FastValue::Float64Array => unreachable!(),
- FastValue::SeqOneByteString => quote!(CType::SeqOneByteString),
+ V8FastCallType::Void => quote!(CType::Void),
+ V8FastCallType::Bool => quote!(CType::Bool),
+ V8FastCallType::U32 => quote!(CType::Uint32),
+ V8FastCallType::I32 => quote!(CType::Int32),
+ V8FastCallType::U64 => quote!(CType::Uint64),
+ V8FastCallType::I64 => quote!(CType::Int64),
+ V8FastCallType::F32 => quote!(CType::Float32),
+ V8FastCallType::F64 => quote!(CType::Float64),
+ V8FastCallType::Pointer => quote!(CType::Pointer),
+ V8FastCallType::V8Value => quote!(CType::V8Value),
+ V8FastCallType::CallbackOptions => quote!(CType::CallbackOptions),
+ V8FastCallType::Uint8Array => unreachable!(),
+ V8FastCallType::Uint32Array => unreachable!(),
+ V8FastCallType::Float64Array => unreachable!(),
+ V8FastCallType::SeqOneByteString => quote!(CType::SeqOneByteString),
}
}
/// Quote fast value type's variant.
fn quote_type(&self) -> TokenStream {
match &self {
- FastValue::Void => quote!(Type::Void),
- FastValue::Bool => quote!(Type::Bool),
- FastValue::U32 => quote!(Type::Uint32),
- FastValue::I32 => quote!(Type::Int32),
- FastValue::U64 => quote!(Type::Uint64),
- FastValue::I64 => quote!(Type::Int64),
- FastValue::F32 => quote!(Type::Float32),
- FastValue::F64 => quote!(Type::Float64),
- FastValue::Pointer => quote!(Type::Pointer),
- FastValue::V8Value => quote!(Type::V8Value),
- FastValue::Uint8Array => quote!(Type::TypedArray(CType::Uint8)),
- FastValue::Uint32Array => quote!(Type::TypedArray(CType::Uint32)),
- FastValue::Float64Array => quote!(Type::TypedArray(CType::Float64)),
- FastValue::SeqOneByteString => quote!(Type::SeqOneByteString),
+ V8FastCallType::Void => quote!(Type::Void),
+ V8FastCallType::Bool => quote!(Type::Bool),
+ V8FastCallType::U32 => quote!(Type::Uint32),
+ V8FastCallType::I32 => quote!(Type::Int32),
+ V8FastCallType::U64 => quote!(Type::Uint64),
+ V8FastCallType::I64 => quote!(Type::Int64),
+ V8FastCallType::F32 => quote!(Type::Float32),
+ V8FastCallType::F64 => quote!(Type::Float64),
+ V8FastCallType::Pointer => quote!(Type::Pointer),
+ V8FastCallType::V8Value => quote!(Type::V8Value),
+ V8FastCallType::CallbackOptions => quote!(Type::CallbackOptions),
+ V8FastCallType::Uint8Array => quote!(Type::TypedArray(CType::Uint8)),
+ V8FastCallType::Uint32Array => quote!(Type::TypedArray(CType::Uint32)),
+ V8FastCallType::Float64Array => quote!(Type::TypedArray(CType::Float64)),
+ V8FastCallType::SeqOneByteString => quote!(Type::SeqOneByteString),
}
}
}
@@ -95,104 +103,181 @@ pub fn generate_dispatch_fast(
generator_state: &mut GeneratorState,
signature: &ParsedSignature,
) -> Result<Option<(TokenStream, TokenStream)>, V8MappingError> {
- // Result not fast-call compatible (yet)
- if matches!(signature.ret_val, RetVal::Result(..)) {
- return Ok(None);
- }
-
let mut inputs = vec![];
for arg in &signature.args {
- let fv = match arg {
- Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None),
- Arg::Numeric(NumericArg::bool) => FastValue::Bool,
- Arg::Numeric(NumericArg::u32)
- | Arg::Numeric(NumericArg::u16)
- | Arg::Numeric(NumericArg::u8) => FastValue::U32,
- Arg::Numeric(NumericArg::i32)
- | Arg::Numeric(NumericArg::i16)
- | Arg::Numeric(NumericArg::i8)
- | Arg::Numeric(NumericArg::__SMI__) => FastValue::I32,
- Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => {
- FastValue::U64
- }
- Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
- FastValue::I64
- }
- _ => {
- return Err(V8MappingError::NoMapping("a fast argument", arg.clone()))
- }
+ let Some(fv) = map_arg_to_v8_fastcall_type(arg)? else {
+ return Ok(None);
};
inputs.push(fv);
}
+ let mut names = inputs
+ .iter()
+ .enumerate()
+ .map(|(i, _)| format_ident!("arg{i}"))
+ .collect::<Vec<_>>();
let ret_val = match &signature.ret_val {
RetVal::Infallible(arg) => arg,
RetVal::Result(arg) => arg,
};
- let output = match ret_val {
- Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None),
- Arg::Void => FastValue::Void,
- Arg::Numeric(NumericArg::bool) => FastValue::Bool,
- Arg::Numeric(NumericArg::u32)
- | Arg::Numeric(NumericArg::u16)
- | Arg::Numeric(NumericArg::u8) => FastValue::U32,
- Arg::Numeric(NumericArg::i32)
- | Arg::Numeric(NumericArg::i16)
- | Arg::Numeric(NumericArg::i8) => FastValue::I32,
- Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => {
- FastValue::U64
- }
- Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
- FastValue::I64
- }
- Arg::Special(_) => return Ok(None),
- _ => {
- return Err(V8MappingError::NoMapping(
- "a fast return value",
- ret_val.clone(),
- ))
- }
+ let output = match map_retval_to_v8_fastcall_type(ret_val)? {
+ None => return Ok(None),
+ Some(rv) => rv,
};
let GeneratorState {
fast_function,
deno_core,
+ result,
+ opctx,
+ fast_api_callback_options,
+ needs_fast_api_callback_options,
+ needs_fast_opctx,
..
- } = &generator_state;
+ } = generator_state;
+
+ let handle_error = match signature.ret_val {
+ RetVal::Infallible(_) => quote!(),
+ RetVal::Result(_) => {
+ *needs_fast_api_callback_options = true;
+ *needs_fast_opctx = true;
+ inputs.push(V8FastCallType::CallbackOptions);
+ quote! {
+ let #result = match #result {
+ Ok(#result) => #result,
+ Err(err) => {
+ // FASTCALL FALLBACK: This is where we set the errors for the slow-call error pickup path. There
+ // is no code running between this and the other FASTCALL FALLBACK comment, except some V8 code
+ // required to perform the fallback process. This is why the below call is safe.
- let input_types = inputs.iter().map(|fv| fv.quote_type());
+ // The reason we need to do this is because V8 does not allow exceptions to be thrown from the
+ // fast call. Instead, you are required to set the fallback flag, which indicates to V8 that it
+ // should re-call the slow version of the function. Technically the slow call should perform the
+ // same operation and then throw the same error (because it should be idempotent), but in our
+ // case we stash the error and pick it up on the slow path before doing any work.
+
+ // TODO(mmastrac): We should allow an #[op] flag to re-perform slow calls without the error path when
+ // the method is performance sensitive.
+
+ // SAFETY: We guarantee that OpCtx has no mutable references once ops are live and being called,
+ // allowing us to perform this one little bit of mutable magic.
+ unsafe { #opctx.unsafely_set_last_error_for_ops_only(err); }
+ #fast_api_callback_options.fallback = true;
+ return ::std::default::Default::default();
+ }
+ };
+ }
+ }
+ };
+
+ let input_types = inputs.iter().map(|fv| fv.quote_type()).collect::<Vec<_>>();
let output_type = output.quote_ctype();
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(
- &[ #( #input_types ),* ],
+ &[ Type::V8Value, #( #input_types ),* ],
#output_type,
Self::#fast_function as *const ::std::ffi::c_void
)
};
- let output_type = output.quote_rust_type();
- let names = &inputs
+ let output_type = output.quote_rust_type(deno_core);
+ let mut types = inputs
.iter()
- .enumerate()
- .map(|(i, _)| format_ident!("arg{i}"))
+ .map(|rv| rv.quote_rust_type(deno_core))
.collect::<Vec<_>>();
- let types = inputs.iter().map(|rv| rv.quote_rust_type());
+
+ let call_args = names.clone();
+
+ let with_fast_api_callback_options = if *needs_fast_api_callback_options {
+ types.push(V8FastCallType::CallbackOptions.quote_rust_type(deno_core));
+ names.push(fast_api_callback_options.clone());
+ quote! {
+ let #fast_api_callback_options = unsafe { &mut *#fast_api_callback_options };
+ }
+ } else {
+ quote!()
+ };
+ let with_opctx = if *needs_fast_opctx {
+ quote!(
+ let #opctx = unsafe {
+ &*(#deno_core::v8::Local::<v8::External>::cast(unsafe { #fast_api_callback_options.data.data }).value()
+ as *const #deno_core::_ops::OpCtx)
+ };
+ )
+ } else {
+ quote!()
+ };
let fast_fn = quote!(
fn #fast_function(
_: #deno_core::v8::Local<#deno_core::v8::Object>,
#( #names: #types, )*
) -> #output_type {
- #(
- let #names = #names as _;
- )*
- Self::call(#(#names),*)
+ #with_fast_api_callback_options
+ #with_opctx
+ let #result = Self::call(#(#call_args as _),*);
+ #handle_error
+ #result
}
);
Ok(Some((fast_definition, fast_fn)))
}
+
+fn map_arg_to_v8_fastcall_type(
+ arg: &Arg,
+) -> Result<Option<V8FastCallType>, V8MappingError> {
+ let rv = match arg {
+ Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None),
+ Arg::Numeric(NumericArg::bool) => V8FastCallType::Bool,
+ Arg::Numeric(NumericArg::u32)
+ | Arg::Numeric(NumericArg::u16)
+ | Arg::Numeric(NumericArg::u8) => V8FastCallType::U32,
+ Arg::Numeric(NumericArg::i32)
+ | Arg::Numeric(NumericArg::i16)
+ | Arg::Numeric(NumericArg::i8)
+ | Arg::Numeric(NumericArg::__SMI__) => V8FastCallType::I32,
+ Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => {
+ V8FastCallType::U64
+ }
+ Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
+ V8FastCallType::I64
+ }
+ _ => return Err(V8MappingError::NoMapping("a fast argument", arg.clone())),
+ };
+ Ok(Some(rv))
+}
+
+fn map_retval_to_v8_fastcall_type(
+ arg: &Arg,
+) -> Result<Option<V8FastCallType>, V8MappingError> {
+ let rv = match arg {
+ Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None),
+ Arg::Void => V8FastCallType::Void,
+ Arg::Numeric(NumericArg::bool) => V8FastCallType::Bool,
+ Arg::Numeric(NumericArg::u32)
+ | Arg::Numeric(NumericArg::u16)
+ | Arg::Numeric(NumericArg::u8) => V8FastCallType::U32,
+ Arg::Numeric(NumericArg::i32)
+ | Arg::Numeric(NumericArg::i16)
+ | Arg::Numeric(NumericArg::i8) => V8FastCallType::I32,
+ Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => {
+ V8FastCallType::U64
+ }
+ Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
+ V8FastCallType::I64
+ }
+ Arg::Special(_) => return Ok(None),
+ _ => {
+ return Err(V8MappingError::NoMapping(
+ "a fast return value",
+ arg.clone(),
+ ))
+ }
+ };
+ Ok(Some(rv))
+}