Skip to content

Commit 7f71a97

Browse files
committed
Add lambda and lambda_unsafe macros
1 parent ccf1160 commit 7f71a97

4 files changed

Lines changed: 115 additions & 15 deletions

File tree

‎libcc2rs-macros/src/lambda.rs‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) 2022-present INESC-ID.
2+
// Distributed under the MIT license that can be found in the LICENSE file.
3+
4+
use proc_macro::TokenStream;
5+
use quote::quote;
6+
use syn::parse::{Parse, ParseStream};
7+
use syn::{Block, Error, ExprClosure, Pat, Result, Stmt, Token, parse_macro_input};
8+
9+
struct Lambda {
10+
captures: Block,
11+
closure: ExprClosure,
12+
}
13+
14+
impl Parse for Lambda {
15+
fn parse(input: ParseStream) -> Result<Self> {
16+
let captures = input.parse()?;
17+
input.parse::<Token![,]>()?;
18+
let closure = input.parse()?;
19+
input.parse::<Option<Token![,]>>()?;
20+
Ok(Lambda { captures, closure })
21+
}
22+
}
23+
24+
fn expand_lambda(lambda: Lambda, is_unsafe: bool) -> Result<proc_macro2::TokenStream> {
25+
let mut names = Vec::new();
26+
let mut types = Vec::new();
27+
let mut inits = Vec::new();
28+
for stmt in &lambda.captures.stmts {
29+
let Stmt::Local(local) = stmt else {
30+
return Err(Error::new_spanned(stmt, "expected a `let` for a capture"));
31+
};
32+
let (Pat::Type(pat), Some(init)) = (&local.pat, &local.init) else {
33+
return Err(Error::new_spanned(
34+
local,
35+
"expected `let <name>: <type> = <init>;` for a capture",
36+
));
37+
};
38+
names.push(&pat.pat);
39+
types.push(&pat.ty);
40+
inits.push(&init.expr);
41+
}
42+
43+
let mut params = Vec::new();
44+
let mut param_types = Vec::new();
45+
for input in &lambda.closure.inputs {
46+
let Pat::Type(pat) = input else {
47+
return Err(Error::new_spanned(input, "expected a typed parameter"));
48+
};
49+
params.push(pat);
50+
param_types.push(&pat.ty);
51+
}
52+
53+
let output = &lambda.closure.output;
54+
let body = &lambda.closure.body;
55+
let (receiver, body, constructor) = if is_unsafe {
56+
(
57+
quote! { &mut self },
58+
quote! { unsafe { #body } },
59+
quote! { from_lambda_unsafe },
60+
)
61+
} else {
62+
(quote! { &self }, quote! { #body }, quote! { from_lambda })
63+
};
64+
65+
Ok(quote! {{
66+
struct __Lambda {
67+
#(#names: #types,)*
68+
}
69+
impl __Lambda {
70+
#[allow(unused_unsafe, clippy::too_many_arguments)]
71+
fn call(#receiver #(, #params)*) #output {
72+
#body
73+
}
74+
}
75+
::libcc2rs::FnPtr::<fn(#(#param_types),*) #output>::#constructor(
76+
__Lambda { #(#names: #inits,)* },
77+
__Lambda::call,
78+
)
79+
}})
80+
}
81+
82+
pub fn expand(input: TokenStream, is_unsafe: bool) -> TokenStream {
83+
let lambda = parse_macro_input!(input as Lambda);
84+
expand_lambda(lambda, is_unsafe)
85+
.unwrap_or_else(Error::into_compile_error)
86+
.into()
87+
}

‎libcc2rs-macros/src/lib.rs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use proc_macro::TokenStream;
66
mod byte_repr;
77
mod fn_ptr_arg;
88
mod goto;
9+
mod lambda;
910
mod record;
1011
mod state_machine;
1112
mod switch;
@@ -76,6 +77,16 @@ pub fn goto_block(input: TokenStream) -> TokenStream {
7677
goto::expand(input)
7778
}
7879

80+
#[proc_macro]
81+
pub fn lambda(input: TokenStream) -> TokenStream {
82+
lambda::expand(input, false)
83+
}
84+
85+
#[proc_macro]
86+
pub fn lambda_unsafe(input: TokenStream) -> TokenStream {
87+
lambda::expand(input, true)
88+
}
89+
7990
#[proc_macro]
8091
pub fn goto(_input: TokenStream) -> TokenStream {
8192
quote::quote! {

‎libcc2rs/src/fn_ptr.rs‎

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
#![allow(private_bounds)]
55

66
use std::any::{Any, TypeId};
7-
use std::cell::{RefCell, UnsafeCell};
7+
use std::cell::UnsafeCell;
88
use std::rc::Rc;
99

1010
use crate::fn_ptr_arg::{ArgList, ArgRepr, FnPtrArg, FnPtrArgs, record_from_repr};
11-
use crate::rc::{AsPointer, Ptr};
11+
use crate::rc::Ptr;
1212
use crate::reinterpret::ByteRepr;
1313
use crate::void::{AnyPtr, ErasedPtr};
1414

@@ -191,33 +191,33 @@ macro_rules! impl_fn_ptr_call {
191191
}
192192

193193
#[allow(non_snake_case)]
194-
pub fn with_captures<Captures: 'static>(
195-
captures: Captures,
196-
body: fn(Ptr<Captures> $(, $a)*) -> R,
194+
pub fn from_lambda<Lambda: 'static>(
195+
lambda: Lambda,
196+
call: fn(&Lambda $(, $a)*) -> R,
197197
) -> Self {
198-
let captures = Rc::new(RefCell::new(captures));
198+
let lambda = Rc::new(lambda);
199199
FnPtr {
200-
addr: body as usize,
200+
addr: call as usize,
201201
current: None,
202202
original: None,
203203
lambda: Some(Rc::new(move |($($a,)*): ($($a,)*)| {
204-
body(captures.as_pointer() $(, $a)*)
204+
call(&lambda $(, $a)*)
205205
})),
206206
}
207207
}
208208

209209
#[allow(non_snake_case)]
210-
pub fn with_captures_unsafe<Captures: 'static>(
211-
captures: Captures,
212-
body: fn(&mut Captures $(, $a)*) -> R,
210+
pub fn from_lambda_unsafe<Lambda: 'static>(
211+
lambda: Lambda,
212+
call: fn(&mut Lambda $(, $a)*) -> R,
213213
) -> Self {
214-
let captures = Rc::new(UnsafeCell::new(captures));
214+
let lambda = Rc::new(UnsafeCell::new(lambda));
215215
FnPtr {
216-
addr: body as usize,
216+
addr: call as usize,
217217
current: None,
218218
original: None,
219219
lambda: Some(Rc::new(move |($($a,)*): ($($a,)*)| {
220-
body(unsafe { &mut *captures.get() } $(, $a)*)
220+
call(unsafe { &mut *lambda.get() } $(, $a)*)
221221
})),
222222
}
223223
}

‎libcc2rs/src/lib.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,6 @@ pub use fd::*;
6969
mod format;
7070
pub use format::*;
7171

72-
pub use libcc2rs_macros::{ByteRepr, FnPtrArg, Record, VaArg, goto, goto_block, switch};
72+
pub use libcc2rs_macros::{
73+
ByteRepr, FnPtrArg, Record, VaArg, goto, goto_block, lambda, lambda_unsafe, switch,
74+
};

0 commit comments

Comments
 (0)