summaryrefslogtreecommitdiff
path: root/ops/op2/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ops/op2/mod.rs')
-rw-r--r--ops/op2/mod.rs326
1 files changed, 0 insertions, 326 deletions
diff --git a/ops/op2/mod.rs b/ops/op2/mod.rs
deleted file mode 100644
index 7f652fe1b..000000000
--- a/ops/op2/mod.rs
+++ /dev/null
@@ -1,326 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-use deno_proc_macro_rules::rules;
-use proc_macro2::Ident;
-use proc_macro2::Span;
-use proc_macro2::TokenStream;
-use quote::format_ident;
-use quote::quote;
-use quote::ToTokens;
-use std::iter::zip;
-use syn2::parse2;
-use syn2::parse_str;
-use syn2::FnArg;
-use syn2::ItemFn;
-use syn2::Path;
-use thiserror::Error;
-
-use self::dispatch_fast::generate_dispatch_fast;
-use self::dispatch_slow::generate_dispatch_slow;
-use self::generator_state::GeneratorState;
-use self::signature::parse_signature;
-use self::signature::Arg;
-use self::signature::SignatureError;
-
-pub mod dispatch_fast;
-pub mod dispatch_slow;
-pub mod generator_state;
-pub mod signature;
-
-#[derive(Debug, Error)]
-pub enum Op2Error {
- #[error("Failed to match a pattern for '{0}': (input was '{1}')")]
- PatternMatchFailed(&'static str, String),
- #[error("Invalid attribute: '{0}'")]
- InvalidAttribute(String),
- #[error("Failed to parse syntax tree")]
- ParseError(#[from] syn2::Error),
- #[error("Failed to map a parsed signature to a V8 call")]
- V8MappingError(#[from] V8MappingError),
- #[error("Failed to parse signature")]
- SignatureError(#[from] SignatureError),
- #[error("This op is fast-compatible and should be marked as (fast)")]
- ShouldBeFast,
- #[error("This op is not fast-compatible and should not be marked as (fast)")]
- ShouldNotBeFast,
-}
-
-#[derive(Debug, Error)]
-pub enum V8MappingError {
- #[error("Unable to map {1:?} to {0}")]
- NoMapping(&'static str, Arg),
-}
-
-#[derive(Default)]
-pub(crate) struct MacroConfig {
- pub core: bool,
- pub fast: bool,
-}
-
-impl MacroConfig {
- pub fn from_flags(flags: Vec<Ident>) -> Result<Self, Op2Error> {
- let mut config: MacroConfig = Self::default();
- for flag in flags {
- if flag == "core" {
- config.core = true;
- } else if flag == "fast" {
- config.fast = true;
- } else {
- return Err(Op2Error::InvalidAttribute(flag.to_string()));
- }
- }
- Ok(config)
- }
-
- pub fn from_tokens(tokens: TokenStream) -> Result<Self, Op2Error> {
- let attr_string = tokens.to_string();
- let config = std::panic::catch_unwind(|| {
- rules!(tokens => {
- () => {
- Ok(MacroConfig::default())
- }
- ($($flags:ident),+) => {
- Self::from_flags(flags)
- }
- })
- })
- .map_err(|_| Op2Error::PatternMatchFailed("attribute", attr_string))??;
- Ok(config)
- }
-}
-
-pub fn op2(
- attr: TokenStream,
- item: TokenStream,
-) -> Result<TokenStream, Op2Error> {
- let func = parse2::<ItemFn>(item)?;
- let config = MacroConfig::from_tokens(attr)?;
- generate_op2(config, func)
-}
-
-fn generate_op2(
- config: MacroConfig,
- func: ItemFn,
-) -> Result<TokenStream, Op2Error> {
- // Create a copy of the original function, named "call"
- let call = Ident::new("call", Span::call_site());
- let mut op_fn = func.clone();
- op_fn.attrs.clear();
- op_fn.sig.generics.params.clear();
- op_fn.sig.ident = call.clone();
-
- // Clear inert attributes
- // TODO(mmastrac): This should limit itself to clearing ours only
- for arg in op_fn.sig.inputs.iter_mut() {
- match arg {
- FnArg::Receiver(slf) => slf.attrs.clear(),
- FnArg::Typed(ty) => ty.attrs.clear(),
- }
- }
-
- let signature = parse_signature(func.attrs, func.sig.clone())?;
- let processed_args =
- zip(signature.args.iter(), &func.sig.inputs).collect::<Vec<_>>();
-
- let mut args = vec![];
- let mut needs_args = false;
- for (index, _) in processed_args.iter().enumerate() {
- let input = format_ident!("arg{index}");
- args.push(input);
- needs_args = true;
- }
-
- let retval = Ident::new("rv", Span::call_site());
- let result = Ident::new("result", Span::call_site());
- let fn_args = Ident::new("args", Span::call_site());
- let scope = Ident::new("scope", Span::call_site());
- let info = Ident::new("info", Span::call_site());
- let opctx = Ident::new("opctx", Span::call_site());
- let slow_function = Ident::new("v8_fn_ptr", Span::call_site());
- let fast_function = Ident::new("v8_fn_ptr_fast", Span::call_site());
- let fast_api_callback_options =
- Ident::new("fast_api_callback_options", Span::call_site());
-
- let deno_core = if config.core {
- syn2::parse_str::<Path>("crate")
- } else {
- syn2::parse_str::<Path>("deno_core")
- }
- .expect("Parsing crate should not fail")
- .into_token_stream();
-
- let mut generator_state = GeneratorState {
- args,
- fn_args,
- call,
- scope,
- info,
- opctx,
- fast_api_callback_options,
- deno_core,
- result,
- retval,
- needs_args,
- slow_function,
- fast_function,
- needs_retval: false,
- needs_scope: false,
- needs_opctx: false,
- needs_opstate: false,
- needs_fast_opctx: false,
- needs_fast_api_callback_options: false,
- };
-
- let name = func.sig.ident;
-
- let slow_fn =
- generate_dispatch_slow(&config, &mut generator_state, &signature)?;
- let (fast_definition, fast_fn) =
- match generate_dispatch_fast(&mut generator_state, &signature)? {
- Some((fast_definition, fast_fn)) => {
- if !config.fast {
- return Err(Op2Error::ShouldBeFast);
- }
- (quote!(Some({#fast_definition})), fast_fn)
- }
- None => {
- if config.fast {
- return Err(Op2Error::ShouldNotBeFast);
- }
- (quote!(None), quote!())
- }
- };
-
- let GeneratorState {
- deno_core,
- slow_function,
- ..
- } = &generator_state;
-
- let arg_count: usize = generator_state.args.len();
- let vis = func.vis;
- let generic = signature
- .generic_bounds
- .keys()
- .map(|s| format_ident!("{s}"))
- .collect::<Vec<_>>();
- let bound = signature
- .generic_bounds
- .values()
- .map(|p| parse_str::<Path>(p).expect("Failed to reparse path"))
- .collect::<Vec<_>>();
-
- Ok(quote! {
- #[allow(non_camel_case_types)]
- #vis struct #name <#(#generic),*> {
- // We need to mark these type parameters as used, so we use a PhantomData
- _unconstructable: ::std::marker::PhantomData<(#(#generic),*)>
- }
-
- impl <#(#generic : #bound),*> #deno_core::_ops::Op for #name <#(#generic),*> {
- const NAME: &'static str = stringify!(#name);
- const DECL: #deno_core::_ops::OpDecl = #deno_core::_ops::OpDecl {
- name: stringify!(#name),
- v8_fn_ptr: Self::#slow_function as _,
- enabled: true,
- fast_fn: #fast_definition,
- is_async: false,
- is_unstable: false,
- is_v8: false,
- arg_count: #arg_count as u8,
- };
- }
-
- impl <#(#generic : #bound),*> #name <#(#generic),*> {
- pub const fn name() -> &'static str {
- stringify!(#name)
- }
-
- pub const fn decl() -> #deno_core::_ops::OpDecl {
- #deno_core::_ops::OpDecl {
- name: stringify!(#name),
- v8_fn_ptr: Self::#slow_function as _,
- enabled: true,
- fast_fn: #fast_definition,
- is_async: false,
- is_unstable: false,
- is_v8: false,
- arg_count: #arg_count as u8,
- }
- }
-
- #fast_fn
- #slow_fn
-
- #[inline(always)]
- #op_fn
- }
- })
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use pretty_assertions::assert_eq;
- use std::path::PathBuf;
- use syn2::parse_str;
- use syn2::File;
- use syn2::Item;
-
- #[testing_macros::fixture("op2/test_cases/**/*.rs")]
- fn test_signature_parser(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 file = parse_str::<File>(&source).expect("Failed to parse Rust file");
- let mut expected_out = vec![];
- for item in file.items {
- if let Item::Fn(mut func) = item {
- let mut config = None;
- func.attrs.retain(|attr| {
- let tokens = attr.into_token_stream();
- let attr_string = attr.clone().into_token_stream().to_string();
- println!("{}", attr_string);
- use syn2 as syn;
- if let Some(new_config) = rules!(tokens => {
- (#[op2]) => {
- Some(MacroConfig::default())
- }
- (#[op2( $($x:ident),* )]) => {
- Some(MacroConfig::from_flags(x).expect("Failed to parse attribute"))
- }
- (#[$_attr:meta]) => {
- None
- }
- }) {
- config = Some(new_config);
- false
- } else {
- true
- }
- });
- let tokens =
- generate_op2(config.unwrap(), func).expect("Failed to generate op");
- println!("======== Raw tokens ========:\n{}", tokens.clone());
- let tree = syn::parse2(tokens).unwrap();
- let actual = prettyplease::unparse(&tree);
- println!("======== Generated ========:\n{}", actual);
- expected_out.push(actual);
- }
- }
-
- let expected_out = expected_out.join("\n");
-
- if update_expected {
- std::fs::write(input.with_extension("out"), expected_out)
- .expect("Failed to write expectation file");
- } else {
- let expected = std::fs::read_to_string(input.with_extension("out"))
- .expect("Failed to read expectation file");
- assert_eq!(
- expected, expected_out,
- "Failed to match expectation. Use UPDATE_EXPECTED=1."
- );
- }
- }
-}