2023-01-02 16:00:42 -05:00
|
|
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2022-08-21 08:07:53 -04:00
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
use attrs::Attributes;
|
2023-01-14 23:18:58 -05:00
|
|
|
use optimizer::BailoutReason;
|
|
|
|
use optimizer::Optimizer;
|
2022-03-14 13:44:15 -04:00
|
|
|
use proc_macro::TokenStream;
|
2023-01-14 23:18:58 -05:00
|
|
|
use proc_macro2::Span;
|
|
|
|
use proc_macro2::TokenStream as TokenStream2;
|
|
|
|
use quote::quote;
|
|
|
|
use quote::ToTokens;
|
|
|
|
use syn::parse;
|
|
|
|
use syn::parse_macro_input;
|
|
|
|
use syn::punctuated::Punctuated;
|
|
|
|
use syn::token::Comma;
|
|
|
|
use syn::FnArg;
|
|
|
|
use syn::GenericParam;
|
|
|
|
use syn::Ident;
|
|
|
|
use syn::ItemFn;
|
|
|
|
use syn::Lifetime;
|
|
|
|
use syn::LifetimeDef;
|
2022-11-10 06:53:31 -05:00
|
|
|
|
|
|
|
mod attrs;
|
|
|
|
mod deno;
|
|
|
|
mod fast_call;
|
|
|
|
mod optimizer;
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
const SCOPE_LIFETIME: &str = "'scope";
|
|
|
|
|
|
|
|
/// Add the 'scope lifetime to the function signature.
|
|
|
|
fn add_scope_lifetime(func: &mut ItemFn) {
|
|
|
|
let span = Span::call_site();
|
|
|
|
let lifetime = LifetimeDef::new(Lifetime::new(SCOPE_LIFETIME, span));
|
|
|
|
let generics = &mut func.sig.generics;
|
|
|
|
if !generics.lifetimes().any(|def| *def == lifetime) {
|
|
|
|
generics.params.push(GenericParam::Lifetime(lifetime));
|
2022-03-14 13:44:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
struct Op {
|
|
|
|
orig: ItemFn,
|
|
|
|
item: ItemFn,
|
|
|
|
/// Is this an async op?
|
|
|
|
/// - `async fn`
|
|
|
|
/// - returns a Future
|
|
|
|
is_async: bool,
|
|
|
|
type_params: Punctuated<GenericParam, Comma>,
|
|
|
|
// optimizer: Optimizer,
|
|
|
|
core: TokenStream2,
|
|
|
|
attrs: Attributes,
|
2022-04-01 18:09:21 -04:00
|
|
|
}
|
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
impl Op {
|
|
|
|
fn new(mut item: ItemFn, attrs: Attributes) -> Self {
|
|
|
|
// Preserve the original function. Change the name to `call`.
|
|
|
|
//
|
|
|
|
// impl op_foo {
|
|
|
|
// fn call() {}
|
|
|
|
// ...
|
|
|
|
// }
|
|
|
|
let mut orig = item.clone();
|
|
|
|
orig.sig.ident = Ident::new("call", Span::call_site());
|
|
|
|
|
2022-11-17 20:59:10 -05:00
|
|
|
add_scope_lifetime(&mut item);
|
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
let is_async = item.sig.asyncness.is_some() || is_future(&item.sig.output);
|
|
|
|
let type_params = exclude_lifetime_params(&item.sig.generics.params);
|
2022-11-25 09:47:21 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
let core = quote!(deno_core);
|
|
|
|
#[cfg(not(test))]
|
2022-11-10 06:53:31 -05:00
|
|
|
let core = deno::import();
|
|
|
|
|
|
|
|
Self {
|
|
|
|
orig,
|
|
|
|
item,
|
|
|
|
type_params,
|
|
|
|
is_async,
|
|
|
|
core,
|
|
|
|
attrs,
|
2022-04-01 18:09:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
fn gen(mut self) -> TokenStream2 {
|
|
|
|
let mut optimizer = Optimizer::new();
|
|
|
|
match optimizer.analyze(&mut self) {
|
2022-11-26 23:48:17 -05:00
|
|
|
Err(BailoutReason::MustBeSingleSegment)
|
|
|
|
| Err(BailoutReason::FastUnsupportedParamType) => {
|
2022-11-10 06:53:31 -05:00
|
|
|
optimizer.fast_compatible = false;
|
|
|
|
}
|
2022-11-26 23:48:17 -05:00
|
|
|
_ => {}
|
2022-11-10 06:53:31 -05:00
|
|
|
};
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
let Self {
|
|
|
|
core,
|
|
|
|
item,
|
|
|
|
is_async,
|
|
|
|
orig,
|
|
|
|
attrs,
|
|
|
|
type_params,
|
|
|
|
} = self;
|
|
|
|
let name = &item.sig.ident;
|
|
|
|
let generics = &item.sig.generics;
|
|
|
|
let where_clause = &item.sig.generics.where_clause;
|
|
|
|
|
|
|
|
// First generate fast call bindings to opt-in to error handling in slow call
|
|
|
|
let fast_call::FastImplItems {
|
|
|
|
impl_and_fn,
|
|
|
|
decl,
|
|
|
|
active,
|
|
|
|
} = fast_call::generate(&core, &mut optimizer, &item);
|
|
|
|
|
2023-03-05 03:00:22 -05:00
|
|
|
let docline = format!("Use `{name}::decl()` to get an op-declaration");
|
|
|
|
|
|
|
|
let is_v8 = attrs.is_v8;
|
|
|
|
let is_unstable = attrs.is_unstable;
|
|
|
|
|
|
|
|
if let Some(v8_fn) = attrs.relation {
|
|
|
|
return quote! {
|
|
|
|
#[allow(non_camel_case_types)]
|
|
|
|
#[doc="Auto-generated by `deno_ops`, i.e: `#[op]`"]
|
|
|
|
#[doc=""]
|
|
|
|
#[doc=#docline]
|
|
|
|
#[doc="you can include in a `deno_core::Extension`."]
|
|
|
|
pub struct #name;
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
impl #name {
|
|
|
|
pub fn name() -> &'static str {
|
|
|
|
stringify!(#name)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn v8_fn_ptr #generics () -> #core::v8::FunctionCallback #where_clause {
|
|
|
|
use #core::v8::MapFnTo;
|
|
|
|
#v8_fn::v8_func::<#type_params>.map_fn_to()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn decl #generics () -> #core::OpDecl #where_clause {
|
|
|
|
#core::OpDecl {
|
|
|
|
name: Self::name(),
|
|
|
|
v8_fn_ptr: Self::v8_fn_ptr::<#type_params>(),
|
|
|
|
enabled: true,
|
|
|
|
fast_fn: #decl,
|
|
|
|
is_async: #is_async,
|
|
|
|
is_unstable: #is_unstable,
|
|
|
|
is_v8: #is_v8,
|
2023-03-18 18:30:04 -04:00
|
|
|
force_registration: false,
|
2023-03-05 03:00:22 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
#orig
|
|
|
|
}
|
|
|
|
|
|
|
|
#impl_and_fn
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
let has_fallible_fast_call = active && optimizer.returns_result;
|
|
|
|
|
2023-03-05 06:16:43 -05:00
|
|
|
let v8_body = if is_async {
|
2023-03-05 03:00:22 -05:00
|
|
|
let deferred = attrs.deferred;
|
2022-11-10 06:53:31 -05:00
|
|
|
codegen_v8_async(
|
|
|
|
&core,
|
|
|
|
&item,
|
|
|
|
attrs,
|
|
|
|
item.sig.asyncness.is_some(),
|
2023-03-05 03:00:22 -05:00
|
|
|
deferred,
|
2022-11-10 06:53:31 -05:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
codegen_v8_sync(&core, &item, attrs, has_fallible_fast_call)
|
|
|
|
};
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
// Generate wrapper
|
|
|
|
quote! {
|
|
|
|
#[allow(non_camel_case_types)]
|
|
|
|
#[doc="Auto-generated by `deno_ops`, i.e: `#[op]`"]
|
|
|
|
#[doc=""]
|
|
|
|
#[doc=#docline]
|
|
|
|
#[doc="you can include in a `deno_core::Extension`."]
|
|
|
|
pub struct #name;
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
impl #name {
|
2023-03-31 08:42:14 -04:00
|
|
|
pub const fn name() -> &'static str {
|
2022-11-10 06:53:31 -05:00
|
|
|
stringify!(#name)
|
|
|
|
}
|
2022-09-22 22:55:37 -04:00
|
|
|
|
2023-03-31 08:42:14 -04:00
|
|
|
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
|
|
|
pub extern "C" fn v8_fn_ptr #generics (info: *const #core::v8::FunctionCallbackInfo) #where_clause {
|
|
|
|
let info = unsafe { &*info };
|
|
|
|
let scope = &mut unsafe { #core::v8::CallbackScope::new(info) };
|
|
|
|
let args = #core::v8::FunctionCallbackArguments::from_function_callback_info(info);
|
|
|
|
let rv = #core::v8::ReturnValue::from_function_callback_info(info);
|
|
|
|
Self::v8_func::<#type_params>(scope, args, rv);
|
2022-11-10 06:53:31 -05:00
|
|
|
}
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2023-03-31 08:42:14 -04:00
|
|
|
pub const fn decl #generics () -> #core::OpDecl #where_clause {
|
2022-11-10 06:53:31 -05:00
|
|
|
#core::OpDecl {
|
|
|
|
name: Self::name(),
|
2023-03-31 08:42:14 -04:00
|
|
|
v8_fn_ptr: Self::v8_fn_ptr::<#type_params> as _,
|
2022-11-10 06:53:31 -05:00
|
|
|
enabled: true,
|
|
|
|
fast_fn: #decl,
|
|
|
|
is_async: #is_async,
|
|
|
|
is_unstable: #is_unstable,
|
|
|
|
is_v8: #is_v8,
|
2023-03-18 18:30:04 -04:00
|
|
|
force_registration: false,
|
2022-11-10 06:53:31 -05:00
|
|
|
}
|
|
|
|
}
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
#[inline]
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
#orig
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
pub fn v8_func #generics (
|
|
|
|
scope: &mut #core::v8::HandleScope<'scope>,
|
|
|
|
args: #core::v8::FunctionCallbackArguments,
|
|
|
|
mut rv: #core::v8::ReturnValue,
|
|
|
|
) #where_clause {
|
|
|
|
#v8_body
|
2022-03-15 18:43:17 -04:00
|
|
|
}
|
2022-03-14 13:44:15 -04:00
|
|
|
}
|
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
#impl_and_fn
|
2022-03-14 13:44:15 -04:00
|
|
|
}
|
2022-11-10 06:53:31 -05:00
|
|
|
}
|
|
|
|
}
|
2022-08-21 08:07:53 -04:00
|
|
|
|
2022-11-10 06:53:31 -05:00
|
|
|
#[proc_macro_attribute]
|
|
|
|
pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|
|
|
let margs = parse_macro_input!(attr as Attributes);
|
|
|
|
let func = parse::<ItemFn>(item).expect("expected a function");
|
|
|
|
let op = Op::new(func, margs);
|
|
|
|
op.gen().into()
|
2022-03-14 13:44:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Generate the body of a v8 func for an async op
|
2022-05-12 13:06:42 -04:00
|
|
|
fn codegen_v8_async(
|
|
|
|
core: &TokenStream2,
|
|
|
|
f: &syn::ItemFn,
|
2022-11-10 06:53:31 -05:00
|
|
|
margs: Attributes,
|
2022-06-07 22:28:26 -04:00
|
|
|
asyncness: bool,
|
2022-09-06 13:38:37 -04:00
|
|
|
deferred: bool,
|
2023-03-05 06:16:43 -05:00
|
|
|
) -> TokenStream2 {
|
2022-11-10 06:53:31 -05:00
|
|
|
let Attributes { is_v8, .. } = margs;
|
2022-06-16 11:04:55 -04:00
|
|
|
let special_args = f
|
|
|
|
.sig
|
|
|
|
.inputs
|
|
|
|
.iter()
|
|
|
|
.map_while(|a| {
|
2022-12-05 11:10:22 -05:00
|
|
|
(if is_v8 { scope_arg(a) } else { None })
|
|
|
|
.or_else(|| rc_refcell_opstate_arg(a))
|
2022-06-16 11:04:55 -04:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let rust_i0 = special_args.len();
|
|
|
|
let args_head = special_args.into_iter().collect::<TokenStream2>();
|
|
|
|
|
2023-03-27 12:03:07 -04:00
|
|
|
let (arg_decls, args_tail, _) = codegen_args(core, f, rust_i0, 1, asyncness);
|
2022-06-07 05:25:10 -04:00
|
|
|
let type_params = exclude_lifetime_params(&f.sig.generics.params);
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2022-06-16 11:04:55 -04:00
|
|
|
let (pre_result, mut result_fut) = match asyncness {
|
2022-06-07 22:28:26 -04:00
|
|
|
true => (
|
|
|
|
quote! {},
|
2022-06-16 11:04:55 -04:00
|
|
|
quote! { Self::call::<#type_params>(#args_head #args_tail).await; },
|
2022-06-07 22:28:26 -04:00
|
|
|
),
|
|
|
|
false => (
|
|
|
|
quote! { let result_fut = Self::call::<#type_params>(#args_head #args_tail); },
|
2022-06-16 11:04:55 -04:00
|
|
|
quote! { result_fut.await; },
|
2022-06-07 22:28:26 -04:00
|
|
|
),
|
|
|
|
};
|
2022-05-12 13:13:25 -04:00
|
|
|
let result_wrapper = match is_result(&f.sig.output) {
|
2022-06-16 11:04:55 -04:00
|
|
|
true => {
|
|
|
|
// Support `Result<impl Future<Output = Result<T, AnyError>> + 'static, AnyError>`
|
|
|
|
if !asyncness {
|
|
|
|
result_fut = quote! { result_fut; };
|
|
|
|
quote! {
|
|
|
|
let result = match result {
|
|
|
|
Ok(fut) => fut.await,
|
feat(core): Reland support for async ops in realms (#17204)
Currently realms are supported on `deno_core`, but there was no support
for async ops anywhere other than the main realm. The main issue is that
the `js_recv_cb` callback, which resolves promises corresponding to
async ops, was only set for the main realm, so async ops in other realms
would never resolve. Furthermore, promise ID's are specific to each
realm, which meant that async ops from other realms would result in a
wrong promise from the main realm being resolved.
This change takes the `ContextState` struct added in #17050, and adds to
it a `js_recv_cb` callback for each realm. Combined with the fact that
that same PR also added a list of known realms to `JsRuntimeState`, and
that #17174 made `OpCtx` instances realm-specific and had them include
an index into that list of known realms, this makes it possible to know
the current realm in the `queue_async_op` and `queue_fast_async_op`
methods, and therefore to send the results of promises for each realm to
that realm, and prevent the ID's from getting mixed up.
Additionally, since promise ID's are no longer unique to the isolate,
having a single set of unrefed ops doesn't work. This change therefore
also moves `unrefed_ops` from `JsRuntimeState` to `ContextState`, and
adds the lengths of the unrefed op sets for all known realms to get the
total number of unrefed ops to compare in the event loop.
This PR is a reland of #14734 after it was reverted in #16366, except
that `ContextState` and `JsRuntimeState::known_realms` were previously
relanded in #17050. Another significant difference with the original PR
is passing around an index into `JsRuntimeState::known_realms` instead
of a `v8::Global<v8::Context>` to identify the realm, because async op
queuing in fast calls cannot call into V8, and therefore cannot have
access to V8 globals. This also simplified the implementation of
`resolve_async_ops`.
Co-authored-by: Luis Malheiro <luismalheiro@gmail.com>
2023-01-14 08:40:16 -05:00
|
|
|
Err(e) => return (realm_idx, promise_id, op_id, #core::_ops::to_op_result::<()>(get_class, Err(e))),
|
2022-06-16 11:04:55 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
quote! {}
|
|
|
|
}
|
|
|
|
}
|
2022-05-12 13:13:25 -04:00
|
|
|
false => quote! { let result = Ok(result); },
|
|
|
|
};
|
|
|
|
|
2023-03-05 06:16:43 -05:00
|
|
|
quote! {
|
|
|
|
use #core::futures::FutureExt;
|
|
|
|
// SAFETY: #core guarantees args.data() is a v8 External pointing to an OpCtx for the isolates lifetime
|
|
|
|
let ctx = unsafe {
|
|
|
|
&*(#core::v8::Local::<#core::v8::External>::cast(args.data()).value()
|
|
|
|
as *const #core::_ops::OpCtx)
|
|
|
|
};
|
|
|
|
let op_id = ctx.id;
|
|
|
|
let realm_idx = ctx.realm_idx;
|
|
|
|
|
|
|
|
let promise_id = args.get(0);
|
|
|
|
let promise_id = #core::v8::Local::<#core::v8::Integer>::try_from(promise_id)
|
|
|
|
.map(|l| l.value() as #core::PromiseId)
|
|
|
|
.map_err(#core::anyhow::Error::from);
|
|
|
|
// Fail if promise id invalid (not an int)
|
|
|
|
let promise_id: #core::PromiseId = match promise_id {
|
|
|
|
Ok(promise_id) => promise_id,
|
|
|
|
Err(err) => {
|
|
|
|
#core::_ops::throw_type_error(scope, format!("invalid promise id: {}", err));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2023-03-05 06:16:43 -05:00
|
|
|
#arg_decls
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2023-03-05 06:16:43 -05:00
|
|
|
// Track async call & get copy of get_error_class_fn
|
|
|
|
let get_class = {
|
|
|
|
let state = ::std::cell::RefCell::borrow(&ctx.state);
|
|
|
|
state.tracker.track_async(op_id);
|
|
|
|
state.get_error_class_fn
|
|
|
|
};
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2023-03-05 06:16:43 -05:00
|
|
|
#pre_result
|
2023-04-13 08:32:47 -04:00
|
|
|
let maybe_response = #core::_ops::queue_async_op(ctx, scope, #deferred, async move {
|
2023-03-05 06:16:43 -05:00
|
|
|
let result = #result_fut
|
|
|
|
#result_wrapper
|
|
|
|
(realm_idx, promise_id, op_id, #core::_ops::to_op_result(get_class, result))
|
|
|
|
});
|
2023-04-13 08:32:47 -04:00
|
|
|
|
|
|
|
if let Some(response) = maybe_response {
|
|
|
|
rv.set(response);
|
|
|
|
}
|
2023-03-05 06:16:43 -05:00
|
|
|
}
|
2022-03-14 13:44:15 -04:00
|
|
|
}
|
|
|
|
|
2022-06-16 11:04:55 -04:00
|
|
|
fn scope_arg(arg: &FnArg) -> Option<TokenStream2> {
|
|
|
|
if is_handle_scope(arg) {
|
|
|
|
Some(quote! { scope, })
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn opstate_arg(arg: &FnArg) -> Option<TokenStream2> {
|
|
|
|
match arg {
|
|
|
|
arg if is_rc_refcell_opstate(arg) => Some(quote! { ctx.state.clone(), }),
|
|
|
|
arg if is_mut_ref_opstate(arg) => {
|
2022-10-16 09:11:45 -04:00
|
|
|
Some(quote! { &mut std::cell::RefCell::borrow_mut(&ctx.state), })
|
2022-06-16 11:04:55 -04:00
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-05 11:10:22 -05:00
|
|
|
fn rc_refcell_opstate_arg(arg: &FnArg) -> Option<TokenStream2> {
|
|
|
|
match arg {
|
|
|
|
arg if is_rc_refcell_opstate(arg) => Some(quote! { ctx.state.clone(), }),
|
|
|
|
arg if is_mut_ref_opstate(arg) => Some(
|
|
|
|
quote! { compile_error!("mutable opstate is not supported in async ops"), },
|
|
|
|
),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
/// Generate the body of a v8 func for a sync op
|
2022-05-12 13:06:42 -04:00
|
|
|
fn codegen_v8_sync(
|
|
|
|
core: &TokenStream2,
|
|
|
|
f: &syn::ItemFn,
|
2022-11-10 06:53:31 -05:00
|
|
|
margs: Attributes,
|
2022-09-22 22:55:37 -04:00
|
|
|
has_fallible_fast_call: bool,
|
2023-03-05 06:16:43 -05:00
|
|
|
) -> TokenStream2 {
|
2022-11-10 06:53:31 -05:00
|
|
|
let Attributes { is_v8, .. } = margs;
|
2022-05-12 13:06:42 -04:00
|
|
|
let special_args = f
|
|
|
|
.sig
|
|
|
|
.inputs
|
|
|
|
.iter()
|
|
|
|
.map_while(|a| {
|
|
|
|
(if is_v8 { scope_arg(a) } else { None }).or_else(|| opstate_arg(a))
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let rust_i0 = special_args.len();
|
|
|
|
let args_head = special_args.into_iter().collect::<TokenStream2>();
|
2023-03-05 06:16:43 -05:00
|
|
|
let (arg_decls, args_tail, _) = codegen_args(core, f, rust_i0, 0, false);
|
2022-03-14 13:44:15 -04:00
|
|
|
let ret = codegen_sync_ret(core, &f.sig.output);
|
2022-06-07 05:25:10 -04:00
|
|
|
let type_params = exclude_lifetime_params(&f.sig.generics.params);
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2022-09-22 22:55:37 -04:00
|
|
|
let fast_error_handler = if has_fallible_fast_call {
|
|
|
|
quote! {
|
|
|
|
{
|
2022-10-16 09:11:45 -04:00
|
|
|
let op_state = &mut std::cell::RefCell::borrow_mut(&ctx.state);
|
2022-09-22 22:55:37 -04:00
|
|
|
if let Some(err) = op_state.last_fast_op_error.take() {
|
|
|
|
let exception = #core::error::to_v8_error(scope, op_state.get_error_class_fn, &err);
|
|
|
|
scope.throw_exception(exception);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
quote! {}
|
|
|
|
};
|
|
|
|
|
2023-03-05 06:16:43 -05:00
|
|
|
quote! {
|
|
|
|
// SAFETY: #core guarantees args.data() is a v8 External pointing to an OpCtx for the isolates lifetime
|
|
|
|
let ctx = unsafe {
|
|
|
|
&*(#core::v8::Local::<#core::v8::External>::cast(args.data()).value()
|
|
|
|
as *const #core::_ops::OpCtx)
|
|
|
|
};
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2023-03-05 06:16:43 -05:00
|
|
|
#fast_error_handler
|
|
|
|
#arg_decls
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2023-03-05 06:16:43 -05:00
|
|
|
let result = Self::call::<#type_params>(#args_head #args_tail);
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2023-03-05 06:16:43 -05:00
|
|
|
// use RefCell::borrow instead of state.borrow to avoid clash with std::borrow::Borrow
|
|
|
|
let op_state = ::std::cell::RefCell::borrow(&*ctx.state);
|
|
|
|
op_state.tracker.track_sync(ctx.id);
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2023-03-05 06:16:43 -05:00
|
|
|
#ret
|
|
|
|
}
|
2022-03-14 13:44:15 -04:00
|
|
|
}
|
|
|
|
|
2022-10-28 07:20:17 -04:00
|
|
|
/// (full declarations, idents, v8 argument count)
|
|
|
|
type ArgumentDecl = (TokenStream2, TokenStream2, usize);
|
|
|
|
|
2022-03-14 18:38:53 -04:00
|
|
|
fn codegen_args(
|
|
|
|
core: &TokenStream2,
|
|
|
|
f: &syn::ItemFn,
|
|
|
|
rust_i0: usize, // Index of first generic arg in rust
|
|
|
|
v8_i0: usize, // Index of first generic arg in v8/js
|
2023-01-15 02:40:01 -05:00
|
|
|
asyncness: bool,
|
2022-10-28 07:20:17 -04:00
|
|
|
) -> ArgumentDecl {
|
2022-03-14 18:38:53 -04:00
|
|
|
let inputs = &f.sig.inputs.iter().skip(rust_i0).enumerate();
|
|
|
|
let ident_seq: TokenStream2 = inputs
|
|
|
|
.clone()
|
|
|
|
.map(|(i, _)| format!("arg_{i}"))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(", ")
|
|
|
|
.parse()
|
|
|
|
.unwrap();
|
|
|
|
let decls: TokenStream2 = inputs
|
|
|
|
.clone()
|
|
|
|
.map(|(i, arg)| {
|
2023-01-15 02:40:01 -05:00
|
|
|
codegen_arg(core, arg, format!("arg_{i}").as_ref(), v8_i0 + i, asyncness)
|
2022-03-14 18:38:53 -04:00
|
|
|
})
|
|
|
|
.collect();
|
2022-10-28 07:20:17 -04:00
|
|
|
(decls, ident_seq, inputs.len())
|
2022-03-14 18:38:53 -04:00
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
fn codegen_arg(
|
|
|
|
core: &TokenStream2,
|
|
|
|
arg: &syn::FnArg,
|
|
|
|
name: &str,
|
2022-03-14 18:38:53 -04:00
|
|
|
idx: usize,
|
2023-01-15 02:40:01 -05:00
|
|
|
asyncness: bool,
|
2022-03-14 13:44:15 -04:00
|
|
|
) -> TokenStream2 {
|
|
|
|
let ident = quote::format_ident!("{name}");
|
2022-08-30 05:01:14 -04:00
|
|
|
let (pat, ty) = match arg {
|
2022-09-01 06:23:06 -04:00
|
|
|
syn::FnArg::Typed(pat) => {
|
2022-11-27 08:54:28 -05:00
|
|
|
if is_optional_fast_callback_option(&pat.ty)
|
|
|
|
|| is_optional_wasm_memory(&pat.ty)
|
|
|
|
{
|
2022-09-01 06:23:06 -04:00
|
|
|
return quote! { let #ident = None; };
|
|
|
|
}
|
|
|
|
(&pat.pat, &pat.ty)
|
|
|
|
}
|
2022-03-14 13:44:15 -04:00
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
// Fast path if arg should be skipped
|
|
|
|
if matches!(**pat, syn::Pat::Wild(_)) {
|
|
|
|
return quote! { let #ident = (); };
|
|
|
|
}
|
2022-08-30 05:01:14 -04:00
|
|
|
// Fast path for `String`
|
2023-03-03 08:34:10 -05:00
|
|
|
if let Some(is_ref) = is_string(&**ty) {
|
|
|
|
let ref_block = if is_ref {
|
|
|
|
quote! { let #ident = #ident.as_ref(); }
|
|
|
|
} else {
|
|
|
|
quote! {}
|
|
|
|
};
|
2022-08-30 05:01:14 -04:00
|
|
|
return quote! {
|
|
|
|
let #ident = match #core::v8::Local::<#core::v8::String>::try_from(args.get(#idx as i32)) {
|
|
|
|
Ok(v8_string) => #core::serde_v8::to_utf8(v8_string, scope),
|
|
|
|
Err(_) => {
|
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected string at position {}", #idx));
|
|
|
|
}
|
|
|
|
};
|
2023-03-03 08:34:10 -05:00
|
|
|
#ref_block
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// Fast path for `Cow<'_, str>`
|
|
|
|
if is_cow_str(&**ty) {
|
|
|
|
return quote! {
|
|
|
|
let #ident = match #core::v8::Local::<#core::v8::String>::try_from(args.get(#idx as i32)) {
|
|
|
|
Ok(v8_string) => ::std::borrow::Cow::Owned(#core::serde_v8::to_utf8(v8_string, scope)),
|
|
|
|
Err(_) => {
|
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected string at position {}", #idx));
|
|
|
|
}
|
|
|
|
};
|
2022-08-30 05:01:14 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
// Fast path for `Option<String>`
|
|
|
|
if is_option_string(&**ty) {
|
|
|
|
return quote! {
|
|
|
|
let #ident = match #core::v8::Local::<#core::v8::String>::try_from(args.get(#idx as i32)) {
|
|
|
|
Ok(v8_string) => Some(#core::serde_v8::to_utf8(v8_string, scope)),
|
|
|
|
Err(_) => None
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
2022-09-07 06:51:47 -04:00
|
|
|
// Fast path for &/&mut [u8] and &/&mut [u32]
|
2022-09-17 07:18:15 -04:00
|
|
|
match is_ref_slice(&**ty) {
|
|
|
|
None => {}
|
|
|
|
Some(SliceType::U32Mut) => {
|
2023-01-15 02:40:01 -05:00
|
|
|
assert!(!asyncness, "Memory slices are not allowed in async ops");
|
2022-09-17 07:18:15 -04:00
|
|
|
let blck = codegen_u32_mut_slice(core, idx);
|
|
|
|
return quote! {
|
|
|
|
let #ident = #blck;
|
|
|
|
};
|
|
|
|
}
|
2023-02-17 08:18:09 -05:00
|
|
|
Some(SliceType::F64Mut) => {
|
|
|
|
assert!(!asyncness, "Memory slices are not allowed in async ops");
|
|
|
|
let blck = codegen_f64_mut_slice(core, idx);
|
|
|
|
return quote! {
|
|
|
|
let #ident = #blck;
|
|
|
|
};
|
|
|
|
}
|
2022-09-17 07:18:15 -04:00
|
|
|
Some(_) => {
|
2023-01-15 02:40:01 -05:00
|
|
|
assert!(!asyncness, "Memory slices are not allowed in async ops");
|
2022-09-17 07:18:15 -04:00
|
|
|
let blck = codegen_u8_slice(core, idx);
|
|
|
|
return quote! {
|
|
|
|
let #ident = #blck;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2022-11-26 09:37:43 -05:00
|
|
|
// Fast path for `*const u8`
|
|
|
|
if is_ptr_u8(&**ty) {
|
|
|
|
let blk = codegen_u8_ptr(core, idx);
|
|
|
|
return quote! {
|
|
|
|
let #ident = #blk;
|
|
|
|
};
|
|
|
|
}
|
2023-02-22 12:32:38 -05:00
|
|
|
// Fast path for `*const c_void` and `*mut c_void`
|
|
|
|
if is_ptr_cvoid(&**ty) {
|
|
|
|
let blk = codegen_cvoid_ptr(core, idx);
|
|
|
|
return quote! {
|
|
|
|
let #ident = #blk;
|
|
|
|
};
|
|
|
|
}
|
2022-09-17 07:18:15 -04:00
|
|
|
// Otherwise deserialize it via serde_v8
|
|
|
|
quote! {
|
|
|
|
let #ident = args.get(#idx as i32);
|
|
|
|
let #ident = match #core::serde_v8::from_v8(scope, #ident) {
|
|
|
|
Ok(v) => v,
|
|
|
|
Err(err) => {
|
|
|
|
let msg = format!("Error parsing args at position {}: {}", #idx, #core::anyhow::Error::from(err));
|
|
|
|
return #core::_ops::throw_type_error(scope, msg);
|
|
|
|
}
|
2022-09-07 06:51:47 -04:00
|
|
|
};
|
2022-09-17 07:18:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn codegen_u8_slice(core: &TokenStream2, idx: usize) -> TokenStream2 {
|
|
|
|
quote! {{
|
|
|
|
let value = args.get(#idx as i32);
|
|
|
|
match #core::v8::Local::<#core::v8::ArrayBuffer>::try_from(value) {
|
|
|
|
Ok(b) => {
|
2022-11-11 09:37:18 -05:00
|
|
|
let byte_length = b.byte_length();
|
2022-11-26 10:35:46 -05:00
|
|
|
if let Some(data) = b.data() {
|
|
|
|
let store = data.cast::<u8>().as_ptr();
|
|
|
|
// SAFETY: rust guarantees that lifetime of slice is no longer than the call.
|
|
|
|
unsafe { ::std::slice::from_raw_parts_mut(store, byte_length) }
|
|
|
|
} else {
|
|
|
|
&mut []
|
|
|
|
}
|
2022-09-17 07:18:15 -04:00
|
|
|
},
|
|
|
|
Err(_) => {
|
2022-09-07 06:51:47 -04:00
|
|
|
if let Ok(view) = #core::v8::Local::<#core::v8::ArrayBufferView>::try_from(value) {
|
2022-11-11 09:37:18 -05:00
|
|
|
let len = view.byte_length();
|
|
|
|
let offset = view.byte_offset();
|
2022-09-07 06:51:47 -04:00
|
|
|
let buffer = match view.buffer(scope) {
|
|
|
|
Some(v) => v,
|
|
|
|
None => {
|
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx));
|
|
|
|
}
|
|
|
|
};
|
2022-11-26 10:35:46 -05:00
|
|
|
if let Some(data) = buffer.data() {
|
|
|
|
let store = data.cast::<u8>().as_ptr();
|
|
|
|
// SAFETY: rust guarantees that lifetime of slice is no longer than the call.
|
|
|
|
unsafe { ::std::slice::from_raw_parts_mut(store.add(offset), len) }
|
|
|
|
} else {
|
|
|
|
&mut []
|
|
|
|
}
|
2022-09-07 06:51:47 -04:00
|
|
|
} else {
|
2022-09-17 07:18:15 -04:00
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx));
|
2022-09-07 06:51:47 -04:00
|
|
|
}
|
2022-09-17 07:18:15 -04:00
|
|
|
}
|
|
|
|
}}
|
2022-09-07 06:51:47 -04:00
|
|
|
}
|
2022-09-17 07:18:15 -04:00
|
|
|
}
|
|
|
|
|
2022-11-26 09:37:43 -05:00
|
|
|
fn codegen_u8_ptr(core: &TokenStream2, idx: usize) -> TokenStream2 {
|
|
|
|
quote! {{
|
|
|
|
let value = args.get(#idx as i32);
|
|
|
|
match #core::v8::Local::<#core::v8::ArrayBuffer>::try_from(value) {
|
2022-11-26 10:35:46 -05:00
|
|
|
Ok(b) => {
|
|
|
|
if let Some(data) = b.data() {
|
|
|
|
data.cast::<u8>().as_ptr()
|
|
|
|
} else {
|
|
|
|
std::ptr::null::<u8>()
|
|
|
|
}
|
|
|
|
},
|
2022-11-26 09:37:43 -05:00
|
|
|
Err(_) => {
|
|
|
|
if let Ok(view) = #core::v8::Local::<#core::v8::ArrayBufferView>::try_from(value) {
|
|
|
|
let offset = view.byte_offset();
|
|
|
|
let buffer = match view.buffer(scope) {
|
|
|
|
Some(v) => v,
|
|
|
|
None => {
|
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx));
|
|
|
|
}
|
|
|
|
};
|
2022-11-26 10:35:46 -05:00
|
|
|
let store = if let Some(data) = buffer.data() {
|
|
|
|
data.cast::<u8>().as_ptr()
|
|
|
|
} else {
|
|
|
|
std::ptr::null_mut::<u8>()
|
|
|
|
};
|
2022-11-26 09:37:43 -05:00
|
|
|
unsafe { store.add(offset) }
|
|
|
|
} else {
|
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2023-02-22 12:32:38 -05:00
|
|
|
fn codegen_cvoid_ptr(core: &TokenStream2, idx: usize) -> TokenStream2 {
|
|
|
|
quote! {{
|
|
|
|
let value = args.get(#idx as i32);
|
|
|
|
if value.is_null() {
|
|
|
|
std::ptr::null_mut()
|
|
|
|
} else if let Ok(b) = #core::v8::Local::<#core::v8::External>::try_from(value) {
|
|
|
|
b.value()
|
|
|
|
} else {
|
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected External at position {}", #idx));
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2022-09-17 07:18:15 -04:00
|
|
|
fn codegen_u32_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 {
|
2022-03-14 13:44:15 -04:00
|
|
|
quote! {
|
2022-09-17 07:18:15 -04:00
|
|
|
if let Ok(view) = #core::v8::Local::<#core::v8::Uint32Array>::try_from(args.get(#idx as i32)) {
|
|
|
|
let (offset, len) = (view.byte_offset(), view.byte_length());
|
|
|
|
let buffer = match view.buffer(scope) {
|
|
|
|
Some(v) => v,
|
|
|
|
None => {
|
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected Uint32Array at position {}", #idx));
|
|
|
|
}
|
|
|
|
};
|
2022-11-26 10:35:46 -05:00
|
|
|
if let Some(data) = buffer.data() {
|
|
|
|
let store = data.cast::<u8>().as_ptr();
|
|
|
|
// SAFETY: buffer from Uint32Array. Rust guarantees that lifetime of slice is no longer than the call.
|
|
|
|
unsafe { ::std::slice::from_raw_parts_mut(store.add(offset) as *mut u32, len / 4) }
|
|
|
|
} else {
|
|
|
|
&mut []
|
|
|
|
}
|
2022-09-17 07:18:15 -04:00
|
|
|
} else {
|
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected Uint32Array at position {}", #idx));
|
|
|
|
}
|
2022-03-14 13:44:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-17 08:18:09 -05:00
|
|
|
fn codegen_f64_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 {
|
|
|
|
quote! {
|
|
|
|
if let Ok(view) = #core::v8::Local::<#core::v8::Float64Array>::try_from(args.get(#idx as i32)) {
|
|
|
|
let (offset, len) = (view.byte_offset(), view.byte_length());
|
|
|
|
let buffer = match view.buffer(scope) {
|
|
|
|
Some(v) => v,
|
|
|
|
None => {
|
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected Float64Array at position {}", #idx));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if let Some(data) = buffer.data() {
|
|
|
|
let store = data.cast::<u8>().as_ptr();
|
|
|
|
unsafe { ::std::slice::from_raw_parts_mut(store.add(offset) as *mut f64, len / 8) }
|
|
|
|
} else {
|
|
|
|
&mut []
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return #core::_ops::throw_type_error(scope, format!("Expected Float64Array at position {}", #idx));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
fn codegen_sync_ret(
|
|
|
|
core: &TokenStream2,
|
|
|
|
output: &syn::ReturnType,
|
|
|
|
) -> TokenStream2 {
|
2022-05-12 15:51:57 -04:00
|
|
|
if is_void(output) {
|
|
|
|
return quote! {};
|
|
|
|
}
|
2022-03-14 13:44:15 -04:00
|
|
|
|
2022-07-01 13:29:24 -04:00
|
|
|
if is_u32_rv(output) {
|
|
|
|
return quote! {
|
|
|
|
rv.set_uint32(result as u32);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
// Optimize Result<(), Err> to skip serde_v8 when Ok(...)
|
2022-05-12 15:51:57 -04:00
|
|
|
let ok_block = if is_unit_result(output) {
|
2022-03-14 13:44:15 -04:00
|
|
|
quote! {}
|
2022-07-01 13:29:24 -04:00
|
|
|
} else if is_u32_rv_result(output) {
|
|
|
|
quote! {
|
|
|
|
rv.set_uint32(result as u32);
|
|
|
|
}
|
2023-02-22 12:32:38 -05:00
|
|
|
} else if is_ptr_cvoid(output) || is_ptr_cvoid_rv(output) {
|
|
|
|
quote! {
|
|
|
|
if result.is_null() {
|
|
|
|
// External canot contain a null pointer, null pointers are instead represented as null.
|
|
|
|
rv.set_null();
|
|
|
|
} else {
|
|
|
|
rv.set(v8::External::new(scope, result as *mut ::std::ffi::c_void).into());
|
|
|
|
}
|
|
|
|
}
|
2022-03-14 13:44:15 -04:00
|
|
|
} else {
|
|
|
|
quote! {
|
2022-05-12 15:51:57 -04:00
|
|
|
match #core::serde_v8::to_v8(scope, result) {
|
2022-03-19 10:59:44 -04:00
|
|
|
Ok(ret) => rv.set(ret),
|
|
|
|
Err(err) => #core::_ops::throw_type_error(
|
|
|
|
scope,
|
|
|
|
format!("Error serializing return: {}", #core::anyhow::Error::from(err)),
|
|
|
|
),
|
|
|
|
};
|
2022-03-14 13:44:15 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-05-12 15:51:57 -04:00
|
|
|
if !is_result(output) {
|
|
|
|
return ok_block;
|
|
|
|
}
|
2022-05-12 13:13:25 -04:00
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
quote! {
|
|
|
|
match result {
|
2022-05-12 15:51:57 -04:00
|
|
|
Ok(result) => {
|
2022-03-14 13:44:15 -04:00
|
|
|
#ok_block
|
|
|
|
},
|
|
|
|
Err(err) => {
|
2022-08-11 05:57:20 -04:00
|
|
|
let exception = #core::error::to_v8_error(scope, op_state.get_error_class_fn, &err);
|
|
|
|
scope.throw_exception(exception);
|
2022-03-14 13:44:15 -04:00
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-12 15:51:57 -04:00
|
|
|
fn is_void(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(ty).is_empty()
|
|
|
|
}
|
|
|
|
|
2022-05-12 13:13:25 -04:00
|
|
|
fn is_result(ty: impl ToTokens) -> bool {
|
|
|
|
let tokens = tokens(ty);
|
|
|
|
if tokens.trim_start_matches("-> ").starts_with("Result <") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Detect `io::Result<...>`, `anyhow::Result<...>`, etc...
|
|
|
|
// i.e: Result aliases/shorthands which are unfortunately "opaque" at macro-time
|
|
|
|
match tokens.find(":: Result <") {
|
|
|
|
Some(idx) => !tokens.split_at(idx).0.contains('<'),
|
|
|
|
None => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-03 08:34:10 -05:00
|
|
|
fn is_string(ty: impl ToTokens) -> Option<bool> {
|
|
|
|
let toks = tokens(ty);
|
|
|
|
if toks == "String" {
|
|
|
|
return Some(false);
|
|
|
|
}
|
|
|
|
if toks == "& str" {
|
|
|
|
return Some(true);
|
|
|
|
}
|
|
|
|
None
|
2022-08-30 05:01:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn is_option_string(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(ty) == "Option < String >"
|
|
|
|
}
|
|
|
|
|
2023-03-03 08:34:10 -05:00
|
|
|
fn is_cow_str(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(&ty).starts_with("Cow <") && tokens(&ty).ends_with("str >")
|
|
|
|
}
|
|
|
|
|
2022-09-07 06:51:47 -04:00
|
|
|
enum SliceType {
|
|
|
|
U8,
|
|
|
|
U8Mut,
|
2022-09-17 07:18:15 -04:00
|
|
|
U32Mut,
|
2023-02-17 08:18:09 -05:00
|
|
|
F64Mut,
|
2022-09-07 06:51:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn is_ref_slice(ty: impl ToTokens) -> Option<SliceType> {
|
|
|
|
if is_u8_slice(&ty) {
|
|
|
|
return Some(SliceType::U8);
|
|
|
|
}
|
|
|
|
if is_u8_slice_mut(&ty) {
|
|
|
|
return Some(SliceType::U8Mut);
|
|
|
|
}
|
2022-09-17 07:18:15 -04:00
|
|
|
if is_u32_slice_mut(&ty) {
|
|
|
|
return Some(SliceType::U32Mut);
|
|
|
|
}
|
2023-02-17 08:18:09 -05:00
|
|
|
if is_f64_slice_mut(&ty) {
|
|
|
|
return Some(SliceType::F64Mut);
|
|
|
|
}
|
2022-09-07 06:51:47 -04:00
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_u8_slice(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(ty) == "& [u8]"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_u8_slice_mut(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(ty) == "& mut [u8]"
|
|
|
|
}
|
|
|
|
|
2022-09-17 07:18:15 -04:00
|
|
|
fn is_u32_slice_mut(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(ty) == "& mut [u32]"
|
|
|
|
}
|
|
|
|
|
2023-02-17 08:18:09 -05:00
|
|
|
fn is_f64_slice_mut(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(ty) == "& mut [f64]"
|
|
|
|
}
|
|
|
|
|
2022-11-26 09:37:43 -05:00
|
|
|
fn is_ptr_u8(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(ty) == "* const u8"
|
|
|
|
}
|
|
|
|
|
2023-02-22 12:32:38 -05:00
|
|
|
fn is_ptr_cvoid(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(&ty) == "* const c_void" || tokens(&ty) == "* mut c_void"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_ptr_cvoid_rv(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(&ty).contains("Result < * const c_void")
|
|
|
|
|| tokens(&ty).contains("Result < * mut c_void")
|
|
|
|
}
|
|
|
|
|
2022-09-01 06:23:06 -04:00
|
|
|
fn is_optional_fast_callback_option(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(&ty).contains("Option < & mut FastApiCallbackOptions")
|
|
|
|
}
|
|
|
|
|
2022-11-27 08:54:28 -05:00
|
|
|
fn is_optional_wasm_memory(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(&ty).contains("Option < & mut [u8]")
|
|
|
|
}
|
|
|
|
|
2022-07-01 13:29:24 -04:00
|
|
|
/// Detects if the type can be set using `rv.set_uint32` fast path
|
|
|
|
fn is_u32_rv(ty: impl ToTokens) -> bool {
|
|
|
|
["u32", "u8", "u16"].iter().any(|&s| tokens(&ty) == s) || is_resource_id(&ty)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Detects if the type is of the format Result<u32/u8/u16, Err>
|
|
|
|
fn is_u32_rv_result(ty: impl ToTokens) -> bool {
|
|
|
|
is_result(&ty)
|
|
|
|
&& (tokens(&ty).contains("Result < u32")
|
|
|
|
|| tokens(&ty).contains("Result < u8")
|
|
|
|
|| tokens(&ty).contains("Result < u16")
|
|
|
|
|| is_resource_id(&ty))
|
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
/// Detects if a type is of the form Result<(), Err>
|
2022-05-12 14:22:42 -04:00
|
|
|
fn is_unit_result(ty: impl ToTokens) -> bool {
|
|
|
|
is_result(&ty) && tokens(&ty).contains("Result < ()")
|
2022-03-14 13:44:15 -04:00
|
|
|
}
|
2022-03-15 19:33:46 -04:00
|
|
|
|
2022-07-01 13:29:24 -04:00
|
|
|
fn is_resource_id(arg: impl ToTokens) -> bool {
|
2023-04-12 21:08:01 -04:00
|
|
|
let re = lazy_regex::regex!(r#": (?:deno_core :: )?ResourceId$"#);
|
|
|
|
re.is_match(&tokens(arg))
|
2022-07-01 13:29:24 -04:00
|
|
|
}
|
|
|
|
|
2022-08-21 08:07:53 -04:00
|
|
|
fn is_mut_ref_opstate(arg: impl ToTokens) -> bool {
|
2023-04-12 21:08:01 -04:00
|
|
|
let re = lazy_regex::regex!(r#": & mut (?:deno_core :: )?OpState$"#);
|
|
|
|
re.is_match(&tokens(arg))
|
2022-03-15 19:33:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn is_rc_refcell_opstate(arg: &syn::FnArg) -> bool {
|
2023-04-12 21:08:01 -04:00
|
|
|
let re =
|
|
|
|
lazy_regex::regex!(r#": Rc < RefCell < (?:deno_core :: )?OpState > >$"#);
|
|
|
|
re.is_match(&tokens(arg))
|
2022-03-15 19:33:46 -04:00
|
|
|
}
|
|
|
|
|
2022-05-12 06:36:09 -04:00
|
|
|
fn is_handle_scope(arg: &syn::FnArg) -> bool {
|
2023-04-12 21:08:01 -04:00
|
|
|
let re = lazy_regex::regex!(
|
|
|
|
r#": & mut (?:deno_core :: )?v8 :: HandleScope(?: < '\w+ >)?$"#
|
|
|
|
);
|
|
|
|
re.is_match(&tokens(arg))
|
2022-05-12 06:36:09 -04:00
|
|
|
}
|
|
|
|
|
2022-06-07 22:28:26 -04:00
|
|
|
fn is_future(ty: impl ToTokens) -> bool {
|
|
|
|
tokens(&ty).contains("impl Future < Output =")
|
|
|
|
}
|
|
|
|
|
2022-03-15 19:33:46 -04:00
|
|
|
fn tokens(x: impl ToTokens) -> String {
|
|
|
|
x.to_token_stream().to_string()
|
|
|
|
}
|
2022-06-07 05:25:10 -04:00
|
|
|
|
|
|
|
fn exclude_lifetime_params(
|
|
|
|
generic_params: &Punctuated<GenericParam, Comma>,
|
|
|
|
) -> Punctuated<GenericParam, Comma> {
|
|
|
|
generic_params
|
|
|
|
.iter()
|
|
|
|
.filter(|t| !tokens(t).starts_with('\''))
|
|
|
|
.cloned()
|
|
|
|
.collect::<Punctuated<GenericParam, Comma>>()
|
|
|
|
}
|
2022-11-26 22:40:31 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-01-14 23:18:58 -05:00
|
|
|
use crate::Attributes;
|
|
|
|
use crate::Op;
|
2023-04-13 08:32:47 -04:00
|
|
|
use pretty_assertions::assert_eq;
|
2022-11-26 22:40:31 -05:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
#[testing_macros::fixture("optimizer_tests/**/*.rs")]
|
|
|
|
fn test_codegen(input: PathBuf) {
|
|
|
|
let update_expected = std::env::var("UPDATE_EXPECTED").is_ok();
|
|
|
|
|
|
|
|
let source =
|
|
|
|
std::fs::read_to_string(&input).expect("Failed to read test file");
|
|
|
|
|
|
|
|
let mut attrs = Attributes::default();
|
|
|
|
if source.contains("// @test-attr:fast") {
|
|
|
|
attrs.must_be_fast = true;
|
|
|
|
}
|
2022-11-27 08:54:28 -05:00
|
|
|
if source.contains("// @test-attr:wasm") {
|
|
|
|
attrs.is_wasm = true;
|
|
|
|
attrs.must_be_fast = true;
|
|
|
|
}
|
2022-11-26 22:40:31 -05:00
|
|
|
|
|
|
|
let item = syn::parse_str(&source).expect("Failed to parse test file");
|
|
|
|
let op = Op::new(item, attrs);
|
|
|
|
|
|
|
|
let expected = std::fs::read_to_string(input.with_extension("out"))
|
|
|
|
.expect("Failed to read expected output file");
|
|
|
|
|
|
|
|
let actual = op.gen();
|
|
|
|
// Validate syntax tree.
|
|
|
|
let tree = syn::parse2(actual).unwrap();
|
|
|
|
let actual = prettyplease::unparse(&tree);
|
|
|
|
if update_expected {
|
|
|
|
std::fs::write(input.with_extension("out"), actual)
|
|
|
|
.expect("Failed to write expected file");
|
|
|
|
} else {
|
|
|
|
assert_eq!(actual, expected);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|