1use proc_macro2::TokenStream;
2use quote::quote;
3use std::iter::repeat;
4use syn::{
5 parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::Paren, AttributeArgs,
6 Error, Expr, FnArg, Ident, ItemFn, NestedMeta, Pat, PatIdent, PatType, Result, ReturnType,
7 Stmt, Token, Type, TypeTuple,
8};
9
10#[proc_macro_attribute]
11pub fn syscall(
12 attrs: proc_macro::TokenStream,
13 item: proc_macro::TokenStream,
14) -> proc_macro::TokenStream {
15 let args = parse_macro_input!(attrs as AttributeArgs);
16 let input = parse_macro_input!(item as ItemFn);
17 expand(args, input)
18 .unwrap_or_else(Error::into_compile_error)
19 .into()
20}
21
22fn expand(_args: Vec<NestedMeta>, mut input: ItemFn) -> Result<proc_macro2::TokenStream> {
23 let body = &input.block;
24 let mut pre_block = Vec::new();
25 let mut post_block = Vec::new();
26 let mut real_args = None;
27 let mut real_ret = None;
28
29 for stmt in &body.stmts {
30 if real_args.is_none() {
31 match stmt {
32 Stmt::Local(local) => {
33 if let Some((_, expr)) = &local.init {
36 real_args = is_real_macro(expr);
37 if real_args.is_some() {
38 if let Pat::Ident(pat_ident) = &local.pat {
39 real_ret = Some(pat_ident.clone());
40 }
41 }
42 }
43 }
44 Stmt::Expr(expr) => {
45 real_args = is_real_macro(expr);
48 }
49 Stmt::Semi(expr, _) => {
50 real_args = is_real_macro(expr)
53 }
54 Stmt::Item(_) => {
55 }
57 }
58
59 if real_args.is_some() {
60 continue;
61 }
62
63 pre_block.push(stmt.clone());
64 continue;
65 }
66
67 post_block.push(stmt.clone());
68 }
69
70 let attrs = &input.attrs;
71 let sig = &mut input.sig;
72 let vis = &input.vis;
73 let ident = &sig.ident;
74 let ident_str = &sig.ident.to_string();
75
76 let mut sig_pre = sig.clone();
79 sig_pre.ident = Ident::new(&format!("pre_{}", sig.ident), sig.span());
80 let ident_pre = &sig_pre.ident;
81 let mut args = sig_pre
82 .inputs
83 .iter()
84 .filter_map(|a| {
85 if let FnArg::Typed(t) = a {
86 Some(t.ty.clone())
87 } else {
88 None
89 }
90 })
91 .collect::<Vec<_>>();
92 let fn_variant = Ident::new(&format!("Func{}", args.len()), sig_pre.span());
93 let dummy_args =
94 repeat(Box::new(Type::Verbatim(quote!(u64)))).take(6usize.saturating_sub(args.len()));
95 args.extend(dummy_args);
96 let sig_ret = match &sig.output {
97 ReturnType::Default => Box::new(Type::Verbatim(quote!(()))),
98 ReturnType::Type(_, bt) => bt.clone(),
99 };
100 let (real_args, pre_func) = if real_args.is_none() {
101 (
105 quote!(),
106 quote!(interceptor_rs::syscall::Variant::<#sig_ret, #(#args),*>::Block(interceptor_rs::syscall::BlockVariant::#fn_variant(#ident_pre))),
107 )
108 } else {
109 sig_pre.output = ReturnType::Type(
110 Token),
111 Box::new(Type::Tuple(TypeTuple {
112 paren_token: Paren {
113 span: sig_pre.span(),
114 },
115 elems: {
116 sig.inputs
117 .iter()
118 .filter_map(|a| {
119 if let FnArg::Typed(pt) = a {
120 Some(*(pt.ty.clone()))
121 } else {
122 None
123 }
124 })
125 .collect()
126 },
127 })),
128 );
129 (
130 quote!((#real_args)),
131 quote!(interceptor_rs::syscall::Variant::<#sig_ret, #(#args),*>::Passthrough(interceptor_rs::syscall::PassthroughVariant::#fn_variant(#ident_pre))),
132 )
133 };
134
135 let mut sig_post = sig.clone();
138 sig_post.ident = Ident::new(&format!("post_{}", sig.ident), sig.span());
139 let mut sig_post_args = Punctuated::new();
140 let sig_post_arg = if let Some(rr) = &real_ret {
141 rr.clone()
142 } else {
143 PatIdent {
144 attrs: vec![],
145 by_ref: None,
146 mutability: None,
147 ident: Ident::new("real_ret", sig_post_args.span()),
148 subpat: None,
149 }
150 };
151 let sig_post_arg_ident = &sig_post_arg.ident;
152 let post_block = if post_block.is_empty() {
153 quote!(#sig_post_arg_ident)
154 } else {
155 quote!(#(#post_block)*)
156 };
157
158 sig_post_args.push_value(FnArg::Typed(PatType {
159 attrs: vec![],
160 pat: Box::new(Pat::Ident(sig_post_arg)),
161 colon_token: Token),
162 ty: sig_ret.clone(),
163 }));
164 sig_post.inputs = sig_post_args;
165 let ident_post = &sig_post.ident;
166
167 Ok(quote!(
168 #(#attrs)*
169 #vis #sig_pre {
170 {#(#pre_block)*}
171 #real_args
172 }
173
174 #(#attrs)*
175 #vis #sig_post {
176 #post_block
177 }
178
179 #[allow(non_upper_case_globals)]
180 #vis static #ident: interceptor_rs::syscall::SysCall<#sig_ret, #(#args),*> = interceptor_rs::syscall::SysCall {
181 name: #ident_str,
182 pre: #pre_func,
183 post: #ident_post,
184 };
185 ))
186}
187
188fn is_real_macro(expr: &Expr) -> Option<TokenStream> {
189 if let Expr::Macro(expr_macro) = expr {
190 let mac = &expr_macro.mac;
191 if let Some(ident) = mac.path.get_ident() {
192 if *ident == "real" {
193 return Some(mac.tokens.clone());
194 }
195 }
196 }
197
198 None
199}