lib.rs

  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}