From e14a7adfe510613b6a1ee7568c817f8f85891934 Mon Sep 17 00:00:00 2001 From: Christopher Schramm Date: Thu, 21 Sep 2023 00:59:39 +0200 Subject: [PATCH] Allow creating `Vec`s of exported enums (#3624) --- crates/backend/src/codegen.rs | 60 +++++++++++++++++++++++++++++++++++ tests/wasm/enum_vecs.js | 23 ++++++++++++++ tests/wasm/enum_vecs.rs | 36 +++++++++++++++++++++ tests/wasm/main.rs | 1 + 4 files changed, 120 insertions(+) create mode 100644 tests/wasm/enum_vecs.js create mode 100644 tests/wasm/enum_vecs.rs diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a65418fdf86..86a584becc8 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1391,6 +1391,7 @@ impl ToTokens for ast::Enum { } } }); + let try_from_cast_clauses = cast_clauses.clone(); let wasm_bindgen = &self.wasm_bindgen; (quote! { #[automatically_derived] @@ -1435,6 +1436,65 @@ impl ToTokens for ast::Enum { inform(#hole); } } + + #[automatically_derived] + impl #wasm_bindgen::__rt::core::convert::From<#enum_name> for + #wasm_bindgen::JsValue + { + fn from(value: #enum_name) -> Self { + #wasm_bindgen::JsValue::from_f64((value as u32).into()) + } + } + + #[allow(clippy::all)] + impl #wasm_bindgen::__rt::core::convert::TryFrom<#wasm_bindgen::JsValue> for #enum_name { + type Error = #wasm_bindgen::JsValue; + + fn try_from(value: #wasm_bindgen::JsValue) + -> #wasm_bindgen::__rt::std::result::Result { + let js = f64::try_from(&value)? as u32; + + #wasm_bindgen::__rt::std::result::Result::Ok( + #(#try_from_cast_clauses else)* { + return #wasm_bindgen::__rt::std::result::Result::Err(value) + } + ) + } + } + + impl #wasm_bindgen::describe::WasmDescribeVector for #enum_name { + fn describe_vector() { + use #wasm_bindgen::describe::*; + inform(VECTOR); + <#wasm_bindgen::JsValue as #wasm_bindgen::describe::WasmDescribe>::describe(); + } + } + + impl #wasm_bindgen::convert::VectorIntoWasmAbi for #enum_name { + type Abi = < + #wasm_bindgen::__rt::std::boxed::Box<[#wasm_bindgen::JsValue]> + as #wasm_bindgen::convert::IntoWasmAbi + >::Abi; + + fn vector_into_abi( + vector: #wasm_bindgen::__rt::std::boxed::Box<[#enum_name]> + ) -> Self::Abi { + #wasm_bindgen::convert::js_value_vector_into_abi(vector) + } + } + + impl #wasm_bindgen::convert::VectorFromWasmAbi for #enum_name { + type Abi = < + #wasm_bindgen::__rt::std::boxed::Box<[#wasm_bindgen::JsValue]> + as #wasm_bindgen::convert::FromWasmAbi + >::Abi; + + unsafe fn vector_from_abi( + js: Self::Abi + ) -> #wasm_bindgen::__rt::std::boxed::Box<[#enum_name]> { + #wasm_bindgen::convert::js_value_vector_from_abi(js) + } + } }) .to_tokens(into); } diff --git a/tests/wasm/enum_vecs.js b/tests/wasm/enum_vecs.js new file mode 100644 index 00000000000..bef43b3e417 --- /dev/null +++ b/tests/wasm/enum_vecs.js @@ -0,0 +1,23 @@ +const wasm = require('wasm-bindgen-test.js'); +const assert = require('assert'); + +exports.pass_enum_vec = () => { + const el1 = wasm.EnumArrayElement.Unit; + const el2 = wasm.EnumArrayElement.Unit; + const ret = wasm.consume_enum_vec([el1, el2]); + assert.strictEqual(ret.length, 3); + + const ret2 = wasm.consume_optional_enum_vec(ret); + assert.strictEqual(ret2.length, 4); + + assert.strictEqual(wasm.consume_optional_enum_vec(undefined), undefined); +}; + +exports.pass_invalid_enum_vec = () => { + try { + wasm.consume_enum_vec(['not an enum value']); + } catch (e) { + assert.match(e.message, /invalid enum value passed/) + assert.match(e.stack, /consume_enum_vec/) + } +}; diff --git a/tests/wasm/enum_vecs.rs b/tests/wasm/enum_vecs.rs new file mode 100644 index 00000000000..7b6e6bcaf46 --- /dev/null +++ b/tests/wasm/enum_vecs.rs @@ -0,0 +1,36 @@ +use wasm_bindgen::prelude::*; +use wasm_bindgen_test::*; + +#[wasm_bindgen(module = "tests/wasm/enum_vecs.js")] +extern "C" { + fn pass_enum_vec(); + fn pass_invalid_enum_vec(); +} + +#[wasm_bindgen] +pub enum EnumArrayElement { + Unit, +} + +#[wasm_bindgen] +pub fn consume_enum_vec(mut vec: Vec) -> Vec { + vec.push(EnumArrayElement::Unit); + vec +} + +#[wasm_bindgen] +pub fn consume_optional_enum_vec( + vec: Option>, +) -> Option> { + vec.map(consume_enum_vec) +} + +#[wasm_bindgen_test] +fn test_valid() { + pass_enum_vec(); +} + +#[wasm_bindgen_test] +fn test_invalid() { + pass_invalid_enum_vec(); +} diff --git a/tests/wasm/main.rs b/tests/wasm/main.rs index 30ea94b422d..72bb5c414f4 100644 --- a/tests/wasm/main.rs +++ b/tests/wasm/main.rs @@ -23,6 +23,7 @@ pub mod closures; pub mod comments; pub mod duplicate_deps; pub mod duplicates; +pub mod enum_vecs; pub mod enums; #[path = "final.rs"] pub mod final_;