From 92436069bd2a9e9bc7c8ab540751aacf8c1424b0 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Wed, 7 Mar 2018 16:28:42 -0800 Subject: [PATCH] wasm-bindgen-macro: Formalize building literals with a trait --- crates/wasm-bindgen-macro/src/ast.rs | 268 ++-------------------- crates/wasm-bindgen-macro/src/lib.rs | 5 +- crates/wasm-bindgen-macro/src/literal.rs | 273 +++++++++++++++++++++++ 3 files changed, 295 insertions(+), 251 deletions(-) create mode 100644 crates/wasm-bindgen-macro/src/literal.rs diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index aa5d692a6..11089b976 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -1,5 +1,4 @@ -use std::collections::BTreeSet; - +use literal::{self, Literal}; use proc_macro2::Span; use quote::{Tokens, ToTokens}; use shared; @@ -50,7 +49,12 @@ pub struct Struct { pub struct Enum { pub name: syn::Ident, - pub variants: Vec<(syn::Ident, u32)> + pub variants: Vec +} + +pub struct Variant { + pub name: syn::Ident, + pub value: u32, } pub struct ImportedType { @@ -231,7 +235,10 @@ impl Program { _ => panic!("Enums may only have number literal values") }; - (v.ident, value) + Variant { + name: v.ident, + value, + } }).collect(); self.enums.push(Enum { name: item.ident, @@ -334,34 +341,12 @@ impl Program { }); } - pub fn wbg_literal(&self, dst: &mut Tokens) -> usize { + pub fn literal(&self, dst: &mut Tokens) -> usize { let mut tmp = Tokens::new(); let cnt = { - let mut a = LiteralBuilder { - dst: &mut tmp, - cnt: 0, - }; - a.fields(&[ - ("exports", &|a| a.list(&self.exports, Export::wbg_literal)), - ("imports", &|a| a.list(&self.imports, Import::wbg_literal)), - ("enums", &|a| a.list(&self.enums, Enum::wbg_literal)), - ("custom_type_names", &|a| { - let names = self.exports.iter() - .filter_map(|e| e.class) - .chain(self.structs.iter().map(|s| s.name)) - .collect::>(); - a.list(&names, |s, a| { - let val = shared::name_to_descriptor(s.as_ref()); - a.fields(&[ - ("descriptor", &|a| a.char(val)), - ("name", &|a| a.str(s.as_ref())) - ]); - }) - }), - ("version", &|a| a.str(&shared::version())), - ("schema_version", &|a| a.str(&shared::SCHEMA_VERSION)), - ]); - a.cnt + let mut a = literal::LiteralBuilder::new(&mut tmp); + Literal::literal(self, &mut a); + a.finish() }; let cnt = cnt as u32; (quote! { @@ -442,19 +427,6 @@ impl Function { rust_attrs: attrs, }, mutable) } - - fn wbg_literal(&self, a: &mut LiteralBuilder) { - a.fields(&[ - ("name", &|a| a.str(self.name.as_ref())), - ("arguments", &|a| a.list(&self.arguments, Type::wbg_literal)), - ("ret", &|a| { - match self.ret { - Some(ref s) => s.wbg_literal(a), - None => a.append("null"), - } - }), - ]); - } } pub fn extract_path_ident(path: &syn::Path) -> Option { @@ -527,43 +499,6 @@ impl Type { Type::ByValue(ty.clone()) } - - fn wbg_literal(&self, a: &mut LiteralBuilder) { - match *self { - Type::Vector(VectorType::String, true) => a.char(shared::TYPE_STRING), - Type::Vector(VectorType::String, false) => a.char(shared::TYPE_BORROWED_STR), - Type::Vector(VectorType::U8, true) => a.char(shared::TYPE_VECTOR_U8), - Type::Vector(VectorType::U8, false) => a.char(shared::TYPE_SLICE_U8), - Type::Vector(VectorType::I8, true) => a.char(shared::TYPE_VECTOR_I8), - Type::Vector(VectorType::I8, false) => a.char(shared::TYPE_SLICE_I8), - Type::Vector(VectorType::U16, true) => a.char(shared::TYPE_VECTOR_U16), - Type::Vector(VectorType::U16, false) => a.char(shared::TYPE_SLICE_U16), - Type::Vector(VectorType::I16, true) => a.char(shared::TYPE_VECTOR_I16), - Type::Vector(VectorType::I16, false) => a.char(shared::TYPE_SLICE_I16), - Type::Vector(VectorType::U32, true) => a.char(shared::TYPE_VECTOR_U32), - Type::Vector(VectorType::U32, false) => a.char(shared::TYPE_SLICE_U32), - Type::Vector(VectorType::I32, true) => a.char(shared::TYPE_VECTOR_I32), - Type::Vector(VectorType::I32, false) => a.char(shared::TYPE_SLICE_I32), - Type::Vector(VectorType::F32, true) => a.char(shared::TYPE_VECTOR_F32), - Type::Vector(VectorType::F32, false) => a.char(shared::TYPE_SLICE_F32), - Type::Vector(VectorType::F64, true) => a.char(shared::TYPE_VECTOR_F64), - Type::Vector(VectorType::F64, false) => a.char(shared::TYPE_SLICE_F64), - Type::Vector(VectorType::JsValue, true) => a.char(shared::TYPE_VECTOR_JSVALUE), - Type::Vector(VectorType::JsValue, false) => panic!("Slices of JsValues not supported"), - Type::ByValue(ref t) => { - a.as_char(my_quote! { - <#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR - }); - } - Type::ByRef(ref ty) | - Type::ByMutRef(ref ty) => { - a.as_char(my_quote! { - (<#ty as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR | - ::wasm_bindgen::convert::DESCRIPTOR_CUSTOM_REF_FLAG) - }); - } - } - } } impl Export { @@ -592,191 +527,26 @@ impl Export { }; syn::LitStr::new(&name, Span::def_site()) } - - fn wbg_literal(&self, a: &mut LiteralBuilder) { - a.fields(&[ - ("class", &|a| { - match self.class { - Some(ref s) => a.str(s.as_ref()), - None => a.append("null"), - } - }), - ("method", &|a| a.bool(self.method)), - ("function", &|a| self.function.wbg_literal(a)), - ]); - } } impl Import { - fn wbg_literal(&self, a: &mut LiteralBuilder) { - let mut method = false; - let mut js_new = false; - let mut statik = false; - let mut class_name = None; - match self.kind { - ImportKind::Method { ref class, .. } => { - method = true; - class_name = Some(class); - } - ImportKind::JsConstructor { ref class, .. } => { - js_new = true; - class_name = Some(class); - } - ImportKind::Static { ref class, .. } => { - statik = true; - class_name = Some(class); - } - ImportKind::Normal => {} - } - - let mut getter = None; - let mut setter = None; - - if self.function.opts.getter() { - getter = Some(self.infer_getter_property()); - } - if self.function.opts.setter() { - setter = Some(self.infer_setter_property()); - } - a.fields(&[ - ("module", &|a| { - match self.module { - Some(ref s) => a.str(s), - None => a.append("null"), - } - }), - ("catch", &|a| a.bool(self.function.opts.catch())), - ("method", &|a| a.bool(method)), - ("js_new", &|a| a.bool(js_new)), - ("statik", &|a| a.bool(statik)), - ("getter", &|a| { - match getter { - Some(ref s) => a.str(s), - None => a.append("null"), - } - }), - ("setter", &|a| { - match setter { - Some(ref s) => a.str(s), - None => a.append("null"), - } - }), - ("function", &|a| self.function.wbg_literal(a)), - ("class", &|a| { - match class_name { - Some(s) => a.str(s), - None => a.append("null"), - } - }), - ]); - } - - fn infer_getter_property(&self) -> String { + pub fn infer_getter_property(&self) -> String { self.function.name.as_ref().to_string() } - fn infer_setter_property(&self) -> String { + pub fn infer_setter_property(&self) -> String { let name = self.function.name.as_ref(); assert!(name.starts_with("set_"), "setters must start with `set_`"); name[4..].to_string() } } -impl Enum { - fn wbg_literal(&self, a: &mut LiteralBuilder) { - a.fields(&[ - ("name", &|a| a.str(self.name.as_ref())), - ("variants", &|a| a.list(&self.variants, |v, a| { - let &(name, value) = v; - a.fields(&[("name", &|a| a.str(name.as_ref())), - ("value", &|a| a.append(&format!("{}", value)))]) - })), - ]); - } -} - impl Struct { fn from(s: syn::ItemStruct, _opts: BindgenAttrs) -> Struct { Struct { name: s.ident } } } -struct LiteralBuilder<'a> { - dst: &'a mut Tokens, - cnt: usize, -} - -impl<'a> LiteralBuilder<'a> { - fn char_lit(&mut self, c: char) { - if self.cnt > 0 { - ::syn::token::Comma::default().to_tokens(self.dst); - } - self.cnt += 1; - (c as u32).to_tokens(self.dst); - } - - fn append(&mut self, s: &str) { - for c in s.chars() { - self.char_lit(c); - } - } - - fn str(&mut self, s: &str) { - self.append("\""); - self.append(s); - self.append("\""); - } - - fn bool(&mut self, v: bool) { - if v { - self.append("true") - } else { - self.append("false") - } - } - - fn char(&mut self, s: char) { - self.append("\""); - self.char_lit(s); - self.append("\""); - } - - fn as_char(&mut self, tokens: Tokens) { - self.append("\""); - ::syn::token::Comma::default().to_tokens(self.dst); - tokens.to_tokens(self.dst); - self.cnt += 1; - self.append("\""); - } - - fn fields(&mut self, fields: &[(&str, &Fn(&mut Self))]) { - self.append("{"); - for (i, &(field, cb)) in fields.iter().enumerate() { - if i > 0 { - self.append(","); - } - self.str(field); - self.append(":"); - cb(self); - } - self.append("}"); - } - - fn list(&mut self, list: T, mut cb: F) - where F: FnMut(T::Item, &mut Self), - T: IntoIterator, - { - self.append("["); - for (i, element) in list.into_iter().enumerate() { - if i > 0 { - self.append(","); - } - cb(element, self); - } - self.append("]"); - } -} - #[derive(Default)] pub struct BindgenAttrs { attrs: Vec, @@ -848,7 +618,7 @@ impl BindgenAttrs { .next() } - fn getter(&self) -> bool { + pub fn getter(&self) -> bool { self.attrs.iter() .any(|a| { match *a { @@ -858,7 +628,7 @@ impl BindgenAttrs { }) } - fn setter(&self) -> bool { + pub fn setter(&self) -> bool { self.attrs.iter() .any(|a| { match *a { diff --git a/crates/wasm-bindgen-macro/src/lib.rs b/crates/wasm-bindgen-macro/src/lib.rs index 1a8bd01ea..e65dbd357 100755 --- a/crates/wasm-bindgen-macro/src/lib.rs +++ b/crates/wasm-bindgen-macro/src/lib.rs @@ -23,6 +23,7 @@ macro_rules! my_quote { } mod ast; +mod literal; #[proc_macro_attribute] pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { @@ -96,7 +97,7 @@ impl ToTokens for ast::Program { let generated_static_name = syn::Ident::from(generated_static_name); let mut generated_static_value = Tokens::new(); - let generated_static_length = self.wbg_literal(&mut generated_static_value); + let generated_static_length = self.literal(&mut generated_static_value); (my_quote! { #[no_mangle] @@ -559,7 +560,7 @@ impl ToTokens for ast::Enum { let incoming_u32 = quote! { n }; let enum_name_as_string = enum_name.to_string(); let cast_clauses = self.variants.iter().map(|variant| { - let &(variant_name, _) = variant; + let variant_name = &variant.name; quote! { if #incoming_u32 == #enum_name::#variant_name as u32 { #enum_name::#variant_name diff --git a/crates/wasm-bindgen-macro/src/literal.rs b/crates/wasm-bindgen-macro/src/literal.rs new file mode 100644 index 000000000..4f3f6a0eb --- /dev/null +++ b/crates/wasm-bindgen-macro/src/literal.rs @@ -0,0 +1,273 @@ +use ast; +use proc_macro2::Span; +use quote::{ToTokens, Tokens}; +use shared; +use std::collections::BTreeSet; + +pub struct LiteralBuilder<'a> { + dst: &'a mut Tokens, + cnt: usize, +} + +impl<'a> LiteralBuilder<'a> { + pub fn new(dst: &'a mut Tokens) -> LiteralBuilder<'a> { + LiteralBuilder { + dst, + cnt: 0, + } + } + + pub fn finish(self) -> usize { + self.cnt + } + + fn char_lit(&mut self, c: char) { + if self.cnt > 0 { + ::syn::token::Comma::default().to_tokens(self.dst); + } + self.cnt += 1; + (c as u32).to_tokens(self.dst); + } + + fn append(&mut self, s: &str) { + for c in s.chars() { + self.char_lit(c); + } + } + + fn str(&mut self, s: &str) { + self.append("\""); + self.append(s); + self.append("\""); + } + + fn bool(&mut self, v: bool) { + if v { + self.append("true") + } else { + self.append("false") + } + } + + fn char(&mut self, s: char) { + self.append("\""); + self.char_lit(s); + self.append("\""); + } + + fn as_char(&mut self, tokens: Tokens) { + self.append("\""); + ::syn::token::Comma::default().to_tokens(self.dst); + tokens.to_tokens(self.dst); + self.cnt += 1; + self.append("\""); + } + + pub fn fields(&mut self, fields: &[(&str, &Fn(&mut Self))]) { + self.append("{"); + for (i, &(field, cb)) in fields.iter().enumerate() { + if i > 0 { + self.append(","); + } + self.str(field); + self.append(":"); + cb(self); + } + self.append("}"); + } + + pub fn list_of<'b, T, U>(&mut self, list: T) + where + T: IntoIterator, + U: 'b + Literal, + { + self.list(list, U::literal) + } + + fn list(&mut self, list: T, mut cb: F) + where F: FnMut(T::Item, &mut Self), + T: IntoIterator, + { + self.append("["); + for (i, element) in list.into_iter().enumerate() { + if i > 0 { + self.append(","); + } + cb(element, self); + } + self.append("]"); + } +} + +pub trait Literal { + fn literal(&self, a: &mut LiteralBuilder); +} + +impl Literal for ast::Program { + fn literal(&self, a: &mut LiteralBuilder) { + a.fields(&[ + ("exports", &|a| a.list_of(&self.exports)), + ("imports", &|a| a.list_of(&self.imports)), + ("enums", &|a| a.list_of(&self.enums)), + ("custom_type_names", &|a| { + let names = self.exports + .iter() + .filter_map(|e| e.class) + .chain(self.structs.iter().map(|s| s.name)) + .collect::>(); + a.list(&names, |s, a| { + let val = shared::name_to_descriptor(s.as_ref()); + a.fields(&[ + ("descriptor", &|a| a.char(val)), + ("name", &|a| a.str(s.as_ref())), + ]); + }) + }), + ("version", &|a| a.str(&shared::version())), + ("schema_version", &|a| a.str(&shared::SCHEMA_VERSION)), + ]); + } +} + +impl Literal for ast::Function { + fn literal(&self, a: &mut LiteralBuilder) { + a.fields(&[ + ("name", &|a| a.str(self.name.as_ref())), + ("arguments", &|a| a.list_of(&self.arguments)), + ("ret", &|a| match self.ret { + Some(ref s) => s.literal(a), + None => a.append("null"), + }), + ]); + } +} + +impl Literal for ast::Type { + fn literal(&self, a: &mut LiteralBuilder) { + match *self { + ast::Type::Vector(ast::VectorType::String, true) => a.char(shared::TYPE_STRING), + ast::Type::Vector(ast::VectorType::String, false) => a.char(shared::TYPE_BORROWED_STR), + ast::Type::Vector(ast::VectorType::U8, true) => a.char(shared::TYPE_VECTOR_U8), + ast::Type::Vector(ast::VectorType::U8, false) => a.char(shared::TYPE_SLICE_U8), + ast::Type::Vector(ast::VectorType::I8, true) => a.char(shared::TYPE_VECTOR_I8), + ast::Type::Vector(ast::VectorType::I8, false) => a.char(shared::TYPE_SLICE_I8), + ast::Type::Vector(ast::VectorType::U16, true) => a.char(shared::TYPE_VECTOR_U16), + ast::Type::Vector(ast::VectorType::U16, false) => a.char(shared::TYPE_SLICE_U16), + ast::Type::Vector(ast::VectorType::I16, true) => a.char(shared::TYPE_VECTOR_I16), + ast::Type::Vector(ast::VectorType::I16, false) => a.char(shared::TYPE_SLICE_I16), + ast::Type::Vector(ast::VectorType::U32, true) => a.char(shared::TYPE_VECTOR_U32), + ast::Type::Vector(ast::VectorType::U32, false) => a.char(shared::TYPE_SLICE_U32), + ast::Type::Vector(ast::VectorType::I32, true) => a.char(shared::TYPE_VECTOR_I32), + ast::Type::Vector(ast::VectorType::I32, false) => a.char(shared::TYPE_SLICE_I32), + ast::Type::Vector(ast::VectorType::F32, true) => a.char(shared::TYPE_VECTOR_F32), + ast::Type::Vector(ast::VectorType::F32, false) => a.char(shared::TYPE_SLICE_F32), + ast::Type::Vector(ast::VectorType::F64, true) => a.char(shared::TYPE_VECTOR_F64), + ast::Type::Vector(ast::VectorType::F64, false) => a.char(shared::TYPE_SLICE_F64), + ast::Type::Vector(ast::VectorType::JsValue, true) => { + a.char(shared::TYPE_VECTOR_JSVALUE) + } + ast::Type::Vector(ast::VectorType::JsValue, false) => { + panic!("Slices of JsValues not supported") + } + ast::Type::ByValue(ref t) => { + a.as_char(my_quote! { + <#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR + }); + } + ast::Type::ByRef(ref ty) | ast::Type::ByMutRef(ref ty) => { + a.as_char(my_quote! { + (<#ty as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR | + ::wasm_bindgen::convert::DESCRIPTOR_CUSTOM_REF_FLAG) + }); + } + } + } +} + +impl Literal for ast::Export { + fn literal(&self, a: &mut LiteralBuilder) { + a.fields(&[ + ("class", &|a| match self.class { + Some(ref s) => a.str(s.as_ref()), + None => a.append("null"), + }), + ("method", &|a| a.bool(self.method)), + ("function", &|a| self.function.literal(a)), + ]); + } +} + +impl Literal for ast::Import { + fn literal(&self, a: &mut LiteralBuilder) { + let mut method = false; + let mut js_new = false; + let mut statik = false; + let mut class_name = None; + match self.kind { + ast::ImportKind::Method { ref class, .. } => { + method = true; + class_name = Some(class); + } + ast::ImportKind::JsConstructor { ref class, .. } => { + js_new = true; + class_name = Some(class); + } + ast::ImportKind::Static { ref class, .. } => { + statik = true; + class_name = Some(class); + } + ast::ImportKind::Normal => {} + } + + let mut getter = None; + let mut setter = None; + + if self.function.opts.getter() { + getter = Some(self.infer_getter_property()); + } + if self.function.opts.setter() { + setter = Some(self.infer_setter_property()); + } + a.fields(&[ + ("module", &|a| match self.module { + Some(ref s) => a.str(s), + None => a.append("null"), + }), + ("catch", &|a| a.bool(self.function.opts.catch())), + ("method", &|a| a.bool(method)), + ("js_new", &|a| a.bool(js_new)), + ("statik", &|a| a.bool(statik)), + ("getter", &|a| match getter { + Some(ref s) => a.str(s), + None => a.append("null"), + }), + ("setter", &|a| match setter { + Some(ref s) => a.str(s), + None => a.append("null"), + }), + ("function", &|a| self.function.literal(a)), + ("class", &|a| match class_name { + Some(s) => a.str(s), + None => a.append("null"), + }), + ]); + } +} + +impl Literal for ast::Enum { + fn literal(&self, a: &mut LiteralBuilder) { + a.fields(&[ + ("name", &|a| a.str(self.name.as_ref())), + ("variants", &|a| a.list_of(&self.variants)), + ]); + } +} + +impl Literal for ast::Variant { + fn literal(&self, a: &mut LiteralBuilder) { + a.fields(&[ + ("name", &|a| a.str(self.name.as_ref())), + ("value", &|a| a.append(&format!("{}", self.value))), + ]) + } +}