2023-06-24 07:54:10 -04:00
|
|
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
|
|
use super::generator_state::GeneratorState;
|
|
|
|
use super::signature::Arg;
|
|
|
|
use super::signature::NumericArg;
|
|
|
|
use super::signature::ParsedSignature;
|
|
|
|
use super::signature::RetVal;
|
|
|
|
use super::V8MappingError;
|
|
|
|
use proc_macro2::TokenStream;
|
|
|
|
use quote::format_ident;
|
|
|
|
use quote::quote;
|
|
|
|
|
|
|
|
#[allow(unused)]
|
|
|
|
#[derive(Debug, Default, PartialEq, Clone)]
|
2023-06-25 10:36:09 -04:00
|
|
|
pub(crate) enum V8FastCallType {
|
2023-06-24 07:54:10 -04:00
|
|
|
#[default]
|
|
|
|
Void,
|
|
|
|
Bool,
|
|
|
|
U32,
|
|
|
|
I32,
|
|
|
|
U64,
|
|
|
|
I64,
|
|
|
|
F32,
|
|
|
|
F64,
|
|
|
|
Pointer,
|
|
|
|
V8Value,
|
|
|
|
Uint8Array,
|
|
|
|
Uint32Array,
|
|
|
|
Float64Array,
|
|
|
|
SeqOneByteString,
|
2023-06-25 10:36:09 -04:00
|
|
|
CallbackOptions,
|
2023-06-24 07:54:10 -04:00
|
|
|
}
|
|
|
|
|
2023-06-25 10:36:09 -04:00
|
|
|
impl V8FastCallType {
|
2023-06-24 07:54:10 -04:00
|
|
|
/// Quote fast value type.
|
2023-06-25 10:36:09 -04:00
|
|
|
fn quote_rust_type(&self, deno_core: &TokenStream) -> TokenStream {
|
2023-06-24 07:54:10 -04:00
|
|
|
match self {
|
2023-06-25 10:36:09 -04:00
|
|
|
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!(),
|
2023-06-24 07:54:10 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Quote fast value type's variant.
|
|
|
|
fn quote_ctype(&self) -> TokenStream {
|
|
|
|
match &self {
|
2023-06-25 10:36:09 -04:00
|
|
|
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),
|
2023-06-24 07:54:10 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Quote fast value type's variant.
|
|
|
|
fn quote_type(&self) -> TokenStream {
|
|
|
|
match &self {
|
2023-06-25 10:36:09 -04:00
|
|
|
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),
|
2023-06-24 07:54:10 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn generate_dispatch_fast(
|
|
|
|
generator_state: &mut GeneratorState,
|
|
|
|
signature: &ParsedSignature,
|
|
|
|
) -> Result<Option<(TokenStream, TokenStream)>, V8MappingError> {
|
|
|
|
let mut inputs = vec![];
|
|
|
|
for arg in &signature.args {
|
2023-06-25 10:36:09 -04:00
|
|
|
let Some(fv) = map_arg_to_v8_fastcall_type(arg)? else {
|
|
|
|
return Ok(None);
|
2023-06-24 07:54:10 -04:00
|
|
|
};
|
|
|
|
inputs.push(fv);
|
|
|
|
}
|
2023-06-25 10:36:09 -04:00
|
|
|
let mut names = inputs
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, _)| format_ident!("arg{i}"))
|
|
|
|
.collect::<Vec<_>>();
|
2023-06-24 07:54:10 -04:00
|
|
|
|
|
|
|
let ret_val = match &signature.ret_val {
|
|
|
|
RetVal::Infallible(arg) => arg,
|
|
|
|
RetVal::Result(arg) => arg,
|
|
|
|
};
|
|
|
|
|
2023-06-25 10:36:09 -04:00
|
|
|
let output = match map_retval_to_v8_fastcall_type(ret_val)? {
|
|
|
|
None => return Ok(None),
|
|
|
|
Some(rv) => rv,
|
2023-06-24 07:54:10 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
let GeneratorState {
|
|
|
|
fast_function,
|
|
|
|
deno_core,
|
2023-06-25 10:36:09 -04:00
|
|
|
result,
|
|
|
|
opctx,
|
|
|
|
fast_api_callback_options,
|
|
|
|
needs_fast_api_callback_options,
|
|
|
|
needs_fast_opctx,
|
2023-06-24 07:54:10 -04:00
|
|
|
..
|
2023-06-25 10:36:09 -04:00
|
|
|
} = 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.
|
2023-06-24 07:54:10 -04:00
|
|
|
|
2023-06-25 10:36:09 -04:00
|
|
|
// 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<_>>();
|
2023-06-24 07:54:10 -04:00
|
|
|
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(
|
2023-06-25 10:36:09 -04:00
|
|
|
&[ Type::V8Value, #( #input_types ),* ],
|
2023-06-24 07:54:10 -04:00
|
|
|
#output_type,
|
|
|
|
Self::#fast_function as *const ::std::ffi::c_void
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
2023-06-25 10:36:09 -04:00
|
|
|
let output_type = output.quote_rust_type(deno_core);
|
|
|
|
let mut types = inputs
|
2023-06-24 07:54:10 -04:00
|
|
|
.iter()
|
2023-06-25 10:36:09 -04:00
|
|
|
.map(|rv| rv.quote_rust_type(deno_core))
|
2023-06-24 07:54:10 -04:00
|
|
|
.collect::<Vec<_>>();
|
2023-06-25 10:36:09 -04:00
|
|
|
|
|
|
|
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!()
|
|
|
|
};
|
2023-06-24 07:54:10 -04:00
|
|
|
|
|
|
|
let fast_fn = quote!(
|
|
|
|
fn #fast_function(
|
|
|
|
_: #deno_core::v8::Local<#deno_core::v8::Object>,
|
|
|
|
#( #names: #types, )*
|
|
|
|
) -> #output_type {
|
2023-06-25 10:36:09 -04:00
|
|
|
#with_fast_api_callback_options
|
|
|
|
#with_opctx
|
|
|
|
let #result = Self::call(#(#call_args as _),*);
|
|
|
|
#handle_error
|
|
|
|
#result
|
2023-06-24 07:54:10 -04:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(Some((fast_definition, fast_fn)))
|
|
|
|
}
|
2023-06-25 10:36:09 -04:00
|
|
|
|
|
|
|
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))
|
|
|
|
}
|