1use proc_macro::TokenStream;
3use quote::quote;
4
5struct Code;
6
7impl Code {
8 fn generate(stream: TokenStream) -> TokenStream {
9 const CTX: &str = "ctx";
11 const FRAME: &str = "frame";
12
13 let input = syn::parse_macro_input!(stream as syn::ItemFn);
15 let mut insns = Vec::new();
16
17 let syn::ItemFn { attrs, vis, mut sig, block } = input;
19 let funcident = sig.ident;
20 let funcout = sig.output;
21
22 let mut idents = Vec::new();
26
27 let inputs = std::mem::take(&mut sig.inputs);
29 let mut inputs = inputs.into_iter().collect::<Vec<syn::FnArg>>();
30
31 if let Some(ctx) = inputs.first() {
35 if Self::is_ptr_to(ctx, "Secondary") {
36 let ctx = inputs.remove(0);
38 let ident = Self::get_ident(&ctx);
39 idents.push(ident);
40 } else {
41 idents.push(syn::Ident::new(CTX, proc_macro2::Span::call_site()));
44 };
45
46 if let Some(frame) = inputs.first() {
47 if Self::is_ptr_to(frame, "CallFrame") {
48 let frame = inputs.remove(0);
50 let ident = Self::get_ident(&frame);
51 idents.push(ident);
52 } else {
53 idents.push(syn::Ident::new(FRAME, proc_macro2::Span::call_site()));
55 }
56 } else {
57 idents.push(syn::Ident::new(FRAME, proc_macro2::Span::call_site()));
60 }
61 } else {
62 idents.push(syn::Ident::new(CTX, proc_macro2::Span::call_site()));
65 idents.push(syn::Ident::new(FRAME, proc_macro2::Span::call_site()));
66 }
67
68 let ctx = idents.first().expect("no context object");
70 let frame = idents.get(1).expect("no frame object");
71 for (i, input) in inputs.into_iter().enumerate() {
72 let ident = Self::get_ident(&input);
73 if let syn::FnArg::Typed(syn::PatType {ty, .. }) = input {
74 let ty = *(ty).clone();
75 let ins = Self::parse_type(ty, &ident, frame, ctx, i, false);
76 insns.push(ins);
77 } else {
78 panic!("unable to parse argument with ident {ident:?}");
79 }
80 }
81
82 let stmts = &block.stmts;
83 let v = quote! {
84 #(#attrs)* #vis fn #funcident<T> (#ctx: &mut pai::ctx::Secondary<T, pai::Error>, #frame: &pai::api::CallFrame) #funcout {
85 #(#insns)*
86 #(#stmts)*
87 }
88 };
89 v.into()
90 }
91
92
93
94 fn is_ptr_to(arg: &syn::FnArg, check: &str) -> bool {
95 if let syn::FnArg::Typed(syn::PatType {ty, .. }) = arg {
96 let ty = *(ty).clone();
97 if let syn::Type::Reference(p) = ty {
98 let ty = *(p.elem).clone();
99
100 if let syn::Type::Path(p) = ty {
101 if p.qself.is_none() && Self::path_is_matching(&p.path, check) {
102 println!("matched generic");
103 true
104 } else {
105 println!("{:?}", p.path.get_ident());
106 let ret = p.path.is_ident(check);
107 println!("match = {ret}");
108 ret
109 }
110 } else {
111 false
112 }
113 } else {
114 false
115 }
116 } else {
117 false
118 }
119 }
120
121
122 fn path_is_option(path: &syn::Path) -> bool {
123 path.leading_colon.is_none()
124 && path.segments.len() == 1
125 && path.segments.iter().next().unwrap().ident == "Option"
126 }
127 fn path_is_matching(path: &syn::Path, check: &str) -> bool {
128 path.leading_colon.is_none()
129 && path.segments.len() == 1
130 && path.segments.iter().next().unwrap().ident == check
131 }
132
133 fn parse_type(ty: syn::Type, ident: &proc_macro2::Ident, frame: &proc_macro2::Ident, ctx: &proc_macro2::Ident, argnum: usize, inopt: bool) -> proc_macro2::TokenStream {
134 let framearg = quote! { #frame.arg(#argnum, #ctx.client_mut())? };
135 if let syn::Type::Path(p) = ty {
136 if p.path.is_ident("i64") {
137 assert!(!inopt);
138 quote! { let #ident = #framearg.as_i64(); }
139 } else if p.path.is_ident("i32") {
140 assert!(!inopt);
141 quote! { let #ident = #framearg.as_i32(); }
142 } else if p.path.is_ident("i16") {
143 assert!(!inopt);
144 quote! { let #ident = #framearg.as_i16(); }
145 } else if p.path.is_ident("i8") {
146 assert!(!inopt);
147 quote! { let #ident = #framearg.as_i8(); }
148 } else if p.path.is_ident("isize") {
149 assert!(!inopt);
150 quote! { let #ident = #framearg.as_isize(); }
151 } else if p.path.is_ident("u64") {
152 assert!(!inopt);
153 quote! { let #ident = #framearg.as_u64(); }
154 } else if p.path.is_ident("u32") {
155 assert!(!inopt);
156 quote! { let #ident = #framearg.as_u32(); }
157 } else if p.path.is_ident("u16") {
158 assert!(!inopt);
159 quote! { let #ident = #framearg.as_u16(); }
160 } else if p.path.is_ident("u8") {
161 assert!(!inopt);
162 quote! { let #ident = #framearg.as_u8(); }
163 } else if p.path.is_ident("usize") {
164 assert!(!inopt);
165 quote! { let #ident = #framearg.as_usize(); }
166 } else if p.path.is_ident("String") {
167 if !inopt {
168 quote! { let #ident = #framearg.read_ptr_as_str(#ctx.client_mut())?; }
169 } else {
170 quote! { let #ident: Option<String> = #framearg.read_ptr_as_str(#ctx.client_mut()).ok(); }
171 }
172 } else if p.qself.is_none() && Self::path_is_option(&p.path) {
173 assert!(!inopt);
174 let type_params = &p.path.segments.first().unwrap().arguments;
175 let generic_arg = match type_params {
177 syn::PathArguments::AngleBracketed(params) => params.args.first().unwrap(),
178 _ => panic!("unable to parse {ident:?}"),
179 };
180 match generic_arg {
181 syn::GenericArgument::Type(ty) => {
182 Self::parse_type(ty.clone(), ident, frame, ctx, argnum, true)
183 },
184 _ => panic!("unable to parse {ident:?}"),
185 }
186 } else {
187 panic!("custom idents not supported yet");
188 }
195 } else {
196 panic!("expected Type::Path");
197 }
198 }
199
200 fn get_ident(arg: &syn::FnArg) -> syn::Ident {
201 if let syn::FnArg::Typed(syn::PatType {ty: _, pat, .. }) = arg {
202 let pat = *(pat).clone();
203 if let syn::Pat::Ident(id) = pat {
204 return id.ident.clone();
205 }
206 }
207 panic!("unable to find ident");
208 }
209}
210
211#[proc_macro_attribute]
215pub fn pai_hook(_attr: TokenStream, stream: TokenStream) -> TokenStream {
216 Code::generate(stream)
217}
218
219struct RegsCode;
220
221impl RegsCode {
222 fn generate(stream: TokenStream) -> TokenStream {
223 let input = syn::parse_macro_input!(stream as syn::DeriveInput);
224 let name = input.ident.clone();
225 let syn::Data::Struct(input) = input.data else { panic!("") };
226 let mut gets = Vec::new();
227 let mut sizes = Vec::new();
228 let mut sp: Option<(syn::Ident, syn::Type)> = None;
229 let mut pc: Option<(syn::Ident, syn::Type)> = None;
230 let mut set_sysno: Option<(syn::Ident, syn::Type)> = None;
231 let mut get_sysno: Option<syn::Ident> = None;
232 let mut fields = Vec::new();
233 for field in input.fields.iter() {
234 let ident = field.ident.as_ref().unwrap().clone();
235
236 for attr in field.attrs.iter() {
237 let p = attr.path();
238 if p.is_ident("sp") {
239 sp = Some((ident.clone(), field.ty.clone()));
240 } else if p.is_ident("pc") {
241 pc = Some((ident.clone(), field.ty.clone()));
242 } else if p.is_ident("sysno") {
243 set_sysno = Some((ident.clone(), field.ty.clone()));
244 get_sysno = Some(ident.clone());
245 } else if p.is_ident("setsysno") {
246 set_sysno = Some((ident.clone(), field.ty.clone()));
247 } else if p.is_ident("getsysno") {
248 get_sysno = Some(ident.clone());
249 }
250 }
251
252 let name = format!("{ident}");
253 fields.push(name.clone());
254 let ins = quote! { #name => Some(std::mem::offset_of!(Self, #ident)), };
255 gets.push(ins);
256 let ins = quote! { #name => Some(std::mem::size_of_val(&self.#ident)), };
257 sizes.push(ins);
258 }
259 let spcode = if let Some((sp, ty)) = sp {
260 quote! {
261 fn _get_sp(&self) -> u64 {
262 self.#sp as u64
263 }
264 fn _set_sp(&mut self, sp: u64) {
265 self.#sp = sp as #ty;
266 }
267 }
268 } else {
269 quote!()
270 };
271 let pccode = if let Some((pc, ty)) = pc {
272 quote! {
273 fn _get_pc(&self) -> u64 {
274 self.#pc as u64
275 }
276 fn _set_pc(&mut self, pc: u64) {
277 self.#pc = pc as #ty;
278 }
279 }
280 } else {
281 quote!()
282 };
283 let sysnocode = if let Some(get) = get_sysno {
284 let (set, ty) = set_sysno.unwrap();
285 quote! {
286 fn _get_sysno(&self) -> usize {
287 self.#get as usize
288 }
289 fn _set_sysno(&mut self, sysno: usize) {
290 self.#set = sysno as #ty;
291 }
292 }
293 } else {
294 quote!()
295 };
296 let res = quote! {
297 impl #name {
298 fn _offset_of(&self, regs: &str) -> Option<usize> {
299 match regs {
300 #(#gets)*
301 _ => None,
302 }
303 }
304 fn _size_of(&self, regs: &str) -> Option<usize> {
305 match regs {
306 #(#sizes)*
307 _ => None,
308 }
309 }
310 fn _fields(&self) -> &[&str] {
311 &[#(#fields),*]
312 }
313 unsafe fn _get_value(&self, offset: usize, size: usize, data: &mut Vec<u8>) {
314 let v: *const u8 = self as *const Self as *const u8;
315 let v = unsafe { v.byte_add(offset) };
316 let v = unsafe { std::slice::from_raw_parts(v, size) };
317 data.extend_from_slice(v);
318 }
319 unsafe fn _set_value(&mut self, offset: usize, data: &[u8]) {
320 let v: *mut u8 = self as *mut Self as *mut u8;
321 let v = unsafe { v.byte_add(offset) };
322 let v: &mut [u8] = unsafe { std::slice::from_raw_parts_mut(v, data.len()) };
323 for (i, b) in v.iter_mut().enumerate() {
324 *b = data[i];
325 }
326 }
327 #spcode
328 #pccode
329 #sysnocode
330 }
331 };
332 res.into()
333 }
334
335}
336
337#[proc_macro_derive(PaiRegs, attributes(sp, pc, sysno, getsysno, setsysno))]
340pub fn derive_regs_attr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
341 RegsCode::generate(input)
342}