diff options
Diffstat (limited to 'ops/op2/dispatch_fast.rs')
-rw-r--r-- | ops/op2/dispatch_fast.rs | 293 |
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)) +} |