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    let body = TokenStream::from(quote! {
119        {
120            // setup
121            let data: (#( #tys ),*) = (#( #args ),*);
122            let data = ::plugin::bincode::serialize(&data).unwrap();
123            let buffer = unsafe { ::plugin::__Buffer::from_vec(data) };
124
125            // operation
126            let new_buffer = unsafe { #extern_fn_name(buffer.into_u64()) };
127            let new_data = unsafe { ::plugin::__Buffer::from_u64(new_buffer).to_vec() };
128
129            // teardown
130            match ::plugin::bincode::deserialize(&new_data) {
131                Ok(d) => d,
132                Err(e) => panic!("Data returned from function not deserializable."),
133            }
134        }
135    });
136
137    let block = parse_macro_input!(body as Block);
138
139    let inner_fn = ItemFn {
140        attrs: fn_declare.attrs,
141        vis: fn_declare.vis,
142        sig: fn_declare.sig,
143        block: Box::new(block),
144    };
145
146    TokenStream::from(quote! {
147        extern "C" {
148            fn #extern_fn_name(buffer: u64) -> u64;
149        }
150
151        #[no_mangle]
152        #inner_fn
153    })
154}