Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

op2++: codegen for &self parameter #686

Merged
merged 1 commit into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions ops/op2/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub(crate) struct MacroConfig {
pub async_deferred: bool,
/// Marks an op as re-entrant (can safely call other ops).
pub reentrant: bool,
/// Marks an op as a method on a wrapped object.
pub method: Option<String>,
}

impl MacroConfig {
Expand Down Expand Up @@ -85,6 +87,17 @@ impl MacroConfig {
config.async_deferred = true;
} else if flag == "reentrant" {
config.reentrant = true;
} else if flag.starts_with("method(") {
let tokens =
syn::parse_str::<TokenTree>(&flag[6..])?.into_token_stream();
config.method = std::panic::catch_unwind(|| {
rules!(tokens => {
( ( $s:ty ) ) => {
Some(s.into_token_stream().to_string())
}
})
})
.map_err(|_| Op2Error::PatternMatchFailed("attribute", flag))?;
} else {
return Err(Op2Error::InvalidAttribute(flag));
}
Expand Down Expand Up @@ -226,5 +239,21 @@ mod tests {
..Default::default()
},
);

test_parse(
"(method(A))",
MacroConfig {
method: Some("A".to_owned()),
..Default::default()
},
);
test_parse(
"(fast, method(T))",
MacroConfig {
method: Some("T".to_owned()),
fast: true,
..Default::default()
},
);
}
}
8 changes: 8 additions & 0 deletions ops/op2/dispatch_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use super::dispatch_slow::with_opctx;
use super::dispatch_slow::with_opstate;
use super::dispatch_slow::with_retval;
use super::dispatch_slow::with_scope;
use super::dispatch_slow::with_self;
use super::generator_state::gs_quote;
use super::generator_state::GeneratorState;
use super::signature::ParsedSignature;
Expand Down Expand Up @@ -136,6 +137,12 @@ pub(crate) fn generate_dispatch_async(
quote!()
};

let with_self = if generator_state.needs_self {
with_self(generator_state)
} else {
quote!()
};

