From 01c31cb33dd3eb5c4bc7ada1130491434b291c71 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 22 Feb 2018 00:55:11 +0100 Subject: [PATCH 01/10] Create Rust wrapping code for enums --- crates/wasm-bindgen-macro/src/ast.rs | 35 ++++++++++++++++++++++++++- crates/wasm-bindgen-macro/src/lib.rs | 30 +++++++++++++++++++++++ crates/wasm-bindgen-shared/src/lib.rs | 1 + 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index 0920cdac..1899a6dc 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -9,6 +9,7 @@ use syn; pub struct Program { pub exports: Vec, pub imports: Vec, + pub enums: Vec, pub imported_types: Vec<(syn::Visibility, syn::Ident)>, pub structs: Vec, } @@ -47,6 +48,10 @@ pub struct Struct { pub name: syn::Ident, } +pub struct Enum { + pub name: syn::Ident +} + pub enum Type { // special Vector(VectorType, bool), @@ -110,8 +115,13 @@ impl Program { let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs)); self.push_foreign_mod(f, opts); } + syn::Item::Enum(mut e) => { + let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut e.attrs)); + e.to_tokens(tokens); + self.push_enum(e, opts); + } _ => panic!("#[wasm_bindgen] can only be applied to a function, \ - struct, impl, or extern block"), + struct, enum, impl, or extern block"), } } @@ -195,6 +205,28 @@ impl Program { } } + pub fn push_enum(&mut self, item: syn::ItemEnum, opts: BindgenAttrs) { + match item.vis { + syn::Visibility::Public(_) => {} + _ => panic!("only public enums are allowed"), + } + + let all_fields_unit = item.variants.iter().all(|ref v| { + match v.fields { + syn::Fields::Unit => true, + _ => false + } + }); + if all_fields_unit { + self.enums.push(Enum { + name: item.ident + }); + + } else { + panic!("Only C-Style enums allowed") + } + } + pub fn push_foreign_fn(&mut self, mut f: syn::ForeignItemFn, module_opts: &BindgenAttrs) { @@ -301,6 +333,7 @@ impl Program { let names = self.exports.iter() .filter_map(|e| e.class) .chain(self.structs.iter().map(|s| s.name)) + .chain(self.enums.iter().map(|s| s.name)) .collect::>(); a.list(&names, |s, a| { let val = shared::name_to_descriptor(s.as_ref()); diff --git a/crates/wasm-bindgen-macro/src/lib.rs b/crates/wasm-bindgen-macro/src/lib.rs index 1878bb00..039d23ff 100644 --- a/crates/wasm-bindgen-macro/src/lib.rs +++ b/crates/wasm-bindgen-macro/src/lib.rs @@ -50,6 +50,9 @@ fn generate_wrappers(program: ast::Program, tokens: &mut Tokens) { for i in program.imports.iter() { bindgen_import(i, tokens); } + for e in program.enums.iter() { + bindgen_enum(e, tokens); + } for &(ref vis, ref t) in program.imported_types.iter() { bindgen_imported_type(vis, t, tokens); } @@ -510,3 +513,30 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) { invocation.to_tokens(tokens); } } + +fn bindgen_enum(e: &ast::Enum, into: &mut Tokens) { + let name = &e.name; + let c = shared::name_to_descriptor(name.as_ref()) as u32; + (my_quote! { + impl #name { + fn from_usize(n: usize) -> #name { + #name::Blue + } + } + impl ::wasm_bindgen::convert::WasmBoundary for #name { + type Js = u32; + const DESCRIPTOR: u32 = #c; + + fn into_js(self) -> u32 { + Box::into_raw(Box::new(self as u32)) as u32 + } + + unsafe fn from_js(js: u32) -> Self { + let js = js as *mut usize; + ::wasm_bindgen::__rt::assert_not_null(js); + let js = Box::from_raw(js); + #name::from_usize(*js) + } + } + }).to_tokens(into); +} diff --git a/crates/wasm-bindgen-shared/src/lib.rs b/crates/wasm-bindgen-shared/src/lib.rs index 7fe56b5d..2047ce99 100644 --- a/crates/wasm-bindgen-shared/src/lib.rs +++ b/crates/wasm-bindgen-shared/src/lib.rs @@ -77,6 +77,7 @@ pub fn mangled_import_name(struct_: Option<&str>, f: &str) -> String { pub type Type = char; +pub const TYPE_ENUM: char = '\u{5d}'; pub const TYPE_NUMBER: char = '\u{5e}'; pub const TYPE_BORROWED_STR: char = '\u{5f}'; pub const TYPE_STRING: char = '\u{60}'; From 45543c545e772dc5c4040b9fdfe821f0ab6a0a71 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 22 Feb 2018 10:55:44 +0100 Subject: [PATCH 02/10] Pass numbers in js as enums to Rust successfully --- crates/wasm-bindgen-cli-support/src/js.rs | 3 ++- crates/wasm-bindgen-macro/src/ast.rs | 22 ++++++++--------- crates/wasm-bindgen-macro/src/lib.rs | 29 +++++++++++++++-------- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/crates/wasm-bindgen-cli-support/src/js.rs b/crates/wasm-bindgen-cli-support/src/js.rs index 3108beb6..37ec69fc 100644 --- a/crates/wasm-bindgen-cli-support/src/js.rs +++ b/crates/wasm-bindgen-cli-support/src/js.rs @@ -1051,7 +1051,8 @@ impl<'a, 'b> SubContext<'a, 'b> { passed_args.push_str(arg); }; match *arg { - shared::TYPE_NUMBER => { + shared::TYPE_ENUM | shared::TYPE_NUMBER => { + // TODO: TS for Enum dst_ts.push_str(": number"); if self.cx.config.debug { self.cx.expose_assert_num(); diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index 1899a6dc..2904ed0c 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -49,7 +49,8 @@ pub struct Struct { } pub struct Enum { - pub name: syn::Ident + pub name: syn::Ident, + pub variants: Vec } pub enum Type { @@ -211,20 +212,17 @@ impl Program { _ => panic!("only public enums are allowed"), } - let all_fields_unit = item.variants.iter().all(|ref v| { + let variants = item.variants.iter().map(|ref v| { match v.fields { - syn::Fields::Unit => true, - _ => false + syn::Fields::Unit => (), + _ => panic!("Only C-Style enums allowed") } + v.ident + }).collect(); + self.enums.push(Enum { + name: item.ident, + variants }); - if all_fields_unit { - self.enums.push(Enum { - name: item.ident - }); - - } else { - panic!("Only C-Style enums allowed") - } } pub fn push_foreign_fn(&mut self, diff --git a/crates/wasm-bindgen-macro/src/lib.rs b/crates/wasm-bindgen-macro/src/lib.rs index 039d23ff..c47d362e 100644 --- a/crates/wasm-bindgen-macro/src/lib.rs +++ b/crates/wasm-bindgen-macro/src/lib.rs @@ -516,26 +516,35 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) { fn bindgen_enum(e: &ast::Enum, into: &mut Tokens) { let name = &e.name; - let c = shared::name_to_descriptor(name.as_ref()) as u32; - (my_quote! { - impl #name { - fn from_usize(n: usize) -> #name { - #name::Blue + let c = shared::TYPE_ENUM as u32; + let incoming_u32 = quote! { n }; + let name_as_string = name.to_string(); + let cast_clauses = e.variants.iter().map(|ident| { + quote! { + if #incoming_u32 == #name::#ident as u32 { + #name::#ident } } + }); + (my_quote! { + impl #name { + fn from_u32(#incoming_u32: u32) -> #name { + #(#cast_clauses else)* { + wasm_bindgen::throw(&format!("Could not cast {} as {}", #incoming_u32, #name_as_string)); + } + } + } + impl ::wasm_bindgen::convert::WasmBoundary for #name { type Js = u32; const DESCRIPTOR: u32 = #c; fn into_js(self) -> u32 { - Box::into_raw(Box::new(self as u32)) as u32 + self as u32 } unsafe fn from_js(js: u32) -> Self { - let js = js as *mut usize; - ::wasm_bindgen::__rt::assert_not_null(js); - let js = Box::from_raw(js); - #name::from_usize(*js) + #name::from_u32(js) } } }).to_tokens(into); From f11121b0954f9b2eb66d968ea3f0282318e73b12 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 22 Feb 2018 12:01:38 +0100 Subject: [PATCH 03/10] Generate enum js code --- crates/wasm-bindgen-cli-support/src/js.rs | 16 +++++++++++ crates/wasm-bindgen-macro/src/ast.rs | 34 ++++++++++++++++++----- crates/wasm-bindgen-shared/src/lib.rs | 12 ++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/crates/wasm-bindgen-cli-support/src/js.rs b/crates/wasm-bindgen-cli-support/src/js.rs index 37ec69fc..56a82442 100644 --- a/crates/wasm-bindgen-cli-support/src/js.rs +++ b/crates/wasm-bindgen-cli-support/src/js.rs @@ -978,6 +978,9 @@ impl<'a, 'b> SubContext<'a, 'b> { for f in self.program.imports.iter() { self.generate_import(f); } + for e in self.program.enums.iter() { + self.generate_enum(e); + } } pub fn generate_export(&mut self, export: &shared::Export) { @@ -1424,6 +1427,19 @@ impl<'a, 'b> SubContext<'a, 'b> { self.cx.globals.push_str(&dst); self.cx.globals.push_str("\n"); } + + pub fn generate_enum(&mut self, enum_: &shared::Enum) { + let mut variants = String::new(); + + let mut value = 0; + for variant in enum_.variants.iter() { + variants.push_str(&format!("{}:{},", variant.name, value)); + value = value + 1; + } + self.cx.globals.push_str(&format!("export const {} = {{", enum_.name)); + self.cx.globals.push_str(&variants); + self.cx.globals.push_str("}\n"); + } } struct VectorType { diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index 2904ed0c..5b75e647 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -327,17 +327,24 @@ impl Program { 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() + let struct_descriptors = self.exports.iter() .filter_map(|e| e.class) .chain(self.structs.iter().map(|s| s.name)) - .chain(self.enums.iter().map(|s| s.name)) - .collect::>(); - a.list(&names, |s, a| { - let val = shared::name_to_descriptor(s.as_ref()); + .map(|n| { + let val = shared::name_to_descriptor(n.as_ref()); + (val, n.to_string()) + }); + let enum_descriptors = self.enums.iter().map(|e| { + (shared::TYPE_ENUM, e.name.to_string()) + }); + let descriptors = struct_descriptors.chain(enum_descriptors).collect::>(); + + a.list(&descriptors, |s, a| { a.fields(&[ - ("descriptor", &|a| a.char(val)), - ("name", &|a| a.str(s.as_ref())) + ("descriptor", &|a| a.char(s.0)), + ("name", &|a| a.str(&s.1)) ]); }) }), @@ -665,6 +672,19 @@ impl Import { } } +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| { + a.fields(&[ + ("name", &|a| a.str(v.as_ref())) + ]) + })), + ]); + } +} + impl Struct { fn from(s: syn::ItemStruct, _opts: BindgenAttrs) -> Struct { Struct { name: s.ident } diff --git a/crates/wasm-bindgen-shared/src/lib.rs b/crates/wasm-bindgen-shared/src/lib.rs index 2047ce99..7d85eeeb 100644 --- a/crates/wasm-bindgen-shared/src/lib.rs +++ b/crates/wasm-bindgen-shared/src/lib.rs @@ -8,6 +8,7 @@ use std::hash::{Hash, Hasher}; #[derive(Deserialize)] pub struct Program { pub exports: Vec, + pub enums: Vec, pub imports: Vec, pub custom_type_names: Vec, } @@ -32,6 +33,17 @@ pub struct Export { pub function: Function, } +#[derive(Deserialize)] +pub struct Enum { + pub name: String, + pub variants: Vec, +} + +#[derive(Deserialize)] +pub struct EnumVariant { + pub name: String +} + #[derive(Deserialize)] pub struct Function { pub name: String, From 7f8316f89b5f593b4ad7e82a0357cec4956b8606 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 22 Feb 2018 12:08:28 +0100 Subject: [PATCH 04/10] Clean up warnings --- crates/wasm-bindgen-macro/src/ast.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index 5b75e647..9862f5ad 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -206,7 +206,7 @@ impl Program { } } - pub fn push_enum(&mut self, item: syn::ItemEnum, opts: BindgenAttrs) { + pub fn push_enum(&mut self, item: syn::ItemEnum, _opts: BindgenAttrs) { match item.vis { syn::Visibility::Public(_) => {} _ => panic!("only public enums are allowed"), @@ -329,22 +329,16 @@ impl Program { ("imports", &|a| a.list(&self.imports, Import::wbg_literal)), ("enums", &|a| a.list(&self.enums, Enum::wbg_literal)), ("custom_type_names", &|a| { - let struct_descriptors = self.exports.iter() + let names = self.exports.iter() .filter_map(|e| e.class) .chain(self.structs.iter().map(|s| s.name)) - .map(|n| { - let val = shared::name_to_descriptor(n.as_ref()); - (val, n.to_string()) - }); - let enum_descriptors = self.enums.iter().map(|e| { - (shared::TYPE_ENUM, e.name.to_string()) - }); - let descriptors = struct_descriptors.chain(enum_descriptors).collect::>(); + .collect::>(); - a.list(&descriptors, |s, a| { + a.list(&names, |s, a| { + let val = shared::name_to_descriptor(s.as_ref()); a.fields(&[ - ("descriptor", &|a| a.char(s.0)), - ("name", &|a| a.str(&s.1)) + ("descriptor", &|a| a.char(val)), + ("name", &|a| a.str(&s.as_ref())) ]); }) }), From b78343a5511f068944e02ec427f41d7444ac28e9 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 22 Feb 2018 12:11:47 +0100 Subject: [PATCH 05/10] Fix enum formatting issues --- crates/wasm-bindgen-macro/src/ast.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index 9862f5ad..bde31d8d 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -333,12 +333,11 @@ impl Program { .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())) + ("name", &|a| a.str(s.as_ref())) ]); }) }), @@ -671,9 +670,7 @@ impl Enum { a.fields(&[ ("name", &|a| a.str(self.name.as_ref())), ("variants", &|a| a.list(&self.variants, |v, a| { - a.fields(&[ - ("name", &|a| a.str(v.as_ref())) - ]) + a.fields(&[("name", &|a| a.str(v.as_ref()))]) })), ]); } From 3a270b651074c682d7500e28a2782603d7c7e340 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 23 Feb 2018 17:30:18 +0100 Subject: [PATCH 06/10] Add test for enums --- crates/wasm-bindgen-cli-support/src/js.rs | 23 ++++++++++-- tests/enums.rs | 44 +++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 tests/enums.rs diff --git a/crates/wasm-bindgen-cli-support/src/js.rs b/crates/wasm-bindgen-cli-support/src/js.rs index 56a82442..d191d0c7 100644 --- a/crates/wasm-bindgen-cli-support/src/js.rs +++ b/crates/wasm-bindgen-cli-support/src/js.rs @@ -1054,8 +1054,15 @@ impl<'a, 'b> SubContext<'a, 'b> { passed_args.push_str(arg); }; match *arg { - shared::TYPE_ENUM | shared::TYPE_NUMBER => { - // TODO: TS for Enum + shared::TYPE_ENUM => { + dst_ts.push_str(&format!(": {}", "any")); + if self.cx.config.debug { + self.cx.expose_assert_num(); + arg_conversions.push_str(&format!("_assertNum({});\n", name)); + } + pass(&name) + } + shared::TYPE_NUMBER => { dst_ts.push_str(": number"); if self.cx.config.debug { self.cx.expose_assert_num(); @@ -1160,6 +1167,10 @@ impl<'a, 'b> SubContext<'a, 'b> { dst_ts.push_str(": void"); format!("return ret;") } + Some(shared::TYPE_ENUM) => { + dst_ts.push_str(": any"); + format!("return ret;") + } Some(shared::TYPE_NUMBER) => { dst_ts.push_str(": number"); format!("return ret;") @@ -1439,6 +1450,14 @@ impl<'a, 'b> SubContext<'a, 'b> { self.cx.globals.push_str(&format!("export const {} = {{", enum_.name)); self.cx.globals.push_str(&variants); self.cx.globals.push_str("}\n"); + self.cx.typescript.push_str(&format!("export enum {} {{", enum_.name)); + + variants.clear(); + for variant in enum_.variants.iter() { + variants.push_str(&format!("{},", variant.name)); + } + self.cx.typescript.push_str(&variants); + self.cx.typescript.push_str("}\n"); } } diff --git a/tests/enums.rs b/tests/enums.rs new file mode 100644 index 00000000..a1d02188 --- /dev/null +++ b/tests/enums.rs @@ -0,0 +1,44 @@ +extern crate test_support; + +#[test] +fn c_style_enum() { + test_support::project() + .file("src/lib.rs", r#" + #![feature(proc_macro)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + pub enum Color { + Green, + Yellow, + Red, + } + + #[no_mangle] + #[wasm_bindgen] + pub extern fn cycle(color: Color) -> Color { + match color { + Color::Green => Color::Yellow, + Color::Yellow => Color::Red, + Color::Red => Color::Green, + } + } + "#) + .file("test.ts", r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + assert.strictEqual(wasm.Color.Green, 0); + assert.strictEqual(wasm.Color.Yellow, 1); + assert.strictEqual(wasm.Color.Red, 2); + assert.strictEqual(Object.keys(wasm.Color).length, 3); + + assert.strictEqual(wasm.cycle(wasm.Color.Green), wasm.Color.Yellow); + } + "#) + .test(); +} From 71880b8a830e254fa29f6bd84a862b8ee52a552f Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 23 Feb 2018 17:34:08 +0100 Subject: [PATCH 07/10] Enums are numbers --- crates/wasm-bindgen-cli-support/src/js.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/crates/wasm-bindgen-cli-support/src/js.rs b/crates/wasm-bindgen-cli-support/src/js.rs index d191d0c7..c37ab978 100644 --- a/crates/wasm-bindgen-cli-support/src/js.rs +++ b/crates/wasm-bindgen-cli-support/src/js.rs @@ -1054,15 +1054,7 @@ impl<'a, 'b> SubContext<'a, 'b> { passed_args.push_str(arg); }; match *arg { - shared::TYPE_ENUM => { - dst_ts.push_str(&format!(": {}", "any")); - if self.cx.config.debug { - self.cx.expose_assert_num(); - arg_conversions.push_str(&format!("_assertNum({});\n", name)); - } - pass(&name) - } - shared::TYPE_NUMBER => { + shared::TYPE_ENUM | shared::TYPE_NUMBER => { dst_ts.push_str(": number"); if self.cx.config.debug { self.cx.expose_assert_num(); @@ -1168,7 +1160,7 @@ impl<'a, 'b> SubContext<'a, 'b> { format!("return ret;") } Some(shared::TYPE_ENUM) => { - dst_ts.push_str(": any"); + dst_ts.push_str(": number"); format!("return ret;") } Some(shared::TYPE_NUMBER) => { From f7838761923f3db964197bdff12b043a8b6d35c0 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 23 Feb 2018 14:17:53 +0100 Subject: [PATCH 08/10] Support C-Style enums with custom int values --- crates/wasm-bindgen-cli-support/src/js.rs | 4 +--- crates/wasm-bindgen-macro/src/ast.rs | 21 ++++++++++++++++++--- crates/wasm-bindgen-macro/src/lib.rs | 21 +++++++++++---------- crates/wasm-bindgen-shared/src/lib.rs | 3 ++- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/crates/wasm-bindgen-cli-support/src/js.rs b/crates/wasm-bindgen-cli-support/src/js.rs index c37ab978..b19767e2 100644 --- a/crates/wasm-bindgen-cli-support/src/js.rs +++ b/crates/wasm-bindgen-cli-support/src/js.rs @@ -1434,10 +1434,8 @@ impl<'a, 'b> SubContext<'a, 'b> { pub fn generate_enum(&mut self, enum_: &shared::Enum) { let mut variants = String::new(); - let mut value = 0; for variant in enum_.variants.iter() { - variants.push_str(&format!("{}:{},", variant.name, value)); - value = value + 1; + variants.push_str(&format!("{}:{},", variant.name, variant.value)); } self.cx.globals.push_str(&format!("export const {} = {{", enum_.name)); self.cx.globals.push_str(&variants); diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index bde31d8d..7e7ee196 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -50,7 +50,7 @@ pub struct Struct { pub struct Enum { pub name: syn::Ident, - pub variants: Vec + pub variants: Vec<(syn::Ident, u32)> } pub enum Type { @@ -212,12 +212,25 @@ impl Program { _ => panic!("only public enums are allowed"), } + let mut i = 0; let variants = item.variants.iter().map(|ref v| { match v.fields { syn::Fields::Unit => (), _ => panic!("Only C-Style enums allowed") } - v.ident + let value = match v.discriminant { + Some((_, syn::Expr::Lit(syn::ExprLit {attrs: _, lit: syn::Lit::Int(ref int_lit)}))) => { + if int_lit.value() > ::max_value() as u64 { + panic!("Enums can only support numbers that can be represented as u32"); + } + int_lit.value() as u32 + }, + None => i, + _ => panic!("Enums may only have number literal values") + }; + + i = i + 1; + (v.ident, value) }).collect(); self.enums.push(Enum { name: item.ident, @@ -670,7 +683,9 @@ impl Enum { a.fields(&[ ("name", &|a| a.str(self.name.as_ref())), ("variants", &|a| a.list(&self.variants, |v, a| { - a.fields(&[("name", &|a| a.str(v.as_ref()))]) + let &(name, value) = v; + a.fields(&[("name", &|a| a.str(name.as_ref())), + ("value", &|a| a.append(&format!("{}", value)))]) })), ]); } diff --git a/crates/wasm-bindgen-macro/src/lib.rs b/crates/wasm-bindgen-macro/src/lib.rs index c47d362e..13544e5b 100644 --- a/crates/wasm-bindgen-macro/src/lib.rs +++ b/crates/wasm-bindgen-macro/src/lib.rs @@ -515,27 +515,28 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) { } fn bindgen_enum(e: &ast::Enum, into: &mut Tokens) { - let name = &e.name; + let enum_name = &e.name; let c = shared::TYPE_ENUM as u32; let incoming_u32 = quote! { n }; - let name_as_string = name.to_string(); - let cast_clauses = e.variants.iter().map(|ident| { + let enum_name_as_string = enum_name.to_string(); + let cast_clauses = e.variants.iter().map(|variant| { + let &(variant_name, _) = variant; quote! { - if #incoming_u32 == #name::#ident as u32 { - #name::#ident + if #incoming_u32 == #enum_name::#variant_name as u32 { + #enum_name::#variant_name } } }); (my_quote! { - impl #name { - fn from_u32(#incoming_u32: u32) -> #name { + impl #enum_name { + fn from_u32(#incoming_u32: u32) -> #enum_name { #(#cast_clauses else)* { - wasm_bindgen::throw(&format!("Could not cast {} as {}", #incoming_u32, #name_as_string)); + wasm_bindgen::throw(&format!("Could not cast {} as {}", #incoming_u32, #enum_name_as_string)); } } } - impl ::wasm_bindgen::convert::WasmBoundary for #name { + impl ::wasm_bindgen::convert::WasmBoundary for #enum_name { type Js = u32; const DESCRIPTOR: u32 = #c; @@ -544,7 +545,7 @@ fn bindgen_enum(e: &ast::Enum, into: &mut Tokens) { } unsafe fn from_js(js: u32) -> Self { - #name::from_u32(js) + #enum_name::from_u32(js) } } }).to_tokens(into); diff --git a/crates/wasm-bindgen-shared/src/lib.rs b/crates/wasm-bindgen-shared/src/lib.rs index 7d85eeeb..62d0d220 100644 --- a/crates/wasm-bindgen-shared/src/lib.rs +++ b/crates/wasm-bindgen-shared/src/lib.rs @@ -41,7 +41,8 @@ pub struct Enum { #[derive(Deserialize)] pub struct EnumVariant { - pub name: String + pub name: String, + pub value: u32 } #[derive(Deserialize)] From 3ae6614738723983c669474b26bc586a3e35ca5a Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 23 Feb 2018 17:44:48 +0100 Subject: [PATCH 09/10] Add test for custom values in enums --- tests/enums.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/enums.rs b/tests/enums.rs index a1d02188..26b3646c 100644 --- a/tests/enums.rs +++ b/tests/enums.rs @@ -42,3 +42,46 @@ fn c_style_enum() { "#) .test(); } + +#[test] +fn c_style_enum_with_custom_values() { + test_support::project() + .file("src/lib.rs", r#" + #![feature(proc_macro)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + pub enum Color { + Green = 21, + Yellow = 34, + Red, + } + + #[no_mangle] + #[wasm_bindgen] + pub extern fn cycle(color: Color) -> Color { + match color { + Color::Green => Color::Yellow, + Color::Yellow => Color::Red, + Color::Red => Color::Green, + } + } + "#) + .file("test.ts", r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + assert.strictEqual(wasm.Color.Green, 21); + assert.strictEqual(wasm.Color.Yellow, 34); + assert.strictEqual(wasm.Color.Red, 2); + assert.strictEqual(Object.keys(wasm.Color).length, 3); + + assert.strictEqual(wasm.cycle(wasm.Color.Green), wasm.Color.Yellow); + } + "#) + .test(); +} From f1b300c8aac4e039193c25c12c482dc8f7e51a34 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 23 Feb 2018 19:06:23 +0100 Subject: [PATCH 10/10] get rid of unnecessary mutable var --- crates/wasm-bindgen-macro/src/ast.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index 7e7ee196..7abacbde 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -212,8 +212,7 @@ impl Program { _ => panic!("only public enums are allowed"), } - let mut i = 0; - let variants = item.variants.iter().map(|ref v| { + let variants = item.variants.iter().enumerate().map(|(i, v)| { match v.fields { syn::Fields::Unit => (), _ => panic!("Only C-Style enums allowed") @@ -225,11 +224,10 @@ impl Program { } int_lit.value() as u32 }, - None => i, + None => i as u32, _ => panic!("Enums may only have number literal values") }; - i = i + 1; (v.ident, value) }).collect(); self.enums.push(Enum {