1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
#[macro_use]
extern crate quote;
extern crate proc_macro;
extern crate syn;

mod native;

use syn::{
    parse::{Parse, ParseStream},
    parse_macro_input, parse_quote, FnArg, ItemFn, Result, Signature,
};

#[proc_macro]
pub fn export_native(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
    native::export_native(tokens)
}

#[proc_macro]
pub fn export(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let Exported::Fns(mut fns) = parse_macro_input!(tokens as Exported);
    if cfg!(debug_assertions) {
        return quote!(#(#fns)*).into();
    };
    for f in fns.iter_mut() {
        replace_values_with_pointers(f);
        return_null(f);
    }
    quote!(#(#fns)*).into()
}

enum Exported {
    Fns(Vec<syn::ItemFn>),
}

impl Parse for Exported {
    fn parse(input: ParseStream) -> Result<Self> {
        Ok(Exported::Fns(
            syn::Block::parse_within(input)
                .unwrap()
                .iter()
                .cloned()
                .filter_map(|stmt| {
                    if let syn::Stmt::Item(syn::Item::Fn(f)) = stmt {
                        Some(f)
                    } else {
                        None
                    }
                })
                .collect::<Vec<syn::ItemFn>>(),
        ))
    }
}

fn return_null<'a>(f: &'a mut ItemFn) {
    if f.sig.output == parse_quote!() {
        f.sig.output = parse_quote!(-> wasm_rpc::Pointer);
        f.block.stmts.push(syn::Stmt::Expr(parse_quote!(
            wasm_rpc::serde_cbor::Value::Null
        )));
    }
}

fn replace_values_with_pointers(f: &mut ItemFn) {
    let ItemFn {
        sig: Signature { inputs, output, .. },
        block,
        ..
    } = f.clone();
    let pointers: Vec<syn::ExprMethodCall> = f
        .sig
        .inputs
        .clone()
        .into_iter()
        .map(&pointer_to_value)
        .collect();
    f.attrs.push(parse_quote!(#[no_mangle]));
    f.block =
        parse_quote!({wasm_rpc::pointer::from_value(&(|#inputs|#output #block)(#(#pointers),*))});
    f.sig.inputs = f
        .sig
        .inputs
        .iter()
        .cloned()
        .map(|input| {
            if let FnArg::Typed(mut pat) = input {
                pat.ty = parse_quote!(wasm_rpc::Pointer);
                FnArg::Typed(pat)
            } else {
                input
            }
        })
        .collect();
    f.sig.output = parse_quote!(-> wasm_rpc::Pointer);
}

fn pointer_to_value(input: FnArg) -> syn::ExprMethodCall {
    if let FnArg::Typed(syn::PatType { pat, .. }) = input {
        parse_quote!(wasm_rpc::pointer::to_value(#pat).unwrap())
    } else {
        parse_quote!(#input)
    }
}