1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-25 00:29:09 -05:00
denoland-deno/ops/op2/dispatch_slow.rs
Matt Mastracci fa935e553a
chore: Ensure copyright line is the first in the file (#19608)
The copyright checker was allowing files with code above the copyright
line in a few places, mainly as a result of IDEs ordering imports
improperly.

This makes the check more robust, and adds a whitelist of valid lines
that may appear before the copyright line.
2023-06-26 09:38:55 -06:00

343 lines
8.7 KiB
Rust

// 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::signature::Special;
use super::MacroConfig;
use super::V8MappingError;
use proc_macro2::TokenStream;
use quote::quote;
pub(crate) fn generate_dispatch_slow(
config: &MacroConfig,
generator_state: &mut GeneratorState,
signature: &ParsedSignature,
) -> Result<TokenStream, V8MappingError> {
let mut output = TokenStream::new();
// Fast ops require the slow op to check op_ctx for the last error
if config.fast && matches!(signature.ret_val, RetVal::Result(_)) {
generator_state.needs_opctx = true;
let throw_exception = throw_exception(generator_state)?;
// If the fast op returned an error, we must throw it rather than doing work.
output.extend(quote!{
// FASTCALL FALLBACK: This is where we pick up 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.
// 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.
if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } {
#throw_exception
}
});
}
for (index, arg) in signature.args.iter().enumerate() {
output.extend(extract_arg(generator_state, index)?);
output.extend(from_arg(generator_state, index, arg)?);
}
output.extend(call(generator_state)?);
output.extend(return_value(generator_state, &signature.ret_val)?);
let with_scope = if generator_state.needs_scope {
with_scope(generator_state)
} else {
quote!()
};
let with_opctx = if generator_state.needs_opctx {
with_opctx(generator_state)
} else {
quote!()
};
let with_retval = if generator_state.needs_retval {
with_retval(generator_state)
} else {
quote!()
};
let with_args = if generator_state.needs_args {
with_fn_args(generator_state)
} else {
quote!()
};
let GeneratorState {
deno_core,
info,
slow_function,
..
} = &generator_state;
Ok(quote! {
pub extern "C" fn #slow_function(#info: *const #deno_core::v8::FunctionCallbackInfo) {
#with_scope
#with_retval
#with_args
#with_opctx
#output
}})
}
fn with_scope(generator_state: &mut GeneratorState) -> TokenStream {
let GeneratorState {
deno_core,
scope,
info,
..
} = &generator_state;
quote!(let #scope = &mut unsafe { #deno_core::v8::CallbackScope::new(&*#info) };)
}
fn with_retval(generator_state: &mut GeneratorState) -> TokenStream {
let GeneratorState {
deno_core,
retval,
info,
..
} = &generator_state;
quote!(let mut #retval = #deno_core::v8::ReturnValue::from_function_callback_info(unsafe { &*#info });)
}
fn with_fn_args(generator_state: &mut GeneratorState) -> TokenStream {
let GeneratorState {
deno_core,
fn_args,
info,
..
} = &generator_state;
quote!(let #fn_args = #deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { &*#info });)
}
fn with_opctx(generator_state: &mut GeneratorState) -> TokenStream {
let GeneratorState {
deno_core,
opctx,
fn_args,
needs_args,
..
} = generator_state;
*needs_args = true;
quote!(let #opctx = unsafe {
&*(#deno_core::v8::Local::<#deno_core::v8::External>::cast(#fn_args.data()).value()
as *const #deno_core::_ops::OpCtx)
};)
}
pub fn extract_arg(
generator_state: &mut GeneratorState,
index: usize,
) -> Result<TokenStream, V8MappingError> {
let GeneratorState { fn_args, .. } = &generator_state;
let arg_ident = generator_state.args.get(index);
Ok(quote!(
let #arg_ident = #fn_args.get(#index as i32);
))
}
pub fn from_arg(
mut generator_state: &mut GeneratorState,
index: usize,
arg: &Arg,
) -> Result<TokenStream, V8MappingError> {
let GeneratorState {
deno_core, args, ..
} = &mut generator_state;
let arg_ident = args.get_mut(index).expect("Argument at index was missing");
let res = match arg {
Arg::Numeric(NumericArg::bool) => quote! {
let #arg_ident = #arg_ident.is_true();
},
Arg::Numeric(NumericArg::u8)
| Arg::Numeric(NumericArg::u16)
| Arg::Numeric(NumericArg::u32) => {
quote! {
let #arg_ident = #deno_core::_ops::to_u32(&#arg_ident) as _;
}
}
Arg::Numeric(NumericArg::i8)
| Arg::Numeric(NumericArg::i16)
| Arg::Numeric(NumericArg::i32)
| Arg::Numeric(NumericArg::__SMI__) => {
quote! {
let #arg_ident = #deno_core::_ops::to_i32(&#arg_ident) as _;
}
}
Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => {
quote! {
let #arg_ident = #deno_core::_ops::to_u64(&#arg_ident) as _;
}
}
Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
quote! {
let #arg_ident = #deno_core::_ops::to_i64(&#arg_ident) as _;
}
}
Arg::OptionNumeric(numeric) => {
// Ends the borrow of generator_state
let arg_ident = arg_ident.clone();
let some = from_arg(generator_state, index, &Arg::Numeric(*numeric))?;
quote! {
let #arg_ident = if #arg_ident.is_null_or_undefined() {
None
} else {
#some
Some(#arg_ident)
};
}
}
Arg::Option(Special::String) => {
quote! {
let #arg_ident = #arg_ident.to_rust_string_lossy();
}
}
Arg::Special(Special::RefStr) => {
quote! {
let #arg_ident = #arg_ident.to_rust_string_lossy();
}
}
_ => return Err(V8MappingError::NoMapping("a slow argument", arg.clone())),
};
Ok(res)
}
pub fn call(
generator_state: &mut GeneratorState,
) -> Result<TokenStream, V8MappingError> {
let GeneratorState { result, .. } = &generator_state;
let mut tokens = TokenStream::new();
for arg in &generator_state.args {
tokens.extend(quote!( #arg , ));
}
Ok(quote! {
let #result = Self::call( #tokens );
})
}
pub fn return_value(
generator_state: &mut GeneratorState,
ret_type: &RetVal,
) -> Result<TokenStream, V8MappingError> {
match ret_type {
RetVal::Infallible(ret_type) => {
return_value_infallible(generator_state, ret_type)
}
RetVal::Result(ret_type) => return_value_result(generator_state, ret_type),
}
}
pub fn return_value_infallible(
generator_state: &mut GeneratorState,
ret_type: &Arg,
) -> Result<TokenStream, V8MappingError> {
let GeneratorState {
result,
retval,
needs_retval,
..
} = generator_state;
let res = match ret_type {
Arg::Void => {
quote! {/* void */}
}
Arg::Numeric(NumericArg::u8)
| Arg::Numeric(NumericArg::u16)
| Arg::Numeric(NumericArg::u32) => {
*needs_retval = true;
quote!(#retval.set_uint32(#result as u32);)
}
Arg::Numeric(NumericArg::i8)
| Arg::Numeric(NumericArg::i16)
| Arg::Numeric(NumericArg::i32) => {
*needs_retval = true;
quote!(#retval.set_int32(#result as i32);)
}
_ => {
return Err(V8MappingError::NoMapping(
"a slow return value",
ret_type.clone(),
))
}
};
Ok(res)
}
fn return_value_result(
generator_state: &mut GeneratorState,
ret_type: &Arg,
) -> Result<TokenStream, V8MappingError> {
let infallible = return_value_infallible(generator_state, ret_type)?;
let exception = throw_exception(generator_state)?;
let GeneratorState { result, .. } = &generator_state;
let tokens = quote!(
match #result {
Ok(#result) => {
#infallible
}
Err(err) => {
#exception
}
};
);
Ok(tokens)
}
/// Generates code to throw an exception, adding required additional dependencies as needed.
fn throw_exception(
generator_state: &mut GeneratorState,
) -> Result<TokenStream, V8MappingError> {
let maybe_scope = if generator_state.needs_scope {
quote!()
} else {
with_scope(generator_state)
};
let maybe_opctx = if generator_state.needs_opctx {
quote!()
} else {
with_opctx(generator_state)
};
let maybe_args = if generator_state.needs_args {
quote!()
} else {
with_fn_args(generator_state)
};
let GeneratorState {
deno_core,
scope,
opctx,
..
} = &generator_state;
Ok(quote! {
#maybe_scope
#maybe_args
#maybe_opctx
let opstate = ::std::cell::RefCell::borrow(&*#opctx.state);
let exception = #deno_core::error::to_v8_error(
#scope,
opstate.get_error_class_fn,
&err,
);
scope.throw_exception(exception);
return;
})
}