From 164c457b779941b9a211930b7976a3490dabb3ed Mon Sep 17 00:00:00 2001 From: David Huculak Date: Tue, 26 Mar 2024 21:48:18 -0400 Subject: [PATCH 01/13] first working version of enum binding generator --- crates/backend/src/codegen.rs | 138 +++++++++++++------------ crates/cli-support/src/descriptor.rs | 22 +++- crates/cli-support/src/js/binding.rs | 18 ++++ crates/cli-support/src/wit/incoming.rs | 17 +++ crates/cli-support/src/wit/outgoing.rs | 27 +++++ crates/cli-support/src/wit/standard.rs | 9 ++ src/describe.rs | 1 + 7 files changed, 167 insertions(+), 65 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 65c8cd08915..a8bc3f7bd04 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1024,30 +1024,36 @@ impl ToTokens for ast::ImportType { impl ToTokens for ast::ImportEnum { fn to_tokens(&self, tokens: &mut TokenStream) { let vis = &self.vis; - let name = &self.name; - let expect_string = format!("attempted to convert invalid {} into JSValue", name); + let enum_name = &self.name; + let name_str = enum_name.to_string(); + let name_len = name_str.len() as u32; + let name_chars = name_str.chars().map(|c| c as u32); + // let expect_string = format!("attempted to convert invalid {} into JSValue", name); let variants = &self.variants; - let variant_strings = &self.variant_values; + let variant_values = &self.variant_values; + // let variant_strings = &self.variant_values; + let variant_indices = (0..variant_values.len() as u32).collect::>(); + let hole = self.variant_values.len() as u32; let attrs = &self.rust_attrs; - let mut current_idx: usize = 0; - let variant_indexes: Vec = variants - .iter() - .map(|_| { - let this_index = current_idx; - current_idx += 1; - Literal::usize_unsuffixed(this_index) - }) - .collect(); + // let mut current_idx: usize = 0; + // let variant_indexes: Vec = variants + // .iter() + // .map(|_| { + // let this_index = current_idx; + // current_idx += 1; + // Literal::usize_unsuffixed(this_index) + // }) + // .collect(); // Borrow variant_indexes because we need to use it multiple times inside the quote! macro - let variant_indexes_ref = &variant_indexes; + // let variant_indexes_ref = &variant_indexes; // A vector of EnumName::VariantName tokens for this enum let variant_paths: Vec = self .variants .iter() - .map(|v| quote!(#name::#v).into_token_stream()) + .map(|v| quote!(#enum_name::#v).into_token_stream()) .collect(); // Borrow variant_paths because we need to use it multiple times inside the quote! macro @@ -1055,83 +1061,87 @@ impl ToTokens for ast::ImportEnum { let wasm_bindgen = &self.wasm_bindgen; + let describe_variants = self.variant_values.iter().map(|variant_value| { + let length = variant_value.len() as u32; + let chars = variant_value.chars().map(|c| c as u32); + quote! { + inform(#length); + #(inform(#chars);)* + } + }); + let variant_count = self.variant_values.len() as u32; + (quote! { #(#attrs)* - #vis enum #name { - #(#variants = #variant_indexes_ref,)* - #[automatically_derived] - #[doc(hidden)] - __Nonexhaustive, + #[non_exhaustive] + #[repr(u32)] + #vis enum #enum_name { + #(#variants = #variant_indices,)* } #[automatically_derived] - impl #name { - fn from_str(s: &str) -> Option<#name> { - match s { - #(#variant_strings => Some(#variant_paths_ref),)* - _ => None, - } - } - - fn to_str(&self) -> &'static str { - match self { - #(#variant_paths_ref => #variant_strings,)* - #name::__Nonexhaustive => panic!(#expect_string), - } - } - - #vis fn from_js_value(obj: &#wasm_bindgen::JsValue) -> Option<#name> { - obj.as_string().and_then(|obj_str| Self::from_str(obj_str.as_str())) - } - } + impl #wasm_bindgen::convert::IntoWasmAbi for #enum_name { + type Abi = u32; - // It should really be using &str for all of these, but that requires some major changes to cli-support - #[automatically_derived] - impl #wasm_bindgen::describe::WasmDescribe for #name { - fn describe() { - <#wasm_bindgen::JsValue as #wasm_bindgen::describe::WasmDescribe>::describe() + #[inline] + fn into_abi(self) -> u32 { + self as u32 } } #[automatically_derived] - impl #wasm_bindgen::convert::IntoWasmAbi for #name { - type Abi = <#wasm_bindgen::JsValue as #wasm_bindgen::convert::IntoWasmAbi>::Abi; + impl #wasm_bindgen::convert::FromWasmAbi for #enum_name { + type Abi = u32; - #[inline] - fn into_abi(self) -> Self::Abi { - <#wasm_bindgen::JsValue as #wasm_bindgen::convert::IntoWasmAbi>::into_abi(self.into()) + unsafe fn from_abi(val: u32) -> Self { + let s = ::from_abi(val); + match s { + #(#variant_indices => #variant_paths_ref,)* + _ => #wasm_bindgen::throw_str("invalid enum value passed") + } } } #[automatically_derived] - impl #wasm_bindgen::convert::FromWasmAbi for #name { - type Abi = <#wasm_bindgen::JsValue as #wasm_bindgen::convert::FromWasmAbi>::Abi; - - unsafe fn from_abi(js: Self::Abi) -> Self { - let s = <#wasm_bindgen::JsValue as #wasm_bindgen::convert::FromWasmAbi>::from_abi(js); - #name::from_js_value(&s).unwrap_or(#name::__Nonexhaustive) - } + impl #wasm_bindgen::convert::OptionFromWasmAbi for #enum_name { + #[inline] + fn is_none(val: &u32) -> bool { *val == #hole } } #[automatically_derived] - impl #wasm_bindgen::convert::OptionIntoWasmAbi for #name { + impl #wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name { #[inline] - fn none() -> Self::Abi { <::js_sys::Object as #wasm_bindgen::convert::OptionIntoWasmAbi>::none() } + fn none() -> Self::Abi { #hole } } #[automatically_derived] - impl #wasm_bindgen::convert::OptionFromWasmAbi for #name { - #[inline] - fn is_none(abi: &Self::Abi) -> bool { <::js_sys::Object as #wasm_bindgen::convert::OptionFromWasmAbi>::is_none(abi) } + impl #wasm_bindgen::describe::WasmDescribe for #enum_name { + fn describe() { + use #wasm_bindgen::describe::*; + inform(IMPORT_ENUM); + inform(#name_len); + #(inform(#name_chars);)* + inform(#hole); + inform(#variant_count); + #(#describe_variants)* + } } #[automatically_derived] - impl From<#name> for #wasm_bindgen::JsValue { - fn from(obj: #name) -> #wasm_bindgen::JsValue { - #wasm_bindgen::JsValue::from(obj.to_str()) + impl #wasm_bindgen::__rt::core::convert::From<#enum_name> for + #wasm_bindgen::JsValue + { + fn from(value: #enum_name) -> Self { + #wasm_bindgen::JsValue::from_str( + match value { + #(#variant_paths_ref => #variant_values,)* + _ => #wasm_bindgen::throw_str("invalid enum value passed") + } + ) } } - }).to_tokens(tokens); + }) + .to_tokens(tokens); } } diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index 62dd766ac7a..e746a168c8d 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -34,6 +34,7 @@ tys! { EXTERNREF NAMED_EXTERNREF ENUM + IMPORT_ENUM RUST_STRUCT CHAR OPTIONAL @@ -67,7 +68,15 @@ pub enum Descriptor { String, Externref, NamedExternref(String), - Enum { name: String, hole: u32 }, + Enum { + name: String, + hole: u32, + }, + ImportEnum { + name: String, + hole: u32, + variant_values: Vec, + }, RustStruct(String), Char, Option(Box), @@ -156,6 +165,17 @@ impl Descriptor { let hole = get(data); Descriptor::Enum { name, hole } } + IMPORT_ENUM => { + let name = get_string(data); + let hole = get(data); + let variant_count = get(data); + let variant_values = (0..variant_count).map(|_| get_string(data)).collect(); + Descriptor::ImportEnum { + name, + hole, + variant_values, + } + } RUST_STRUCT => { let name = get_string(data); Descriptor::RustStruct(name) diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index ebe4e966c43..9639b587592 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -668,6 +668,23 @@ fn instruction( } } + Instruction::WasmToEnum { variant_values } => { + let index = js.pop(); + + // e.g. ["one","two","three"][someIndex] + let mut result = String::new(); + result.push('['); + for variant in variant_values { + result.push_str(&format!("\"{variant}\",")); + } + result.push(']'); + result.push('['); + result.push_str(&index); + result.push(']'); + + js.push(result) + } + Instruction::MemoryToString(mem) => { let len = js.pop(); let ptr = js.pop(); @@ -1377,6 +1394,7 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String) { AdapterType::NamedExternref(name) => dst.push_str(name), AdapterType::Struct(name) => dst.push_str(name), AdapterType::Enum(name) => dst.push_str(name), + AdapterType::JsEnum { name, .. } => dst.push_str(name), AdapterType::Function => dst.push_str("any"), } } diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index 83bd3b32b16..c741eb70f2b 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -108,6 +108,16 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); }, + Descriptor::ImportEnum { name, .. } => { + self.instruction( + &[AdapterType::Enum(name.clone())], + Instruction::IntToWasm { + input: AdapterType::U32, + output: ValType::I32, + }, + &[AdapterType::I32], + ); + }, Descriptor::Ref(d) => self.incoming_ref(false, d)?, Descriptor::RefMut(d) => self.incoming_ref(true, d)?, Descriptor::Option(d) => self.incoming_option(d)?, @@ -296,6 +306,13 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); } + Descriptor::ImportEnum { name, hole, .. } => { + self.instruction( + &[AdapterType::Enum(name.clone()).option()], + Instruction::I32FromOptionEnum { hole: *hole }, + &[AdapterType::I32], + ); + } Descriptor::RustStruct(name) => { self.instruction( &[AdapterType::Struct(name.clone()).option()], diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index 2c0fceefbcd..805833a0557 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -74,6 +74,11 @@ impl InstructionBuilder<'_, '_> { self.output.push(AdapterType::F64); } Descriptor::Enum { name, .. } => self.outgoing_i32(AdapterType::Enum(name.clone())), + Descriptor::ImportEnum { + name, + variant_values, + .. + } => self.outgoing_import_enum(&name, &variant_values), Descriptor::Char => { self.instruction( @@ -287,6 +292,13 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::Enum(name.clone()).option()], ); } + Descriptor::ImportEnum { + name, + hole, + variant_values, + } => { + todo!() + } Descriptor::RustStruct(name) => { self.instruction( &[AdapterType::I32], @@ -352,6 +364,7 @@ impl InstructionBuilder<'_, '_> { | Descriptor::Boolean | Descriptor::Char | Descriptor::Enum { .. } + | Descriptor::ImportEnum { .. } | Descriptor::RustStruct(_) | Descriptor::Ref(_) | Descriptor::RefMut(_) @@ -520,6 +533,20 @@ impl InstructionBuilder<'_, '_> { self.instruction(&[AdapterType::I32], instr, &[output]); } + fn outgoing_import_enum(&mut self, name: &str, variant_values: &[String]) { + let instr = Instruction::WasmToEnum { + variant_values: variant_values.to_vec(), + }; + self.instruction( + &[AdapterType::I32], + instr, + &[AdapterType::JsEnum { + name: String::from(name), + variant_values: variant_values.to_vec(), + }], + ); + } + fn outgoing_i64(&mut self, output: AdapterType) { let instr = Instruction::WasmToInt { input: walrus::ValType::I64, diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index 104a297abfa..ce6df2eede3 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -86,6 +86,10 @@ pub enum AdapterType { Option(Box), Struct(String), Enum(String), + JsEnum { + name: String, + variant_values: Vec, + }, NamedExternref(String), Function, NonNull, @@ -140,6 +144,11 @@ pub enum Instruction { output: AdapterType, }, + /// Pops a wasm `i32` and pushes the corresponding enum variant from the list. + WasmToEnum { + variant_values: Vec, + }, + /// Pops a `bool` from the stack and pushes an `i32` equivalent I32FromBool, /// Pops a `string` from the stack and pushes the first character as `i32` diff --git a/src/describe.rs b/src/describe.rs index 23190f71987..33f7332308d 100644 --- a/src/describe.rs +++ b/src/describe.rs @@ -42,6 +42,7 @@ tys! { EXTERNREF NAMED_EXTERNREF ENUM + IMPORT_ENUM RUST_STRUCT CHAR OPTIONAL From fdf7dcb8ff003c3ba9032fc2a8405827d711082c Mon Sep 17 00:00:00 2001 From: David Huculak Date: Fri, 5 Apr 2024 04:14:34 -0400 Subject: [PATCH 02/13] cleanup and add support for 'incoming' import enums --- crates/backend/src/codegen.rs | 23 ++----- crates/cli-support/src/js/binding.rs | 83 ++++++++++++++++++++++---- crates/cli-support/src/wit/incoming.rs | 22 ++++--- crates/cli-support/src/wit/outgoing.rs | 19 +++--- crates/cli-support/src/wit/standard.rs | 24 ++++++-- 5 files changed, 119 insertions(+), 52 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a8bc3f7bd04..a6dcccecbb6 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -2,7 +2,7 @@ use crate::ast; use crate::encode; use crate::Diagnostic; use once_cell::sync::Lazy; -use proc_macro2::{Ident, Literal, Span, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::format_ident; use quote::quote_spanned; use quote::{quote, ToTokens}; @@ -1028,27 +1028,13 @@ impl ToTokens for ast::ImportEnum { let name_str = enum_name.to_string(); let name_len = name_str.len() as u32; let name_chars = name_str.chars().map(|c| c as u32); - // let expect_string = format!("attempted to convert invalid {} into JSValue", name); let variants = &self.variants; + let variant_count = self.variant_values.len() as u32; let variant_values = &self.variant_values; - // let variant_strings = &self.variant_values; - let variant_indices = (0..variant_values.len() as u32).collect::>(); - let hole = self.variant_values.len() as u32; + let variant_indices = (0..variant_count).collect::>(); + let hole = variant_count; let attrs = &self.rust_attrs; - // let mut current_idx: usize = 0; - // let variant_indexes: Vec = variants - // .iter() - // .map(|_| { - // let this_index = current_idx; - // current_idx += 1; - // Literal::usize_unsuffixed(this_index) - // }) - // .collect(); - - // Borrow variant_indexes because we need to use it multiple times inside the quote! macro - // let variant_indexes_ref = &variant_indexes; - // A vector of EnumName::VariantName tokens for this enum let variant_paths: Vec = self .variants @@ -1069,7 +1055,6 @@ impl ToTokens for ast::ImportEnum { #(inform(#chars);)* } }); - let variant_count = self.variant_values.len() as u32; (quote! { #(#attrs)* diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index 9639b587592..ae6b5448eb8 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -668,21 +668,82 @@ fn instruction( } } - Instruction::WasmToEnum { variant_values } => { + Instruction::WasmToImportEnum { variant_values } => { let index = js.pop(); - // e.g. ["one","two","three"][someIndex] - let mut result = String::new(); - result.push('['); + // e.g. ["a","b","c"][someIndex] + let mut enum_val_expr = String::new(); + enum_val_expr.push('['); for variant in variant_values { - result.push_str(&format!("\"{variant}\",")); + enum_val_expr.push_str(&format!("\"{variant}\",")); } - result.push(']'); - result.push('['); - result.push_str(&index); - result.push(']'); + enum_val_expr.push(']'); + enum_val_expr.push('['); + enum_val_expr.push_str(&index); + enum_val_expr.push(']'); - js.push(result) + js.push(enum_val_expr) + } + + Instruction::OptionWasmToImportEnum { + variant_values, + hole, + } => { + let index = js.pop(); + + let mut enum_val_expr = String::new(); + enum_val_expr.push('['); + for variant in variant_values { + enum_val_expr.push_str(&format!("\"{variant}\",")); + } + enum_val_expr.push(']'); + enum_val_expr.push('['); + enum_val_expr.push_str(&index); + enum_val_expr.push(']'); + + // e.g. someIndex === hole ? undefined : (["a","b","c"][someIndex]) + js.push(format!( + "{index} === {hole} ? undefined : ({enum_val_expr})" + )) + } + + Instruction::ImportEnumToWasm { variant_values } => { + let enum_val = js.pop(); + + // e.g. {"a":0,"b":1,"c":2}[someEnumVal] + let mut enum_val_expr = String::new(); + enum_val_expr.push('{'); + for (i, variant) in variant_values.iter().enumerate() { + enum_val_expr.push_str(&format!("\"{variant}\":{i},")); + } + enum_val_expr.push('}'); + enum_val_expr.push('['); + enum_val_expr.push_str(&enum_val); + enum_val_expr.push(']'); + + js.push(enum_val_expr) + } + + Instruction::OptionImportEnumToWasm { + variant_values, + hole, + } => { + let enum_val = js.pop(); + + let mut enum_val_expr = String::new(); + enum_val_expr.push('{'); + for (i, variant) in variant_values.iter().enumerate() { + enum_val_expr.push_str(&format!("\"{variant}\":{i},")); + } + enum_val_expr.push('}'); + enum_val_expr.push('['); + enum_val_expr.push_str(&enum_val); + enum_val_expr.push(']'); + + // e.g. someEnumVal === undefined ? hole : ({"a":0,"b":1,"c":2}[someEnumVal]) + js.push(format!( + "{enum_val} === undefined ? {hole} : ({enum_val_expr})" + )) } Instruction::MemoryToString(mem) => { @@ -1394,7 +1455,7 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String) { AdapterType::NamedExternref(name) => dst.push_str(name), AdapterType::Struct(name) => dst.push_str(name), AdapterType::Enum(name) => dst.push_str(name), - AdapterType::JsEnum { name, .. } => dst.push_str(name), + AdapterType::ImportEnum(name) => dst.push_str(name), AdapterType::Function => dst.push_str("any"), } } diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index c741eb70f2b..d6e69aa1bec 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -108,12 +108,11 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); }, - Descriptor::ImportEnum { name, .. } => { + Descriptor::ImportEnum { name, variant_values, .. } => { self.instruction( - &[AdapterType::Enum(name.clone())], - Instruction::IntToWasm { - input: AdapterType::U32, - output: ValType::I32, + &[AdapterType::ImportEnum(name.clone())], + Instruction::ImportEnumToWasm { + variant_values: variant_values.clone(), }, &[AdapterType::I32], ); @@ -306,10 +305,17 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); } - Descriptor::ImportEnum { name, hole, .. } => { + Descriptor::ImportEnum { + name, + variant_values, + hole, + } => { self.instruction( - &[AdapterType::Enum(name.clone()).option()], - Instruction::I32FromOptionEnum { hole: *hole }, + &[AdapterType::ImportEnum(name.clone()).option()], + Instruction::OptionImportEnumToWasm { + variant_values: variant_values.clone(), + hole: *hole, + }, &[AdapterType::I32], ); } diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index 805833a0557..cf10e88e854 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -297,7 +297,14 @@ impl InstructionBuilder<'_, '_> { hole, variant_values, } => { - todo!() + self.instruction( + &[AdapterType::I32], + Instruction::OptionWasmToImportEnum { + variant_values: variant_values.to_vec(), + hole: *hole, + }, + &[AdapterType::ImportEnum(String::from(name))], + ); } Descriptor::RustStruct(name) => { self.instruction( @@ -534,16 +541,12 @@ impl InstructionBuilder<'_, '_> { } fn outgoing_import_enum(&mut self, name: &str, variant_values: &[String]) { - let instr = Instruction::WasmToEnum { - variant_values: variant_values.to_vec(), - }; self.instruction( &[AdapterType::I32], - instr, - &[AdapterType::JsEnum { - name: String::from(name), + Instruction::WasmToImportEnum { variant_values: variant_values.to_vec(), - }], + }, + &[AdapterType::ImportEnum(String::from(name))], ); } diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index ce6df2eede3..8cf02e407cb 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -86,10 +86,7 @@ pub enum AdapterType { Option(Box), Struct(String), Enum(String), - JsEnum { - name: String, - variant_values: Vec, - }, + ImportEnum(String), NamedExternref(String), Function, NonNull, @@ -144,11 +141,26 @@ pub enum Instruction { output: AdapterType, }, - /// Pops a wasm `i32` and pushes the corresponding enum variant from the list. - WasmToEnum { + /// Pops a wasm `i32` and pushes the corresponding enum variant string from the list + WasmToImportEnum { + variant_values: Vec, + }, + + OptionWasmToImportEnum { + variant_values: Vec, + hole: u32, + }, + + /// pops an enum variant string and pushes the corresponding wasm `i32` enum value + ImportEnumToWasm { variant_values: Vec, }, + OptionImportEnumToWasm { + variant_values: Vec, + hole: u32, + }, + /// Pops a `bool` from the stack and pushes an `i32` equivalent I32FromBool, /// Pops a `string` from the stack and pushes the first character as `i32` From cc65e04d8a31744c2b05837fb77f1d3b2e16669c Mon Sep 17 00:00:00 2001 From: David Huculak Date: Fri, 5 Apr 2024 04:59:31 -0400 Subject: [PATCH 03/13] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5cf57ea7e0..5a13aac1b3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ * Generate JS bindings for WebIDL dictionary setters instead of using `Reflect`. This increases the size of the Web API bindings but should be more performant. Also, importing getters/setters from JS now supports specifying the JS attribute name as a string, e.g. `#[wasm_bindgen(method, setter = "x-cdm-codecs")]`. [#3898](https://github.com/rustwasm/wasm-bindgen/pull/3898) +* Greatly improve the performance of sending WebIDL 'import enums' across the JavaScript boundary bindings by converting the enum variant string to/from an int. + [#3915](https://github.com/rustwasm/wasm-bindgen/pull/3915) + ### Fixed * Copy port from headless test server when using `WASM_BINDGEN_TEST_ADDRESS`. From 8614e64bfb5d01d44929e11d19db9d01b690ebc0 Mon Sep 17 00:00:00 2001 From: David Huculak Date: Fri, 5 Apr 2024 05:01:55 -0400 Subject: [PATCH 04/13] fix clippies --- crates/cli-support/src/wit/outgoing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index cf10e88e854..e80d3a7134c 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -78,7 +78,7 @@ impl InstructionBuilder<'_, '_> { name, variant_values, .. - } => self.outgoing_import_enum(&name, &variant_values), + } => self.outgoing_import_enum(name, variant_values), Descriptor::Char => { self.instruction( From 4a572c3018f7ba35a647381933b6126959796b9e Mon Sep 17 00:00:00 2001 From: David Huculak Date: Fri, 5 Apr 2024 06:15:14 -0400 Subject: [PATCH 05/13] fix failing test --- crates/cli-support/src/js/binding.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index ae6b5448eb8..886f884ca9a 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -740,9 +740,10 @@ fn instruction( enum_val_expr.push_str(&enum_val); enum_val_expr.push(']'); - // e.g. someEnumVal === undefined ? hole : ({"a":0,"b":1,"c":2}[someEnumVal]) + // e.g. someEnumVal == undefined ? hole : ({"a":0,"b":1,"c":2}[someEnumVal]) + // double equals is used instead of triple equals to account for null as well as undefined js.push(format!( - "{enum_val} === undefined ? {hole} : ({enum_val_expr})" + "{enum_val} == undefined ? {hole} : ({enum_val_expr})" )) } From 2b33d8f4172752ce55528ef189a5707c567f14b9 Mon Sep 17 00:00:00 2001 From: David Huculak Date: Fri, 5 Apr 2024 12:44:09 -0400 Subject: [PATCH 06/13] add '__Invalid' variant to account for bad values from JS --- crates/backend/src/codegen.rs | 16 ++++++++++------ crates/cli-support/src/descriptor.rs | 3 +++ crates/cli-support/src/js/binding.rs | 21 ++++++++++++++++----- crates/cli-support/src/wit/incoming.rs | 5 ++++- crates/cli-support/src/wit/outgoing.rs | 4 +++- crates/cli-support/src/wit/standard.rs | 6 ++++-- 6 files changed, 40 insertions(+), 15 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a6dcccecbb6..a10ec306ed6 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1032,7 +1032,8 @@ impl ToTokens for ast::ImportEnum { let variant_count = self.variant_values.len() as u32; let variant_values = &self.variant_values; let variant_indices = (0..variant_count).collect::>(); - let hole = variant_count; + let invalid = variant_count; + let hole = variant_count + 1; let attrs = &self.rust_attrs; // A vector of EnumName::VariantName tokens for this enum @@ -1062,6 +1063,9 @@ impl ToTokens for ast::ImportEnum { #[repr(u32)] #vis enum #enum_name { #(#variants = #variant_indices,)* + #[automatically_derived] + #[doc(hidden)] + __Invalid } #[automatically_derived] @@ -1079,10 +1083,9 @@ impl ToTokens for ast::ImportEnum { type Abi = u32; unsafe fn from_abi(val: u32) -> Self { - let s = ::from_abi(val); - match s { + match val { #(#variant_indices => #variant_paths_ref,)* - _ => #wasm_bindgen::throw_str("invalid enum value passed") + _ => Self::__Invalid } } } @@ -1106,6 +1109,7 @@ impl ToTokens for ast::ImportEnum { inform(IMPORT_ENUM); inform(#name_len); #(inform(#name_chars);)* + inform(#invalid); inform(#hole); inform(#variant_count); #(#describe_variants)* @@ -1116,9 +1120,9 @@ impl ToTokens for ast::ImportEnum { impl #wasm_bindgen::__rt::core::convert::From<#enum_name> for #wasm_bindgen::JsValue { - fn from(value: #enum_name) -> Self { + fn from(val: #enum_name) -> Self { #wasm_bindgen::JsValue::from_str( - match value { + match val { #(#variant_paths_ref => #variant_values,)* _ => #wasm_bindgen::throw_str("invalid enum value passed") } diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index e746a168c8d..7e918cfe032 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -74,6 +74,7 @@ pub enum Descriptor { }, ImportEnum { name: String, + invalid: u32, hole: u32, variant_values: Vec, }, @@ -167,11 +168,13 @@ impl Descriptor { } IMPORT_ENUM => { let name = get_string(data); + let invalid = get(data); let hole = get(data); let variant_count = get(data); let variant_values = (0..variant_count).map(|_| get_string(data)).collect(); Descriptor::ImportEnum { name, + invalid, hole, variant_values, } diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index 886f884ca9a..41c4ab3eb46 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -701,16 +701,23 @@ fn instruction( enum_val_expr.push_str(&index); enum_val_expr.push(']'); - // e.g. someIndex === hole ? undefined : (["a","b","c"][someIndex]) + // e.g. someIndex === 4 ? undefined : (["a","b","c"][someIndex]) + // | + // currently, hole = variant_count + 1 js.push(format!( "{index} === {hole} ? undefined : ({enum_val_expr})" )) } - Instruction::ImportEnumToWasm { variant_values } => { + Instruction::ImportEnumToWasm { + variant_values, + invalid, + } => { let enum_val = js.pop(); - // e.g. {"a":0,"b":1,"c":2}[someEnumVal] + // e.g. {"a":0,"b":1,"c":2}[someEnumVal] ?? 3 + // | + // currently, invalid = variant_count let mut enum_val_expr = String::new(); enum_val_expr.push('{'); for (i, variant) in variant_values.iter().enumerate() { @@ -720,12 +727,14 @@ fn instruction( enum_val_expr.push('['); enum_val_expr.push_str(&enum_val); enum_val_expr.push(']'); + enum_val_expr.push_str(&format!(" ?? {invalid}")); js.push(enum_val_expr) } Instruction::OptionImportEnumToWasm { variant_values, + invalid, hole, } => { let enum_val = js.pop(); @@ -739,9 +748,11 @@ fn instruction( enum_val_expr.push('['); enum_val_expr.push_str(&enum_val); enum_val_expr.push(']'); + enum_val_expr.push_str(&format!(" ?? {invalid}")); - // e.g. someEnumVal == undefined ? hole : ({"a":0,"b":1,"c":2}[someEnumVal]) - // double equals is used instead of triple equals to account for null as well as undefined + // e.g. someEnumVal == undefined ? 4 : ({"a":0,"b":1,"c":2}[someEnumVal] ?? 3) + // | + // double equals here in case it's null js.push(format!( "{enum_val} == undefined ? {hole} : ({enum_val_expr})" )) diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index d6e69aa1bec..6df91313fc9 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -108,11 +108,12 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); }, - Descriptor::ImportEnum { name, variant_values, .. } => { + Descriptor::ImportEnum { name, variant_values, invalid, .. } => { self.instruction( &[AdapterType::ImportEnum(name.clone())], Instruction::ImportEnumToWasm { variant_values: variant_values.clone(), + invalid: *invalid, }, &[AdapterType::I32], ); @@ -308,12 +309,14 @@ impl InstructionBuilder<'_, '_> { Descriptor::ImportEnum { name, variant_values, + invalid, hole, } => { self.instruction( &[AdapterType::ImportEnum(name.clone()).option()], Instruction::OptionImportEnumToWasm { variant_values: variant_values.clone(), + invalid: *invalid, hole: *hole, }, &[AdapterType::I32], diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index e80d3a7134c..119d7479e62 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -77,7 +77,8 @@ impl InstructionBuilder<'_, '_> { Descriptor::ImportEnum { name, variant_values, - .. + invalid: _, + hole: _, } => self.outgoing_import_enum(name, variant_values), Descriptor::Char => { @@ -294,6 +295,7 @@ impl InstructionBuilder<'_, '_> { } Descriptor::ImportEnum { name, + invalid: _, hole, variant_values, } => { diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index 8cf02e407cb..814fe487558 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -141,7 +141,7 @@ pub enum Instruction { output: AdapterType, }, - /// Pops a wasm `i32` and pushes the corresponding enum variant string from the list + /// Pops a wasm `i32` and pushes the enum variant as a string WasmToImportEnum { variant_values: Vec, }, @@ -151,13 +151,15 @@ pub enum Instruction { hole: u32, }, - /// pops an enum variant string and pushes the corresponding wasm `i32` enum value + /// pops a string and pushes the enum variant as an `i32` ImportEnumToWasm { variant_values: Vec, + invalid: u32, }, OptionImportEnumToWasm { variant_values: Vec, + invalid: u32, hole: u32, }, From 3ff2d29bbdc90fed837e8a4dc8471999819eb256 Mon Sep 17 00:00:00 2001 From: David Huculak Date: Fri, 5 Apr 2024 13:29:50 -0400 Subject: [PATCH 07/13] use unreachable! macro for states that should not be possible --- crates/backend/src/codegen.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a10ec306ed6..008b48cf35c 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1085,7 +1085,8 @@ impl ToTokens for ast::ImportEnum { unsafe fn from_abi(val: u32) -> Self { match val { #(#variant_indices => #variant_paths_ref,)* - _ => Self::__Invalid + #invalid => #enum_name::__Invalid, + _ => unreachable!("The JS binding should only ever produce a valid value or the specific 'invalid' value") } } } @@ -1124,7 +1125,8 @@ impl ToTokens for ast::ImportEnum { #wasm_bindgen::JsValue::from_str( match val { #(#variant_paths_ref => #variant_values,)* - _ => #wasm_bindgen::throw_str("invalid enum value passed") + #enum_name::__Invalid => #wasm_bindgen::throw_str("Converting an invalid import enum back to a string is currently not supported"), + _ => unreachable!("All possible variants should have been checked") } ) } From 3adafd64164f3d809fd753b56f55b0aa75949214 Mon Sep 17 00:00:00 2001 From: David Huculak Date: Sat, 20 Apr 2024 20:31:00 -0400 Subject: [PATCH 08/13] increase memory of wasm interpreter to avoid running out of memory for the large describe functions generated by import enums --- CHANGELOG.md | 2 +- crates/wasm-interpreter/src/lib.rs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a13aac1b3a..49951759b0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ * Generate JS bindings for WebIDL dictionary setters instead of using `Reflect`. This increases the size of the Web API bindings but should be more performant. Also, importing getters/setters from JS now supports specifying the JS attribute name as a string, e.g. `#[wasm_bindgen(method, setter = "x-cdm-codecs")]`. [#3898](https://github.com/rustwasm/wasm-bindgen/pull/3898) -* Greatly improve the performance of sending WebIDL 'import enums' across the JavaScript boundary bindings by converting the enum variant string to/from an int. +* Greatly improve the performance of sending WebIDL 'import enums' across the JavaScript boundary by converting the enum variant string to/from an int. [#3915](https://github.com/rustwasm/wasm-bindgen/pull/3915) ### Fixed diff --git a/crates/wasm-interpreter/src/lib.rs b/crates/wasm-interpreter/src/lib.rs index b5fda3b8172..359eb197a94 100644 --- a/crates/wasm-interpreter/src/lib.rs +++ b/crates/wasm-interpreter/src/lib.rs @@ -68,11 +68,9 @@ impl Interpreter { pub fn new(module: &Module) -> Result { let mut ret = Interpreter::default(); - // The descriptor functions shouldn't really use all that much memory - // (the LLVM call stack, now the wasm stack). To handle that let's give - // our selves a little bit of memory and set the stack pointer (global - // 0) to the top. - ret.mem = vec![0; 0x400]; + // Give ourselves some memory and set the stack pointer + // (the LLVM call stack, now the wasm stack, global 0) to the top. + ret.mem = vec![0; 0x8000]; ret.sp = ret.mem.len() as i32; // Figure out where the `__wbindgen_describe` imported function is, if @@ -299,6 +297,10 @@ impl Frame<'_> { // theory there doesn't need to be. Instr::Load(e) => { let address = stack.pop().unwrap(); + assert!( + address > 0, + "Read a negative address value from the stack. Did we run out of memory?" + ); let address = address as u32 + e.arg.offset; assert!(address % 4 == 0); stack.push(self.interp.mem[address as usize / 4]) @@ -306,6 +308,10 @@ impl Frame<'_> { Instr::Store(e) => { let value = stack.pop().unwrap(); let address = stack.pop().unwrap(); + assert!( + address > 0, + "Read a negative address value from the stack. Did we run out of memory?" + ); let address = address as u32 + e.arg.offset; assert!(address % 4 == 0); self.interp.mem[address as usize / 4] = value; From 1182a623f36d040bb37ccea3e4206d251482633d Mon Sep 17 00:00:00 2001 From: David Huculak Date: Sat, 20 Apr 2024 20:47:00 -0400 Subject: [PATCH 09/13] fix smoke test --- crates/wasm-interpreter/tests/smoke.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/wasm-interpreter/tests/smoke.rs b/crates/wasm-interpreter/tests/smoke.rs index c14545c2ffa..6848d003932 100644 --- a/crates/wasm-interpreter/tests/smoke.rs +++ b/crates/wasm-interpreter/tests/smoke.rs @@ -91,7 +91,7 @@ fn globals() { ) "#; // __wbindgen_describe is called with a global - in Frame.eval we assume all access to globals is the stack pointer - interpret(wat, "foo", Some(&[1024])); + interpret(wat, "foo", Some(&[32768])); } #[test] From 5eec30ffb65f3e6a461ce0b37c26cea63a5bcb16 Mon Sep 17 00:00:00 2001 From: David Huculak Date: Sun, 19 May 2024 22:27:12 -0400 Subject: [PATCH 10/13] - replace 'as' with u32::from x 2 - re-add deleted from_js_value function - remove hole from descriptor - bump schema version --- crates/backend/src/codegen.rs | 19 ++++++++++++++++--- crates/cli-support/src/descriptor.rs | 2 +- crates/shared/src/lib.rs | 2 +- crates/shared/src/schema_hash_approval.rs | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 008b48cf35c..03a8d275eb6 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1027,7 +1027,7 @@ impl ToTokens for ast::ImportEnum { let enum_name = &self.name; let name_str = enum_name.to_string(); let name_len = name_str.len() as u32; - let name_chars = name_str.chars().map(|c| c as u32); + let name_chars = name_str.chars().map(u32::from); let variants = &self.variants; let variant_count = self.variant_values.len() as u32; let variant_values = &self.variant_values; @@ -1050,7 +1050,7 @@ impl ToTokens for ast::ImportEnum { let describe_variants = self.variant_values.iter().map(|variant_value| { let length = variant_value.len() as u32; - let chars = variant_value.chars().map(|c| c as u32); + let chars = variant_value.chars().map(u32::from); quote! { inform(#length); #(inform(#chars);)* @@ -1068,6 +1068,20 @@ impl ToTokens for ast::ImportEnum { __Invalid } + #[automatically_derived] + impl #enum_name { + fn from_str(s: &str) -> Option<#enum_name> { + match s { + #(#variant_values => Some(#variant_paths_ref),)* + _ => None, + } + } + + #vis fn from_js_value(obj: &#wasm_bindgen::JsValue) -> Option<#enum_name> { + obj.as_string().and_then(|obj_str| Self::from_str(obj_str.as_str())) + } + } + #[automatically_derived] impl #wasm_bindgen::convert::IntoWasmAbi for #enum_name { type Abi = u32; @@ -1111,7 +1125,6 @@ impl ToTokens for ast::ImportEnum { inform(#name_len); #(inform(#name_chars);)* inform(#invalid); - inform(#hole); inform(#variant_count); #(#describe_variants)* } diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index 7e918cfe032..82421b229b1 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -169,8 +169,8 @@ impl Descriptor { IMPORT_ENUM => { let name = get_string(data); let invalid = get(data); - let hole = get(data); let variant_count = get(data); + let hole = variant_count + 1; let variant_values = (0..variant_count).map(|_| get_string(data)).collect(); Descriptor::ImportEnum { name, diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index f8ad45c7cd3..2e49a919eeb 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -6,7 +6,7 @@ mod schema_hash_approval; // This gets changed whenever our schema changes. // At this time versions of wasm-bindgen and wasm-bindgen-cli are required to have the exact same // SCHEMA_VERSION in order to work together. -pub const SCHEMA_VERSION: &str = "0.2.92"; +pub const SCHEMA_VERSION: &str = "0.2.93"; #[macro_export] macro_rules! shared_api { diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs index 471ccc9beac..d2ffe4c2c0c 100644 --- a/crates/shared/src/schema_hash_approval.rs +++ b/crates/shared/src/schema_hash_approval.rs @@ -8,7 +8,7 @@ // If the schema in this library has changed then: // 1. Bump the version in `crates/shared/Cargo.toml` // 2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version -const APPROVED_SCHEMA_FILE_HASH: &str = "11955579329744078753"; +const APPROVED_SCHEMA_FILE_HASH: &str = "6582330021812361761"; #[test] fn schema_version() { From 9e3f4c88510d74cc533957cbf11ad37c2e73336c Mon Sep 17 00:00:00 2001 From: David Huculak Date: Sun, 19 May 2024 22:41:27 -0400 Subject: [PATCH 11/13] fix incorrect schema hash after merging from master --- crates/shared/src/schema_hash_approval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs index d2ffe4c2c0c..a6146554283 100644 --- a/crates/shared/src/schema_hash_approval.rs +++ b/crates/shared/src/schema_hash_approval.rs @@ -8,7 +8,7 @@ // If the schema in this library has changed then: // 1. Bump the version in `crates/shared/Cargo.toml` // 2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version -const APPROVED_SCHEMA_FILE_HASH: &str = "6582330021812361761"; +const APPROVED_SCHEMA_FILE_HASH: &str = "16355783286637101026"; #[test] fn schema_version() { From 130ba7c692abe1b40ea04d8737ce49b6af82a385 Mon Sep 17 00:00:00 2001 From: David Huculak Date: Mon, 20 May 2024 14:24:32 -0400 Subject: [PATCH 12/13] - remove unneeded underscore match branch - re-add removed to_str function - rename 'import enum' to 'string enum' - remove 'invalid' number from describe function --- CHANGELOG.md | 2 +- benchmarks/index.html | 4 ++-- crates/backend/src/ast.rs | 8 ++++---- crates/backend/src/codegen.rs | 27 +++++++++++++++----------- crates/backend/src/encode.rs | 4 ++-- crates/cli-support/src/descriptor.rs | 10 +++++----- crates/cli-support/src/js/binding.rs | 10 +++++----- crates/cli-support/src/wit/incoming.rs | 12 ++++++------ crates/cli-support/src/wit/outgoing.rs | 18 ++++++++--------- crates/cli-support/src/wit/standard.rs | 10 +++++----- crates/macro-support/src/parser.rs | 6 +++--- crates/shared/src/lib.rs | 4 ++-- src/describe.rs | 2 +- 13 files changed, 61 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dff691951a..47e952f69d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,7 @@ * Generate JS bindings for WebIDL dictionary setters instead of using `Reflect`. This increases the size of the Web API bindings but should be more performant. Also, importing getters/setters from JS now supports specifying the JS attribute name as a string, e.g. `#[wasm_bindgen(method, setter = "x-cdm-codecs")]`. [#3898](https://github.com/rustwasm/wasm-bindgen/pull/3898) -* Greatly improve the performance of sending WebIDL 'import enums' across the JavaScript boundary by converting the enum variant string to/from an int. +* Greatly improve the performance of sending WebIDL 'string enums' across the JavaScript boundary by converting the enum variant string to/from an int. [#3915](https://github.com/rustwasm/wasm-bindgen/pull/3915) ### Fixed diff --git a/benchmarks/index.html b/benchmarks/index.html index 020839f85e8..49cf021333a 100644 --- a/benchmarks/index.html +++ b/benchmarks/index.html @@ -277,12 +277,12 @@

wasm-bindgen benchmarks

- Call a custom JS function with an enum value parameter + Call a custom JS function with a string enum value parameter (?)

- Benchmarks the speed of passing enum values to javascript + Benchmarks the speed of passing string enums to javascript

diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 5c6555315c4..123c4ea9036 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -163,7 +163,7 @@ pub enum ImportKind { /// Importing a type/class Type(ImportType), /// Importing a JS enum - Enum(ImportEnum), + Enum(StringEnum), } /// A function being imported from JS @@ -302,10 +302,10 @@ pub struct ImportType { pub wasm_bindgen: Path, } -/// The metadata for an Enum being imported +/// The metadata for a String Enum #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] -pub struct ImportEnum { +pub struct StringEnum { /// The Rust enum's visibility pub vis: syn::Visibility, /// The Rust enum's identifiers @@ -404,7 +404,7 @@ pub struct StructField { pub wasm_bindgen: Path, } -/// Information about an Enum being exported +/// The metadata for an Enum #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct Enum { diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a913c08685b..330ffba6626 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1075,7 +1075,7 @@ impl ToTokens for ast::ImportType { } } -impl ToTokens for ast::ImportEnum { +impl ToTokens for ast::StringEnum { fn to_tokens(&self, tokens: &mut TokenStream) { let vis = &self.vis; let enum_name = &self.name; @@ -1090,6 +1090,11 @@ impl ToTokens for ast::ImportEnum { let hole = variant_count + 1; let attrs = &self.rust_attrs; + let invalid_to_str_msg = format!( + "Converting an invalid string enum ({}) back to a string is currently not supported", + enum_name + ); + // A vector of EnumName::VariantName tokens for this enum let variant_paths: Vec = self .variants @@ -1131,6 +1136,13 @@ impl ToTokens for ast::ImportEnum { } } + fn to_str(&self) -> &'static str { + match self { + #(#variant_paths_ref => #variant_values,)* + #enum_name::__Invalid => panic!(#invalid_to_str_msg), + } + } + #vis fn from_js_value(obj: &#wasm_bindgen::JsValue) -> Option<#enum_name> { obj.as_string().and_then(|obj_str| Self::from_str(obj_str.as_str())) } @@ -1154,7 +1166,7 @@ impl ToTokens for ast::ImportEnum { match val { #(#variant_indices => #variant_paths_ref,)* #invalid => #enum_name::__Invalid, - _ => unreachable!("The JS binding should only ever produce a valid value or the specific 'invalid' value") + _ => unreachable!("The JS binding should only ever produce a valid value or the specific 'invalid' value"), } } } @@ -1175,10 +1187,9 @@ impl ToTokens for ast::ImportEnum { impl #wasm_bindgen::describe::WasmDescribe for #enum_name { fn describe() { use #wasm_bindgen::describe::*; - inform(IMPORT_ENUM); + inform(STRING_ENUM); inform(#name_len); #(inform(#name_chars);)* - inform(#invalid); inform(#variant_count); #(#describe_variants)* } @@ -1189,13 +1200,7 @@ impl ToTokens for ast::ImportEnum { #wasm_bindgen::JsValue { fn from(val: #enum_name) -> Self { - #wasm_bindgen::JsValue::from_str( - match val { - #(#variant_paths_ref => #variant_values,)* - #enum_name::__Invalid => #wasm_bindgen::throw_str("Converting an invalid import enum back to a string is currently not supported"), - _ => unreachable!("All possible variants should have been checked") - } - ) + #wasm_bindgen::JsValue::from_str(val.to_str()) } } }) diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index 9c06aedb168..529edb0dc88 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -339,8 +339,8 @@ fn shared_import_type<'a>(i: &'a ast::ImportType, intern: &'a Interner) -> Impor } } -fn shared_import_enum<'a>(_i: &'a ast::ImportEnum, _intern: &'a Interner) -> ImportEnum { - ImportEnum {} +fn shared_import_enum<'a>(_i: &'a ast::StringEnum, _intern: &'a Interner) -> StringEnum { + StringEnum {} } fn shared_struct<'a>(s: &'a ast::Struct, intern: &'a Interner) -> Struct<'a> { diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index 82421b229b1..6a35786eba5 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -34,7 +34,7 @@ tys! { EXTERNREF NAMED_EXTERNREF ENUM - IMPORT_ENUM + STRING_ENUM RUST_STRUCT CHAR OPTIONAL @@ -72,7 +72,7 @@ pub enum Descriptor { name: String, hole: u32, }, - ImportEnum { + StringEnum { name: String, invalid: u32, hole: u32, @@ -166,13 +166,13 @@ impl Descriptor { let hole = get(data); Descriptor::Enum { name, hole } } - IMPORT_ENUM => { + STRING_ENUM => { let name = get_string(data); - let invalid = get(data); let variant_count = get(data); + let invalid = variant_count; let hole = variant_count + 1; let variant_values = (0..variant_count).map(|_| get_string(data)).collect(); - Descriptor::ImportEnum { + Descriptor::StringEnum { name, invalid, hole, diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index 41c4ab3eb46..31863da50ab 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -668,7 +668,7 @@ fn instruction( } } - Instruction::WasmToImportEnum { variant_values } => { + Instruction::WasmToStringEnum { variant_values } => { let index = js.pop(); // e.g. ["a","b","c"][someIndex] @@ -685,7 +685,7 @@ fn instruction( js.push(enum_val_expr) } - Instruction::OptionWasmToImportEnum { + Instruction::OptionWasmToStringEnum { variant_values, hole, } => { @@ -709,7 +709,7 @@ fn instruction( )) } - Instruction::ImportEnumToWasm { + Instruction::StringEnumToWasm { variant_values, invalid, } => { @@ -732,7 +732,7 @@ fn instruction( js.push(enum_val_expr) } - Instruction::OptionImportEnumToWasm { + Instruction::OptionStringEnumToWasm { variant_values, invalid, hole, @@ -1467,7 +1467,7 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String) { AdapterType::NamedExternref(name) => dst.push_str(name), AdapterType::Struct(name) => dst.push_str(name), AdapterType::Enum(name) => dst.push_str(name), - AdapterType::ImportEnum(name) => dst.push_str(name), + AdapterType::StringEnum(name) => dst.push_str(name), AdapterType::Function => dst.push_str("any"), } } diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index 6df91313fc9..583c1c34378 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -108,10 +108,10 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); }, - Descriptor::ImportEnum { name, variant_values, invalid, .. } => { + Descriptor::StringEnum { name, variant_values, invalid, .. } => { self.instruction( - &[AdapterType::ImportEnum(name.clone())], - Instruction::ImportEnumToWasm { + &[AdapterType::StringEnum(name.clone())], + Instruction::StringEnumToWasm { variant_values: variant_values.clone(), invalid: *invalid, }, @@ -306,15 +306,15 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); } - Descriptor::ImportEnum { + Descriptor::StringEnum { name, variant_values, invalid, hole, } => { self.instruction( - &[AdapterType::ImportEnum(name.clone()).option()], - Instruction::OptionImportEnumToWasm { + &[AdapterType::StringEnum(name.clone()).option()], + Instruction::OptionStringEnumToWasm { variant_values: variant_values.clone(), invalid: *invalid, hole: *hole, diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index 119d7479e62..033217f61ad 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -74,12 +74,12 @@ impl InstructionBuilder<'_, '_> { self.output.push(AdapterType::F64); } Descriptor::Enum { name, .. } => self.outgoing_i32(AdapterType::Enum(name.clone())), - Descriptor::ImportEnum { + Descriptor::StringEnum { name, variant_values, invalid: _, hole: _, - } => self.outgoing_import_enum(name, variant_values), + } => self.outgoing_string_enum(name, variant_values), Descriptor::Char => { self.instruction( @@ -293,7 +293,7 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::Enum(name.clone()).option()], ); } - Descriptor::ImportEnum { + Descriptor::StringEnum { name, invalid: _, hole, @@ -301,11 +301,11 @@ impl InstructionBuilder<'_, '_> { } => { self.instruction( &[AdapterType::I32], - Instruction::OptionWasmToImportEnum { + Instruction::OptionWasmToStringEnum { variant_values: variant_values.to_vec(), hole: *hole, }, - &[AdapterType::ImportEnum(String::from(name))], + &[AdapterType::StringEnum(String::from(name))], ); } Descriptor::RustStruct(name) => { @@ -373,7 +373,7 @@ impl InstructionBuilder<'_, '_> { | Descriptor::Boolean | Descriptor::Char | Descriptor::Enum { .. } - | Descriptor::ImportEnum { .. } + | Descriptor::StringEnum { .. } | Descriptor::RustStruct(_) | Descriptor::Ref(_) | Descriptor::RefMut(_) @@ -542,13 +542,13 @@ impl InstructionBuilder<'_, '_> { self.instruction(&[AdapterType::I32], instr, &[output]); } - fn outgoing_import_enum(&mut self, name: &str, variant_values: &[String]) { + fn outgoing_string_enum(&mut self, name: &str, variant_values: &[String]) { self.instruction( &[AdapterType::I32], - Instruction::WasmToImportEnum { + Instruction::WasmToStringEnum { variant_values: variant_values.to_vec(), }, - &[AdapterType::ImportEnum(String::from(name))], + &[AdapterType::StringEnum(String::from(name))], ); } diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index 814fe487558..e4d7aa745c1 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -86,7 +86,7 @@ pub enum AdapterType { Option(Box), Struct(String), Enum(String), - ImportEnum(String), + StringEnum(String), NamedExternref(String), Function, NonNull, @@ -142,22 +142,22 @@ pub enum Instruction { }, /// Pops a wasm `i32` and pushes the enum variant as a string - WasmToImportEnum { + WasmToStringEnum { variant_values: Vec, }, - OptionWasmToImportEnum { + OptionWasmToStringEnum { variant_values: Vec, hole: u32, }, /// pops a string and pushes the enum variant as an `i32` - ImportEnumToWasm { + StringEnumToWasm { variant_values: Vec, invalid: u32, }, - OptionImportEnumToWasm { + OptionStringEnumToWasm { variant_values: Vec, invalid: u32, hole: u32, diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index af9b38cf1a7..9ad14c6fce2 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1229,7 +1229,7 @@ impl<'a> MacroParse<&ClassMarker> for &'a mut syn::ImplItemFn { } } -fn import_enum(enum_: syn::ItemEnum, program: &mut ast::Program) -> Result<(), Diagnostic> { +fn string_enum(enum_: syn::ItemEnum, program: &mut ast::Program) -> Result<(), Diagnostic> { let mut variants = vec![]; let mut variant_values = vec![]; @@ -1263,7 +1263,7 @@ fn import_enum(enum_: syn::ItemEnum, program: &mut ast::Program) -> Result<(), D program.imports.push(ast::Import { module: None, js_namespace: None, - kind: ast::ImportKind::Enum(ast::ImportEnum { + kind: ast::ImportKind::Enum(ast::StringEnum { vis: enum_.vis, name: enum_.ident, variants, @@ -1295,7 +1295,7 @@ impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum { }) = get_expr(expr) { opts.check_used(); - return import_enum(self, program); + return string_enum(self, program); } } let js_name = opts diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index f8fcaef7e27..0c3af022dbf 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -50,7 +50,7 @@ macro_rules! shared_api { Function(ImportFunction<'a>), Static(ImportStatic<'a>), Type(ImportType<'a>), - Enum(ImportEnum), + Enum(StringEnum), } struct ImportFunction<'a> { @@ -98,7 +98,7 @@ macro_rules! shared_api { vendor_prefixes: Vec<&'a str>, } - struct ImportEnum {} + struct StringEnum {} struct Export<'a> { class: Option<&'a str>, diff --git a/src/describe.rs b/src/describe.rs index 33f7332308d..140018a8097 100644 --- a/src/describe.rs +++ b/src/describe.rs @@ -42,7 +42,7 @@ tys! { EXTERNREF NAMED_EXTERNREF ENUM - IMPORT_ENUM + STRING_ENUM RUST_STRUCT CHAR OPTIONAL From 9efa4f264b9cde6cb6f604c69eba57dde8f9facc Mon Sep 17 00:00:00 2001 From: David Huculak Date: Mon, 20 May 2024 14:32:00 -0400 Subject: [PATCH 13/13] fix schema hash --- crates/shared/src/schema_hash_approval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs index a6146554283..0ba79b8eefb 100644 --- a/crates/shared/src/schema_hash_approval.rs +++ b/crates/shared/src/schema_hash_approval.rs @@ -8,7 +8,7 @@ // If the schema in this library has changed then: // 1. Bump the version in `crates/shared/Cargo.toml` // 2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version -const APPROVED_SCHEMA_FILE_HASH: &str = "16355783286637101026"; +const APPROVED_SCHEMA_FILE_HASH: &str = "6362870592255096957"; #[test] fn schema_version() {