embed_js_derive/
lib.rs

1extern crate proc_macro;
2
3extern crate cpp_syn;
4extern crate embed_js_common;
5#[macro_use] extern crate quote;
6use cpp_syn::{ TokenTree, Ident };
7
8use proc_macro::TokenStream;
9use embed_js_common::{ WasmPrimitiveType, JsMacArg };
10use std::collections::hash_map::DefaultHasher;
11use std::hash::{Hash, Hasher};
12
13fn unwrap_delimited(t: &TokenTree) -> &[TokenTree] {
14    match *t {
15        TokenTree::Delimited(ref delimited, _) => &delimited.tts,
16        _ => panic!("tried to delimit-unwrap a token that isn't a Delimited")
17    }
18}
19
20fn prim_to_ty(ty: WasmPrimitiveType) -> Ident {
21    match ty {
22        WasmPrimitiveType::I32 => Ident::from("i32"),
23        WasmPrimitiveType::I64 => Ident::from("i64"),
24        WasmPrimitiveType::F32 => Ident::from("f32"),
25        WasmPrimitiveType::F64 => Ident::from("f64"),
26    }
27}
28
29#[proc_macro_derive(EmbedJsDetail)]
30pub fn embed_js(input: TokenStream) -> TokenStream {
31    let s: String = input.to_string();
32    let tokens = cpp_syn::parse_token_trees(&s).unwrap();
33    let trimmed = unwrap_delimited(&unwrap_delimited(&unwrap_delimited(&tokens[4])[2])[2]);
34    let js_mac = embed_js_common::parse_js_mac_string_source(trimmed, &s).expect("syntax error in js macro");
35    let mut hasher = DefaultHasher::new();
36    js_mac.hash(&mut hasher);
37    let mac_hash = hasher.finish();
38    let mut type_params = Vec::new();
39    let mut arg_names = Vec::new();
40    let mut arg_types = Vec::new();
41    let mut extern_arg_types = Vec::new();
42    let mut cast_fragments = Vec::new();
43    let mut next_type_param = 0;
44    for arg in js_mac.args {
45        match arg {
46            JsMacArg::Primitive(_, name, ty) => {
47                arg_names.push(Ident::from(name));
48                let ty = prim_to_ty(ty);
49                let ty_ = ty.clone();
50                extern_arg_types.push(quote!(#ty_));
51                cast_fragments.push(quote!());
52                arg_types.push(quote!(#ty));
53            }
54            JsMacArg::Ref(refs, _, name) => {
55                let mutable = refs[0];
56                let mutability = if mutable {
57                    quote!(mut)
58                } else {
59                    quote!()
60                };
61                arg_names.push(Ident::from(name));
62                let type_param = Ident::from(format!("T{}", next_type_param));
63                {
64                    let type_param_ = &type_param;
65                    next_type_param += 1;
66                    extern_arg_types.push(quote!(*mut u8));
67                    arg_types.push(quote!(& #mutability #type_param_));
68                    if mutable {
69                        cast_fragments.push(quote!(as *mut #type_param_ as *mut u8));
70                    } else {
71                        cast_fragments.push(quote!(as *const #type_param_ as *const u8 as *mut u8));
72                    }
73                }
74                type_params.push(type_param);
75            }
76        }
77    }
78    let type_params = if type_params.len() == 0 {
79        quote!()
80    } else {
81        quote!(<#(#type_params),*>)
82    };
83    let arg_names = &arg_names;
84    let arg_types = &arg_types;
85    let extern_name = Ident::from(format!("__embed_js__{:x}", mac_hash));
86    let result = match js_mac.ret {
87        Some(ty) => {
88            let ty = prim_to_ty(ty);
89            quote! {
90                impl EmbedJsStruct {
91                    fn call #type_params(#(#arg_names: #arg_types),*) -> #ty {
92                        extern {
93                            fn #extern_name(#(#arg_names: #extern_arg_types),*) -> #ty;
94                        }
95                        unsafe { #extern_name(#(#arg_names #cast_fragments),*) }
96                    }
97                }
98            }
99        },
100        None => {
101            quote! {
102                impl EmbedJsStruct {
103                    fn call #type_params(#(#arg_names: #arg_types),*) {
104                        extern {
105                            fn #extern_name(#(#arg_names: #extern_arg_types),*);
106                        }
107                        unsafe { #extern_name(#(#arg_names #cast_fragments),*) }
108                    }
109                }
110            }
111        }
112    };
113    result.parse().unwrap()
114}