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}