summaryrefslogtreecommitdiff
path: root/ops/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ops/lib.rs')
-rw-r--r--ops/lib.rs770
1 files changed, 155 insertions, 615 deletions
diff --git a/ops/lib.rs b/ops/lib.rs
index 44f783280..7d4e77f90 100644
--- a/ops/lib.rs
+++ b/ops/lib.rs
@@ -1,189 +1,192 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-use core::panic;
+use attrs::Attributes;
use once_cell::sync::Lazy;
+use optimizer::{BailoutReason, Optimizer};
use proc_macro::TokenStream;
-use proc_macro2::Span;
-use proc_macro2::TokenStream as TokenStream2;
-use proc_macro_crate::crate_name;
-use proc_macro_crate::FoundCrate;
-use quote::format_ident;
-use quote::quote;
-use quote::ToTokens;
+use proc_macro2::{Span, TokenStream as TokenStream2};
+use quote::{quote, ToTokens};
use regex::Regex;
-use std::collections::HashMap;
-use syn::punctuated::Punctuated;
-use syn::token::Comma;
-use syn::FnArg;
-use syn::GenericParam;
-use syn::Ident;
+use syn::{
+ parse, parse_macro_input, punctuated::Punctuated, token::Comma, FnArg,
+ GenericParam, Ident, ItemFn, Lifetime, LifetimeDef,
+};
+
+mod attrs;
+mod deno;
+mod fast_call;
+mod optimizer;
#[cfg(test)]
mod tests;
-// Identifier to the `deno_core` crate.
-//
-// If macro called in deno_core, `crate` is used.
-// If macro called outside deno_core, `deno_core` OR the renamed
-// version from Cargo.toml is used.
-fn core_import() -> TokenStream2 {
- let found_crate =
- crate_name("deno_core").expect("deno_core not present in `Cargo.toml`");
-
- match found_crate {
- FoundCrate::Itself => {
- // TODO(@littledivy): This won't work for `deno_core` examples
- // since `crate` does not refer to `deno_core`.
- // examples must re-export deno_core to make this work
- // until Span inspection APIs are stabalized.
- //
- // https://github.com/rust-lang/rust/issues/54725
- quote!(crate)
- }
- FoundCrate::Name(name) => {
- let ident = Ident::new(&name, Span::call_site());
- quote!(#ident)
- }
- }
-}
+const SCOPE_LIFETIME: &str = "'scope";
-#[derive(Copy, Clone, Debug, Default)]
-struct MacroArgs {
- is_unstable: bool,
- is_v8: bool,
- must_be_fast: bool,
- deferred: bool,
+/// 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));
+ }
}
-impl syn::parse::Parse for MacroArgs {
- fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
- let vars =
- syn::punctuated::Punctuated::<Ident, syn::Token![,]>::parse_terminated(
- input,
- )?;
- let vars: Vec<_> = vars.iter().map(Ident::to_string).collect();
- let vars: Vec<_> = vars.iter().map(String::as_str).collect();
- for var in vars.iter() {
- if !["unstable", "v8", "fast", "deferred"].contains(var) {
- return Err(syn::Error::new(
- input.span(),
- "Ops expect #[op] or #[op(unstable)]",
- ));
- }
+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,
+}
+
+impl Op {
+ fn new(mut item: ItemFn, attrs: Attributes) -> Self {
+ add_scope_lifetime(&mut item);
+
+ // 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());
+
+ let is_async = item.sig.asyncness.is_some() || is_future(&item.sig.output);
+ let type_params = exclude_lifetime_params(&item.sig.generics.params);
+ let core = deno::import();
+
+ Self {
+ orig,
+ item,
+ type_params,
+ is_async,
+ core,
+ attrs,
}
- Ok(Self {
- is_unstable: vars.contains(&"unstable"),
- is_v8: vars.contains(&"v8"),
- must_be_fast: vars.contains(&"fast"),
- deferred: vars.contains(&"deferred"),
- })
}
-}
-#[proc_macro_attribute]
-pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream {
- let margs = syn::parse_macro_input!(attr as MacroArgs);
- let MacroArgs {
- is_unstable,
- is_v8,
- must_be_fast,
- deferred,
- } = margs;
- let func = syn::parse::<syn::ItemFn>(item).expect("expected a function");
- let name = &func.sig.ident;
- let mut generics = func.sig.generics.clone();
- let scope_lifetime =
- syn::LifetimeDef::new(syn::Lifetime::new("'scope", Span::call_site()));
- if !generics.lifetimes().any(|def| *def == scope_lifetime) {
- generics
- .params
- .push(syn::GenericParam::Lifetime(scope_lifetime));
- }
- let type_params = exclude_lifetime_params(&func.sig.generics.params);
- let where_clause = &func.sig.generics.where_clause;
-
- // Preserve the original func as op_foo::call()
- let original_func = {
- let mut func = func.clone();
- func.sig.ident = quote::format_ident!("call");
- func
- };
+ fn gen(mut self) -> TokenStream2 {
+ let mut optimizer = Optimizer::new();
+ match optimizer.analyze(&mut self) {
+ Ok(_) | Err(BailoutReason::MustBeSingleSegment) => {}
+ Err(BailoutReason::FastUnsupportedParamType) => {
+ optimizer.fast_compatible = false;
+ }
+ Err(err) => return quote!(compile_error!(#err);),
+ };
- let core = core_import();
+ 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);
+
+ let has_fallible_fast_call = active && optimizer.returns_result;
+
+ let (v8_body, argc) = if is_async {
+ codegen_v8_async(
+ &core,
+ &item,
+ attrs,
+ item.sig.asyncness.is_some(),
+ attrs.deferred,
+ )
+ } else {
+ codegen_v8_sync(&core, &item, attrs, has_fallible_fast_call)
+ };
- let asyncness = func.sig.asyncness.is_some();
- let is_async = asyncness || is_future(&func.sig.output);
+ let is_v8 = attrs.is_v8;
+ let is_unstable = attrs.is_unstable;
- // First generate fast call bindings to opt-in to error handling in slow call
- let (has_fallible_fast_call, fast_impl, fast_field) =
- codegen_fast_impl(&core, &func, name, is_async, must_be_fast);
+ let docline = format!("Use `{name}::decl()` to get an op-declaration");
+ // 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 {
+ pub fn name() -> &'static str {
+ stringify!(#name)
+ }
- let (v8_body, argc) = if is_async {
- codegen_v8_async(&core, &func, margs, asyncness, deferred)
- } else {
- codegen_v8_sync(&core, &func, margs, has_fallible_fast_call)
- };
+ pub fn v8_fn_ptr #generics () -> #core::v8::FunctionCallback #where_clause {
+ use #core::v8::MapFnTo;
+ Self::v8_func::<#type_params>.map_fn_to()
+ }
- let docline = format!("Use `{name}::decl()` to get an op-declaration");
- // 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 {
- pub fn name() -> &'static str {
- stringify!(#name)
- }
+ 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,
+ argc: #argc,
+ }
+ }
- pub fn v8_fn_ptr #generics () -> #core::v8::FunctionCallback #where_clause {
- use #core::v8::MapFnTo;
- Self::v8_func::<#type_params>.map_fn_to()
- }
+ #[inline]
+ #[allow(clippy::too_many_arguments)]
+ #orig
- 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: #fast_field,
- is_async: #is_async,
- is_unstable: #is_unstable,
- is_v8: #is_v8,
- argc: #argc,
+ pub fn v8_func #generics (
+ scope: &mut #core::v8::HandleScope<'scope>,
+ args: #core::v8::FunctionCallbackArguments,
+ mut rv: #core::v8::ReturnValue,
+ ) #where_clause {
+ #v8_body
}
}
- #[inline]
- #[allow(clippy::too_many_arguments)]
- #original_func
-
- pub fn v8_func #generics (
- scope: &mut #core::v8::HandleScope<'scope>,
- args: #core::v8::FunctionCallbackArguments,
- mut rv: #core::v8::ReturnValue,
- ) #where_clause {
- #v8_body
- }
+ #impl_and_fn
}
+ }
+}
- #fast_impl
- }.into()
+#[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()
}
/// Generate the body of a v8 func for an async op
fn codegen_v8_async(
core: &TokenStream2,
f: &syn::ItemFn,
- margs: MacroArgs,
+ margs: Attributes,
asyncness: bool,
deferred: bool,
) -> (TokenStream2, usize) {
- let MacroArgs { is_v8, .. } = margs;
+ let Attributes { is_v8, .. } = margs;
let special_args = f
.sig
.inputs
@@ -287,241 +290,14 @@ fn opstate_arg(arg: &FnArg) -> Option<TokenStream2> {
}
}
-fn codegen_fast_impl(
- core: &TokenStream2,
- f: &syn::ItemFn,
- name: &syn::Ident,
- is_async: bool,
- must_be_fast: bool,
-) -> (bool, TokenStream2, TokenStream2) {
- if is_async {
- if must_be_fast {
- panic!("async op cannot be a fast api. enforced by #[op(fast)]")
- }
- return (false, quote! {}, quote! { None });
- }
- let fast_info = can_be_fast_api(core, f);
- if must_be_fast && fast_info.is_none() {
- panic!("op cannot be a fast api. enforced by #[op(fast)]")
- }
- if !is_async {
- if let Some(FastApiSyn {
- args,
- ret,
- use_op_state,
- use_fast_cb_opts,
- v8_values,
- returns_result,
- slices,
- }) = fast_info
- {
- let offset = if use_op_state { 1 } else { 0 };
- let mut inputs = f
- .sig
- .inputs
- .iter()
- .skip(offset)
- .enumerate()
- .map(|(idx, arg)| {
- let ident = match arg {
- FnArg::Receiver(_) => unreachable!(),
- FnArg::Typed(t) => match &*t.pat {
- syn::Pat::Ident(i) => format_ident!("{}", i.ident),
- _ => unreachable!(),
- },
- };
- if let Some(ty) = slices.get(&(idx + offset)) {
- return quote! { #ident: *const #core::v8::fast_api::FastApiTypedArray< #ty > };
- }
- if use_fast_cb_opts && idx + offset == f.sig.inputs.len() - 1 {
- return quote! { fast_api_callback_options: *mut #core::v8::fast_api::FastApiCallbackOptions };
- }
- if v8_values.contains(&idx) {
- return quote! { #ident: #core::v8::Local < #core::v8::Value > };
- }
- quote!(#arg)
- })
- .collect::<Vec<_>>();
- if (!slices.is_empty() || use_op_state || returns_result)
- && !use_fast_cb_opts
- {
- inputs.push(quote! { fast_api_callback_options: *mut #core::v8::fast_api::FastApiCallbackOptions });
- }
- let input_idents = f
- .sig
- .inputs
- .iter()
- .enumerate()
- .map(|(idx, a)| {
- let ident = match a {
- FnArg::Receiver(_) => unreachable!(),
- FnArg::Typed(t) => match &*t.pat {
- syn::Pat::Ident(i) => format_ident!("{}", i.ident),
- _ => unreachable!(),
- },
- };
- if slices.get(&idx).is_some() {
- return quote! {
- match unsafe { &* #ident }.get_storage_if_aligned() {
- Some(s) => s,
- None => {
- unsafe { &mut * fast_api_callback_options }.fallback = true;
- return Default::default();
- },
- }
- };
- }
- if use_fast_cb_opts && idx == f.sig.inputs.len() - 1 {
- return quote! { Some(unsafe { &mut * fast_api_callback_options }) };
- }
- if v8_values.contains(&idx) {
- return quote! {
- #core::serde_v8::Value {
- v8_value: #ident,
- }
- };
- }
- quote! { #ident }
- })
- .collect::<Vec<_>>();
- let generics = &f.sig.generics;
- let (impl_generics, ty_generics, where_clause) =
- generics.split_for_impl();
- let type_params = exclude_lifetime_params(&f.sig.generics.params);
- let (trampoline, raw_block) = if is_async {
- // TODO(@littledivy): Fast async calls.
- (
- quote! {
- fn func(recv: #core::v8::Local<#core::v8::Object>, __promise_id: u32, #(#inputs),*) {
- // SAFETY: V8 calling convention guarantees that the callback options pointer is non-null.
- let opts: &#core::v8::fast_api::FastApiCallbackOptions = unsafe { &*fast_api_callback_options };
- // SAFETY: data union is always created as the `v8::Local<v8::Value>` version
- let data = unsafe { opts.data.data };
- // SAFETY: #core guarantees data is a v8 External pointing to an OpCtx for the isolates lifetime
- let ctx = unsafe {
- &*(#core::v8::Local::<#core::v8::External>::cast(data).value()
- as *const #core::_ops::OpCtx)
- };
- let op_id = ctx.op_id;
- #core::_ops::queue_async_op(scope, async move {
- let result = Self::call(#args);
- (__promise_id, __op_id, #core::_ops::OpResult::Ok(result))
- });
- }
- func as *const _
- },
- quote! {},
- )
- } else {
- let output = if returns_result {
- get_fast_result_return_type(&f.sig.output)
- } else {
- let output = &f.sig.output;
- quote! { #output }
- };
- let func_name = format_ident!("func_{}", name);
- let op_state_name = if use_op_state {
- input_idents.first().unwrap().clone()
- } else {
- quote! { op_state }
- };
- let recv_decl = if use_op_state || returns_result {
- quote! {
- // SAFETY: V8 calling convention guarantees that the callback options pointer is non-null.
- let opts: &mut #core::v8::fast_api::FastApiCallbackOptions = unsafe { &mut *fast_api_callback_options };
- // SAFETY: data union is always created as the `v8::Local<v8::Value>` version.
- let data = unsafe { opts.data.data };
- // SAFETY: #core guarantees data is a v8 External pointing to an OpCtx for the isolates lifetime
- let ctx = unsafe {
- &*(#core::v8::Local::<#core::v8::External>::cast(data).value()
- as *const #core::_ops::OpCtx)
- };
- let #op_state_name = &mut std::cell::RefCell::borrow_mut(&ctx.state);
- }
- } else {
- quote! {}
- };
-
- let result_handling = if returns_result {
- quote! {
- match result {
- Ok(result) => {
- result
- },
- Err(err) => {
- #op_state_name.last_fast_op_error.replace(err);
- opts.fallback = true;
- Default::default()
- },
- }
- }
- } else {
- quote! { result }
- };
-
- (
- quote! {
- fn #func_name #generics (_recv: #core::v8::Local<#core::v8::Object>, #(#inputs),*) #output #where_clause {
- #recv_decl
- let result = #name::call::<#type_params>(#(#input_idents),*);
- #result_handling
- }
- },
- quote! {
- #func_name::<#type_params> as *const _
- },
- )
- };
-
- let fast_struct = format_ident!("fast_{}", name);
- let (type_params, ty_generics, struct_generics) =
- if type_params.is_empty() {
- (quote! { () }, quote! {}, quote! {})
- } else {
- (
- quote! { #type_params },
- quote! { #ty_generics },
- quote! { ::<#type_params> },
- )
- };
- return (
- returns_result,
- quote! {
- #[allow(non_camel_case_types)]
- #[doc(hidden)]
- struct #fast_struct #ty_generics {
- _phantom: ::std::marker::PhantomData<#type_params>,
- }
- #trampoline
- impl #impl_generics #core::v8::fast_api::FastFunction for #fast_struct #ty_generics #where_clause {
- fn function(&self) -> *const ::std::ffi::c_void {
- #raw_block
- }
- fn args(&self) -> &'static [#core::v8::fast_api::Type] {
- &[ #args ]
- }
- fn return_type(&self) -> #core::v8::fast_api::CType {
- #ret
- }
- }
- },
- quote! { Some(Box::new(#fast_struct #struct_generics { _phantom: ::std::marker::PhantomData })) },
- );
- }
- }
-
- // Default impl to satisfy generic bounds for non-fast ops
- (false, quote! {}, quote! { None })
-}
-
/// Generate the body of a v8 func for a sync op
fn codegen_v8_sync(
core: &TokenStream2,
f: &syn::ItemFn,
- margs: MacroArgs,
+ margs: Attributes,
has_fallible_fast_call: bool,
) -> (TokenStream2, usize) {
- let MacroArgs { is_v8, .. } = margs;
+ let Attributes { is_v8, .. } = margs;
let special_args = f
.sig
.inputs
@@ -574,242 +350,6 @@ fn codegen_v8_sync(
)
}
-struct FastApiSyn {
- args: TokenStream2,
- ret: TokenStream2,
- use_op_state: bool,
- use_fast_cb_opts: bool,
- v8_values: Vec<usize>,
- returns_result: bool,
- slices: HashMap<usize, TokenStream2>,
-}
-
-fn can_be_fast_api(core: &TokenStream2, f: &syn::ItemFn) -> Option<FastApiSyn> {
- let inputs = &f.sig.inputs;
- let mut returns_result = false;
- let ret = match &f.sig.output {
- syn::ReturnType::Default => quote!(#core::v8::fast_api::CType::Void),
- syn::ReturnType::Type(_, ty) => match is_fast_return_type(core, ty) {
- Some((ret, is_result)) => {
- returns_result = is_result;
- ret
- }
- None => return None,
- },
- };
-
- let mut use_op_state = false;
- let mut use_fast_cb_opts = false;
- let mut v8_values = Vec::new();
- let mut slices = HashMap::new();
- let mut args = vec![quote! { #core::v8::fast_api::Type::V8Value }];
- for (pos, input) in inputs.iter().enumerate() {
- if pos == inputs.len() - 1 && is_optional_fast_callback_option(input) {
- use_fast_cb_opts = true;
- continue;
- }
-
- if pos == 0 && is_mut_ref_opstate(input) {
- use_op_state = true;
- continue;
- }
-
- let ty = match input {
- syn::FnArg::Typed(pat) => &pat.ty,
- _ => unreachable!(),
- };
-
- if let Some(arg) = is_fast_v8_value(core, ty) {
- args.push(arg);
- v8_values.push(pos);
- } else {
- match is_fast_scalar(core, ty, false) {
- None => match is_fast_arg_sequence(core, ty) {
- Some(arg) => {
- args.push(arg);
- }
- None => match is_ref_slice(&ty) {
- Some(SliceType::U32Mut) => {
- args.push(quote! { #core::v8::fast_api::Type::TypedArray(#core::v8::fast_api::CType::Uint32) });
- slices.insert(pos, quote!(u32));
- }
- Some(_) => {
- args.push(quote! { #core::v8::fast_api::Type::TypedArray(#core::v8::fast_api::CType::Uint8) });
- slices.insert(pos, quote!(u8));
- }
- // early return, this function cannot be a fast call.
- None => return None,
- },
- },
- Some(arg) => {
- args.push(arg);
- }
- }
- }
- }
-
- if use_fast_cb_opts || use_op_state {
- // Push CallbackOptions into args; it must be the last argument.
- args.push(quote! { #core::v8::fast_api::Type::CallbackOptions });
- }
-
- let args = args
- .iter()
- .map(|arg| format!("{}", arg))
- .collect::<Vec<_>>()
- .join(", ");
- Some(FastApiSyn {
- args: args.parse().unwrap(),
- ret,
- use_op_state,
- slices,
- v8_values,
- use_fast_cb_opts,
- returns_result,
- })
-}
-
-// A v8::Local<v8::Array> or FastApiTypedArray<T>
-fn is_fast_arg_sequence(
- core: &TokenStream2,
- ty: impl ToTokens,
-) -> Option<TokenStream2> {
- // TODO(@littledivy): Make `v8::` parts optional.
- if is_fast_typed_array(&ty) {
- return Some(
- quote! { #core::v8::fast_api::Type::TypedArray(#core::v8::fast_api::CType::Uint32) },
- );
- }
- if is_local_array(&ty) {
- return Some(
- quote! { #core::v8::fast_api::Type::Sequence(#core::v8::fast_api::CType::Void) },
- );
- }
- None
-}
-
-fn is_fast_v8_value(
- core: &TokenStream2,
- arg: impl ToTokens,
-) -> Option<TokenStream2> {
- if tokens(&arg).contains("serde_v8 :: Value") {
- return Some(quote! { #core::v8::fast_api::Type::V8Value });
- }
- None
-}
-
-fn is_local_array(arg: impl ToTokens) -> bool {
- static RE: Lazy<Regex> =
- Lazy::new(|| Regex::new(r"^v8::Local<v8::Array>$").unwrap());
- RE.is_match(&tokens(arg))
-}
-
-fn is_fast_typed_array(arg: impl ToTokens) -> bool {
- static RE: Lazy<Regex> = Lazy::new(|| {
- Regex::new(r#": (?:deno_core :: )?FastApiTypedArray$"#).unwrap()
- });
- RE.is_match(&tokens(arg))
-}
-
-fn is_fast_return_type(
- core: &TokenStream2,
- ty: impl ToTokens,
-) -> Option<(TokenStream2, bool)> {
- if is_result(&ty) {
- if tokens(&ty).contains("Result < u32") || is_resource_id(&ty) {
- Some((quote! { #core::v8::fast_api::CType::Uint32 }, true))
- } else if tokens(&ty).contains("Result < i32") {
- Some((quote! { #core::v8::fast_api::CType::Int32 }, true))
- } else if tokens(&ty).contains("Result < f32") {
- Some((quote! { #core::v8::fast_api::CType::Float32 }, true))
- } else if tokens(&ty).contains("Result < f64") {
- Some((quote! { #core::v8::fast_api::CType::Float64 }, true))
- } else if tokens(&ty).contains("Result < bool") {
- Some((quote! { #core::v8::fast_api::CType::Bool }, true))
- } else if tokens(&ty).contains("Result < ()") {
- Some((quote! { #core::v8::fast_api::CType::Void }, true))
- } else {
- None
- }
- } else {
- is_fast_scalar(core, ty, true).map(|s| (s, false))
- }
-}
-
-fn get_fast_result_return_type(ty: impl ToTokens) -> TokenStream2 {
- if tokens(&ty).contains("Result < u32") || is_resource_id(&ty) {
- quote! { -> u32 }
- } else if tokens(&ty).contains("Result < i32") {
- quote! { -> i32 }
- } else if tokens(&ty).contains("Result < f32") {
- quote! { -> f32 }
- } else if tokens(&ty).contains("Result < f64") {
- quote! { -> f64 }
- } else if tokens(&ty).contains("Result < bool") {
- quote! { -> bool }
- } else if tokens(&ty).contains("Result < ()") {
- quote! {}
- } else {
- unreachable!()
- }
-}
-
-fn is_fast_scalar(
- core: &TokenStream2,
- ty: impl ToTokens,
- is_ret: bool,
-) -> Option<TokenStream2> {
- let cty = if is_ret {
- quote! { CType }
- } else {
- quote! { Type }
- };
- if is_resource_id(&ty) {
- return Some(quote! { #core::v8::fast_api::#cty::Uint32 });
- }
- if is_void(&ty) {
- return Some(quote! { #core::v8::fast_api::#cty::Void });
- }
- // TODO(@littledivy): Support u8, i8, u16, i16 by casting.
- match tokens(&ty).as_str() {
- "u32" => Some(quote! { #core::v8::fast_api::#cty::Uint32 }),
- "i32" => Some(quote! { #core::v8::fast_api::#cty::Int32 }),
- "u64" => {
- if is_ret {
- None
- } else {
- Some(quote! { #core::v8::fast_api::#cty::Uint64 })
- }
- }
- "i64" => {
- if is_ret {
- None
- } else {
- Some(quote! { #core::v8::fast_api::#cty::Int64 })
- }
- }
- // TODO(@aapoalas): Support 32 bit machines
- "usize" => {
- if is_ret {
- None
- } else {
- Some(quote! { #core::v8::fast_api::#cty::Uint64 })
- }
- }
- "isize" => {
- if is_ret {
- None
- } else {
- Some(quote! { #core::v8::fast_api::#cty::Int64 })
- }
- }
- "f32" => Some(quote! { #core::v8::fast_api::#cty::Float32 }),
- "f64" => Some(quote! { #core::v8::fast_api::#cty::Float64 }),
- "bool" => Some(quote! { #core::v8::fast_api::#cty::Bool }),
- _ => None,
- }
-}
-
/// (full declarations, idents, v8 argument count)
type ArgumentDecl = (TokenStream2, TokenStream2, usize);