Ok(
gs_quote!(generator_state(info, slow_function, slow_function_metrics, opctx) => {
#[inline(always)]
Expand All @@ -148,6 +155,7 @@ pub(crate) fn generate_dispatch_async(
#with_args
#with_opctx
#with_opstate
#with_self

#output
}
Expand Down
20 changes: 18 additions & 2 deletions ops/op2/dispatch_fast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,21 @@ pub(crate) fn generate_dispatch_fast(
quote!()
};

let with_self = if generator_state.needs_self {
gs_quote!(generator_state(self_ty) => {
let self_: &#self_ty = deno_core::cppgc::try_unwrap_cppgc_object(this.into()).unwrap();
})
} else {
quote!()
};

let name = &generator_state.name;
let call = if generator_state.needs_self {
quote!(self_. #name)
} else {
quote!(Self:: #name)
};

let mut fastsig_metrics = fastsig.clone();
fastsig_metrics.ensure_fast_api_callback_options();

Expand Down Expand Up @@ -503,7 +518,7 @@ pub(crate) fn generate_dispatch_fast(

#[allow(clippy::too_many_arguments)]
extern "C" fn #fast_function(
_: deno_core::v8::Local<deno_core::v8::Object>,
this: deno_core::v8::Local<deno_core::v8::Object>,
#( #fastcall_names: #fastcall_types, )*
) -> #output_type {
#[cfg(debug_assertions)]
Expand All @@ -512,9 +527,10 @@ pub(crate) fn generate_dispatch_fast(
#with_fast_api_callback_options
#with_opctx
#with_js_runtime_state
#with_self
let #result = {
#(#call_args)*
Self::call(#(#call_names),*)
#call (#(#call_names),*)
};
#handle_error
#handle_result
Expand Down
24 changes: 23 additions & 1 deletion ops/op2/dispatch_slow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ pub(crate) fn generate_dispatch_slow(
quote!()
};

let with_self = if generator_state.needs_self {
with_self(generator_state)
} else {
quote!()
};

Ok(
gs_quote!(generator_state(opctx, info, slow_function, slow_function_metrics) => {
#[inline(always)]
Expand All @@ -149,6 +155,7 @@ pub(crate) fn generate_dispatch_slow(
#with_isolate
#with_opstate
#with_js_runtime_state
#with_self

#output;
return 0;
Expand Down Expand Up @@ -236,6 +243,13 @@ pub(crate) fn with_js_runtime_state(
)
}

pub(crate) fn with_self(generator_state: &mut GeneratorState) -> TokenStream {
generator_state.needs_opctx = true;
gs_quote!(generator_state(fn_args, self_ty) =>
(let self_: &#self_ty = unsafe { deno_core::cppgc::try_unwrap_cppgc_object(#fn_args.this().into()).unwrap() };)
)
}

pub fn extract_arg(
generator_state: &mut GeneratorState,
index: usize,
Expand Down Expand Up @@ -679,7 +693,15 @@ pub fn call(generator_state: &mut GeneratorState) -> TokenStream {
for arg in &generator_state.args {
tokens.extend(quote!( #arg , ));
}
quote!(Self::call( #tokens ))

let name = &generator_state.name;
let call_ = if generator_state.needs_self {
quote!(self_. #name)
} else {
quote!(Self:: #name)
};

quote!(#call_ ( #tokens ))
}

pub fn return_value(
Expand Down
4 changes: 4 additions & 0 deletions ops/op2/generator_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use proc_macro2::Ident;

pub struct GeneratorState {
pub name: Ident,
/// Identifiers for each of the arguments of the original function
pub args: Vec<Ident>,
/// The result of the `call` function
Expand Down Expand Up @@ -33,6 +34,8 @@ pub struct GeneratorState {
pub fast_function_metrics: Ident,
/// The async function promise ID argument
pub promise_id: Ident,
/// Type of the self argument
pub self_ty: Ident,

pub needs_args: bool,
pub needs_retval: bool,
Expand All @@ -44,6 +47,7 @@ pub struct GeneratorState {
pub needs_fast_opctx: bool,
pub needs_fast_api_callback_options: bool,
pub needs_fast_js_runtime_state: bool,
pub needs_self: bool,
}

/// Quotes a set of generator_state fields, along with variables captured from
Expand Down
39 changes: 34 additions & 5 deletions ops/op2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,14 @@ fn generate_op2(
zip(signature.args.iter(), &func.sig.inputs).collect::<Vec<_>>();

let mut args = vec![];
let mut needs_args = false;
let mut needs_args = config.method.is_some();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function callback arguments are needed for: args.this()

for (index, _) in processed_args.iter().enumerate() {
let input = format_ident!("arg{index}");
args.push(input);
needs_args = true;
}

let name = op_fn.sig.ident.clone();
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());
Expand All @@ -146,8 +147,14 @@ fn generate_op2(
Ident::new("v8_fn_ptr_fast_metrics", Span::call_site());
let fast_api_callback_options =
Ident::new("fast_api_callback_options", Span::call_site());
let self_ty = if let Some(ref ty) = config.method {
format_ident!("{ty}")
} else {
Ident::new("UNINIT", Span::call_site())
};

let mut generator_state = GeneratorState {
name,
args,
fn_args,
scope,
Expand All @@ -164,6 +171,7 @@ fn generate_op2(
fast_function,
fast_function_metrics,
promise_id,
self_ty,
needs_retval: false,
needs_scope: false,
needs_isolate: false,
Expand All @@ -173,6 +181,7 @@ fn generate_op2(
needs_fast_opctx: false,
needs_fast_api_callback_options: false,
needs_fast_js_runtime_state: false,
needs_self: config.method.is_some(),
};

let name = func.sig.ident;
Expand Down Expand Up @@ -241,6 +250,28 @@ fn generate_op2(

let meta_key = signature.metadata.keys().collect::<Vec<_>>();
let meta_value = signature.metadata.values().collect::<Vec<_>>();
let op_fn_sig = &op_fn.sig;
let callable = if let Some(ty) = config.method {
let ident = format_ident!("{ty}");
quote! {
trait Callable {
#op_fn_sig;
}
impl Callable for #ident {
#[inline(always)]
#(#attrs)*
#op_fn
}
}
} else {
quote! {
impl <#(#generic : #bound),*> #name <#(#generic),*> {
#[inline(always)]
#(#attrs)*
#op_fn
}
}
};

Ok(quote! {
#[allow(non_camel_case_types)]
Expand Down Expand Up @@ -277,12 +308,10 @@ fn generate_op2(

#fast_fn
#slow_fn

#[inline(always)]
#(#attrs)*
#op_fn
}

#callable

<#name <#(#generic),*> as ::deno_core::_ops::Op>::DECL
}
})
Expand Down
3 changes: 2 additions & 1 deletion ops/op2/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,8 @@ pub fn parse_signature(
let mut names = vec![];
for input in signature.inputs {
let name = match &input {
FnArg::Receiver(_) => "self".to_owned(),
// Skip receiver
FnArg::Receiver(_) => continue,
FnArg::Typed(ty) => match &*ty.pat {
Pat::Ident(ident) => ident.ident.to_string(),
_ => "(complex)".to_owned(),
Expand Down
2 changes: 2 additions & 0 deletions ops/op2/test_cases/async/async_arg_return.out

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ops/op2/test_cases/async/async_arg_return_result.out

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion ops/op2/test_cases/async/async_deferred.out

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ops/op2/test_cases/async/async_jsbuffer.out

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion ops/op2/test_cases/async/async_lazy.out

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions ops/op2/test_cases/async/async_op_metadata.out

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ops/op2/test_cases/async/async_opstate.out

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ops/op2/test_cases/async/async_result.out

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading