summaryrefslogtreecommitdiff
path: root/ops/fast_call.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ops/fast_call.rs')
-rw-r--r--ops/fast_call.rs363
1 files changed, 0 insertions, 363 deletions
diff --git a/ops/fast_call.rs b/ops/fast_call.rs
deleted file mode 100644
index 63cff4c62..000000000
--- a/ops/fast_call.rs
+++ /dev/null
@@ -1,363 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-//! Code generation for V8 fast calls.
-
-use pmutil::q;
-use pmutil::Quote;
-use pmutil::ToTokensExt;
-use proc_macro2::Span;
-use proc_macro2::TokenStream;
-use quote::quote;
-use syn::parse_quote;
-use syn::punctuated::Punctuated;
-use syn::token::Comma;
-use syn::Generics;
-use syn::Ident;
-use syn::ItemFn;
-
-use crate::optimizer::FastValue;
-use crate::optimizer::Optimizer;
-
-pub(crate) struct FastImplItems {
- pub(crate) impl_and_fn: TokenStream,
- pub(crate) decl: TokenStream,
- pub(crate) active: bool,
-}
-
-pub(crate) fn generate(
- core: &TokenStream,
- optimizer: &mut Optimizer,
- item_fn: &ItemFn,
-) -> FastImplItems {
- if !optimizer.fast_compatible {
- return FastImplItems {
- impl_and_fn: TokenStream::new(),
- decl: quote! { None },
- active: false,
- };
- }
-
- // TODO(@littledivy): Use `let..else` on 1.65.0
- let output_ty = match &optimizer.fast_result {
- // Assert that the optimizer did not set a return type.
- //
- // @littledivy: This *could* potentially be used to optimize resolving
- // promises but knowing the return type at compile time instead of
- // serde_v8 serialization.
- Some(_) if optimizer.is_async => &FastValue::Void,
- Some(ty) => ty,
- None if optimizer.is_async => &FastValue::Void,
- None => {
- return FastImplItems {
- impl_and_fn: TokenStream::new(),
- decl: quote! { None },
- active: false,
- }
- }
- };
-
- // We've got 2 idents.
- //
- // - op_foo, the public op declaration contains the user function.
- // - op_foo_fast_fn, the fast call function.
- let ident = item_fn.sig.ident.clone();
- let fast_fn_ident =
- Ident::new(&format!("{ident}_fast_fn"), Span::call_site());
-
- // Deal with generics.
- let generics = &item_fn.sig.generics;
- let (impl_generics, _, where_clause) = generics.split_for_impl();
-
- // This goes in the FastFunction impl block.
- // let mut segments = Punctuated::new();
- // {
- // let mut arguments = PathArguments::None;
- // if let Some(ref struct_generics) = struct_generics {
- // arguments = PathArguments::AngleBracketed(parse_quote! {
- // #struct_generics
- // });
- // }
- // segments.push_value(PathSegment {
- // ident: fast_ident.clone(),
- // arguments,
- // });
- // }
-
- // Original inputs.
- let mut inputs = item_fn.sig.inputs.clone();
- let mut transforms = q!({});
- let mut pre_transforms = q!({});
-
- // Apply parameter transforms
- for (index, input) in inputs.iter_mut().enumerate() {
- if let Some(transform) = optimizer.transforms.get(&index) {
- let quo: Quote = transform.apply_for_fast_call(core, input);
- transforms.push_tokens(&quo);
- }
- }
-
- // Collect idents to be passed into function call, we can now freely
- // modify the inputs.
- let idents = inputs
- .iter()
- .map(|input| match input {
- syn::FnArg::Typed(pat_type) => match &*pat_type.pat {
- syn::Pat::Ident(pat_ident) => pat_ident.ident.clone(),
- _ => panic!("unexpected pattern"),
- },
- _ => panic!("unexpected argument"),
- })
- .collect::<Punctuated<_, Comma>>();
-
- // Retain only *pure* parameters.
- let mut fast_fn_inputs = if optimizer.has_opstate_in_parameters() {
- inputs.into_iter().skip(1).collect()
- } else {
- inputs
- };
-
- let mut input_variants = optimizer
- .fast_parameters
- .iter()
- .map(q_fast_ty_variant)
- .collect::<Punctuated<_, Comma>>();
-
- // Apply *hard* optimizer hints.
- if optimizer.has_fast_callback_option
- || optimizer.has_wasm_memory
- || optimizer.needs_opstate()
- || optimizer.is_async
- || optimizer.needs_fast_callback_option
- {
- let decl = parse_quote! {
- fast_api_callback_options: *mut #core::v8::fast_api::FastApiCallbackOptions
- };
-
- if optimizer.has_fast_callback_option || optimizer.has_wasm_memory {
- // Replace last parameter.
- assert!(fast_fn_inputs.pop().is_some());
- fast_fn_inputs.push(decl);
- } else {
- fast_fn_inputs.push(decl);
- }
-
- input_variants.push(q!({ CallbackOptions }));
- }
-
- // (recv, p_id, ...)
- //
- // Optimizer has already set it in the fast parameter variant list.
- if optimizer.is_async {
- if fast_fn_inputs.is_empty() {
- fast_fn_inputs.push(parse_quote! { __promise_id: i32 });
- } else {
- fast_fn_inputs.insert(0, parse_quote! { __promise_id: i32 });
- }
- }
-
- let mut output_transforms = q!({});
-
- if optimizer.needs_opstate()
- || optimizer.is_async
- || optimizer.has_fast_callback_option
- || optimizer.has_wasm_memory
- {
- // Dark arts 🪄 ✨
- //
- // - V8 calling convention guarantees that the callback options pointer is non-null.
- // - `data` union is always initialized as the `v8::Local<v8::Value>` variant.
- // - deno_core guarantees that `data` is a v8 External pointing to an OpCtx for the
- // isolate's lifetime.
- let prelude = q!({
- let __opts: &mut v8::fast_api::FastApiCallbackOptions =
- unsafe { &mut *fast_api_callback_options };
- });
-
- pre_transforms.push_tokens(&prelude);
- }
-
- if optimizer.needs_opstate() || optimizer.is_async {
- // Grab the op_state identifier, the first one. ¯\_(ツ)_/¯
- let op_state = match idents.first() {
- Some(ident) if optimizer.has_opstate_in_parameters() => ident.clone(),
- // fn op_foo() -> Result<...>
- _ => Ident::new("op_state", Span::call_site()),
- };
-
- let ctx = q!({
- let __ctx = unsafe {
- &*(v8::Local::<v8::External>::cast(unsafe { __opts.data.data }).value()
- as *const _ops::OpCtx)
- };
- });
-
- pre_transforms.push_tokens(&ctx);
- pre_transforms.push_tokens(&match optimizer.is_async {
- false => q!(
- Vars {
- op_state: &op_state
- },
- {
- let op_state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state);
- }
- ),
- true => q!(
- Vars {
- op_state: &op_state
- },
- {
- let op_state = __ctx.state.clone();
- }
- ),
- });
-
- if optimizer.returns_result && !optimizer.is_async {
- // Magic fallback 🪄
- //
- // If Result<T, E> is Ok(T), return T as fast value.
- //
- // Err(E) gets put into `last_fast_op_error` slot and
- //
- // V8 calls the slow path so we can take the slot
- // value and throw.
- let default = optimizer.fast_result.as_ref().unwrap().default_value();
- let result_wrap = q!(Vars { op_state, default }, {
- match result {
- Ok(result) => result,
- Err(err) => {
- op_state.last_fast_op_error.replace(err);
- __opts.fallback = true;
- default
- }
- }
- });
-
- output_transforms.push_tokens(&result_wrap);
- }
- }
-
- if optimizer.is_async {
- let queue_future = if optimizer.returns_result {
- q!({
- let result = _ops::queue_fast_async_op(__ctx, __promise_id, result);
- })
- } else {
- q!({
- let result =
- _ops::queue_fast_async_op(__ctx, __promise_id, async move {
- Ok(result.await)
- });
- })
- };
-
- output_transforms.push_tokens(&queue_future);
- }
-
- if !optimizer.returns_result {
- let default_output = q!({ result });
- output_transforms.push_tokens(&default_output);
- }
-
- let output = q_fast_ty(output_ty);
- // Generate the function body.
- //
- // fn f <S> (_: Local<Object>, a: T, b: U) -> R {
- // /* Transforms */
- // let a = a.into();
- // let b = b.into();
- //
- // let r = op::call(a, b);
- //
- // /* Return transform */
- // r.into()
- // }
- let fast_fn = q!(
- Vars { core, pre_transforms, op_name_fast: &fast_fn_ident, op_name: &ident, fast_fn_inputs, generics, where_clause, idents, transforms, output_transforms, output: &output },
- {
- impl generics op_name generics where_clause {
- #[allow(clippy::too_many_arguments)]
- fn op_name_fast (_: core::v8::Local<core::v8::Object>, fast_fn_inputs) -> output {
- use core::v8;
- use core::_ops;
- pre_transforms
- transforms
- let result = Self::call (idents);
- output_transforms
- }
- }
- }
- );
-
- let output_variant = q_fast_ty_variant(output_ty);
- let mut generics: Generics = parse_quote! { #impl_generics };
- generics.where_clause = where_clause.cloned();
-
- // fast_api::FastFunction::new(&[ CType::T, CType::U ], CType::T, f::<P> as *const ::std::ffi::c_void)
- let decl = q!(
- Vars {
- core: core,
- fast_fn_ident: fast_fn_ident,
- inputs: input_variants,
- output: output_variant
- },
- {
- {
- use core::v8::fast_api::CType;
- use core::v8::fast_api::Type::*;
- Some(core::v8::fast_api::FastFunction::new(
- &[inputs],
- CType::output,
- Self::fast_fn_ident as *const ::std::ffi::c_void,
- ))
- }
- }
- )
- .dump();
-
- let impl_and_fn = fast_fn.dump();
-
- FastImplItems {
- impl_and_fn,
- decl,
- active: true,
- }
-}
-
-/// Quote fast value type.
-fn q_fast_ty(v: &FastValue) -> Quote {
- match v {
- FastValue::Void => q!({ () }),
- FastValue::Bool => q!({ bool }),
- FastValue::U32 => q!({ u32 }),
- FastValue::I32 => q!({ i32 }),
- FastValue::U64 => q!({ u64 }),
- FastValue::I64 => q!({ i64 }),
- FastValue::F32 => q!({ f32 }),
- FastValue::F64 => q!({ f64 }),
- FastValue::Pointer => q!({ *mut ::std::ffi::c_void }),
- FastValue::V8Value => q!({ v8::Local<v8::Value> }),
- FastValue::Uint8Array
- | FastValue::Uint32Array
- | FastValue::Float64Array
- | FastValue::SeqOneByteString => unreachable!(),
- }
-}
-
-/// Quote fast value type's variant.
-fn q_fast_ty_variant(v: &FastValue) -> Quote {
- match v {
- FastValue::Void => q!({ Void }),
- FastValue::Bool => q!({ Bool }),
- FastValue::U32 => q!({ Uint32 }),
- FastValue::I32 => q!({ Int32 }),
- FastValue::U64 => q!({ Uint64 }),
- FastValue::I64 => q!({ Int64 }),
- FastValue::F32 => q!({ Float32 }),
- FastValue::F64 => q!({ Float64 }),
- FastValue::Pointer => q!({ Pointer }),
- FastValue::V8Value => q!({ V8Value }),
- FastValue::Uint8Array => q!({ TypedArray(CType::Uint8) }),
- FastValue::Uint32Array => q!({ TypedArray(CType::Uint32) }),
- FastValue::Float64Array => q!({ TypedArray(CType::Float64) }),
- FastValue::SeqOneByteString => q!({ SeqOneByteString }),
- }
-}