1use core::panic;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use syn::{
6 parse_macro_input, Block, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatIdent, Type, Visibility,
7};
8
9#[proc_macro_attribute]
10pub fn export(args: TokenStream, function: TokenStream) -> TokenStream {
11 if !args.is_empty() {
12 panic!("The export attribute does not take any arguments");
13 }
14
15 let inner_fn = parse_macro_input!(function as ItemFn);
16
17 if !inner_fn.sig.generics.params.is_empty() {
18 panic!("Exported functions can not take generic parameters");
19 }
20
21 if let Visibility::Public(_) = inner_fn.vis {
22 } else {
23 panic!("The export attribute only works for public functions");
24 }
25
26 let inner_fn_name = format_ident!("{}", inner_fn.sig.ident);
27 let outer_fn_name = format_ident!("__{}", inner_fn_name);
28
29 let variadic = inner_fn.sig.inputs.len();
30 let i = (0..variadic).map(syn::Index::from);
31 let t: Vec<Type> = inner_fn
32 .sig
33 .inputs
34 .iter()
35 .map(|x| match x {
36 FnArg::Receiver(_) => {
37 panic!("All arguments must have specified types, no `self` allowed")
38 }
39 FnArg::Typed(item) => *item.ty.clone(),
40 })
41 .collect();
42
43 // this is cursed...
44 let (args, ty) = if variadic != 1 {
45 (
46 quote! {
47 #( data.#i ),*
48 },
49 quote! {
50 ( #( #t ),* )
51 },
52 )
53 } else {
54 let ty = &t[0];
55 (quote! { data }, quote! { #ty })
56 };
57
58 TokenStream::from(quote! {
59 #[no_mangle]
60 #inner_fn
61
62 #[no_mangle]
63 // TODO: switch len from usize to u32?
64 pub extern "C" fn #outer_fn_name(packed_buffer: u64) -> u64 {
65 // setup
66 let data = unsafe { ::plugin::__Buffer::from_u64(packed_buffer).to_vec() };
67
68 // operation
69 let data: #ty = match ::plugin::bincode::deserialize(&data) {
70 Ok(d) => d,
71 Err(e) => panic!("Data passed to function not deserializable."),
72 };
73 let result = #inner_fn_name(#args);
74 let new_data: Result<Vec<u8>, _> = ::plugin::bincode::serialize(&result);
75 let new_data = new_data.unwrap();
76
77 // teardown
78 let new_buffer = unsafe { ::plugin::__Buffer::from_vec(new_data) }.into_u64();
79 return new_buffer;
80 }
81 })
82}
83
84#[proc_macro_attribute]
85pub fn import(args: TokenStream, function: TokenStream) -> TokenStream {
86 if !args.is_empty() {
87 panic!("The import attribute does not take any arguments");
88 }
89
90 let fn_declare = parse_macro_input!(function as ForeignItemFn);
91
92 if !fn_declare.sig.generics.params.is_empty() {
93 panic!("Exported functions can not take generic parameters");
94 }
95
96 // let inner_fn_name = format_ident!("{}", fn_declare.sig.ident);
97 let extern_fn_name = format_ident!("__{}", fn_declare.sig.ident);
98
99 let (args, tys): (Vec<Ident>, Vec<Type>) = fn_declare
100 .sig
101 .inputs
102 .clone()
103 .into_iter()
104 .map(|x| match x {
105 FnArg::Receiver(_) => {
106 panic!("All arguments must have specified types, no `self` allowed")
107 }
108 FnArg::Typed(t) => {
109 if let Pat::Ident(i) = *t.pat {
110 (i.ident, *t.ty)
111 } else {
112 panic!("All function arguments must be identifiers");
113 }
114 }
115 })
116 .unzip();
117
118 dbg!("hello");
119
120 let body = TokenStream::from(quote! {
121 {
122 // dbg!("executing imported function");
123 // setup
124 let data: (#( #tys ),*) = (#( #args ),*);
125 let data = ::plugin::bincode::serialize(&data).unwrap();
126 let buffer = unsafe { ::plugin::__Buffer::from_vec(data) };
127
128 // operation
129 let new_buffer = unsafe { #extern_fn_name(buffer.into_u64()) };
130 let new_data = unsafe { ::plugin::__Buffer::from_u64(new_buffer).to_vec() };
131
132 // teardown
133 match ::plugin::bincode::deserialize(&new_data) {
134 Ok(d) => d,
135 Err(e) => panic!("Data returned from function not deserializable."),
136 }
137 }
138 });
139
140 dbg!("hello2");
141
142 let block = parse_macro_input!(body as Block);
143
144 dbg!("hello {:?}", &block);
145
146 let inner_fn = ItemFn {
147 attrs: fn_declare.attrs,
148 vis: fn_declare.vis,
149 sig: fn_declare.sig,
150 block: Box::new(block),
151 };
152
153 TokenStream::from(quote! {
154 extern "C" {
155 fn #extern_fn_name(buffer: u64) -> u64;
156 }
157
158 #[no_mangle]
159 #inner_fn
160 })
161}