Skip to content

Commit

Permalink
op2++: codegen for &self parameter (#686)
Browse files Browse the repository at this point in the history
Refactor out codegen for `&self` from #682 

```rust
struct State {
  name: &'static str,
}

impl State {
  #[op2(fast, method(State)]
  fn print(&self) {
    println!("{}", self.name);
  }
}

const STATE_DECL: [OpDecl; 1] = [
  State::print()
];
```
  • Loading branch information
littledivy authored Apr 8, 2024
1 parent 5245f5d commit 26880ec
Show file tree
Hide file tree
Showing 55 changed files with 322 additions and 45 deletions.
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();
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

0 comments on commit 26880ec

Please sign in to comment.