From 6f3a9bbd8de27f03e59cad83148d11318dbdae12 Mon Sep 17 00:00:00 2001 From: Jason Siefken Date: Mon, 29 Apr 2024 21:35:23 -0400 Subject: [PATCH 1/4] Forward error messages when converting to js types --- tests/wasm.rs | 35 ++++++++++++++++++++++++++- tsify-next-macros/src/wasm_bindgen.rs | 30 +++++++++++++++++++++-- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/tests/wasm.rs b/tests/wasm.rs index f00a0e2..87d74d7 100644 --- a/tests/wasm.rs +++ b/tests/wasm.rs @@ -1,3 +1,5 @@ +use core::panic; + use serde::{Deserialize, Serialize}; use tsify_next::Tsify; use wasm_bindgen::prelude::*; @@ -35,7 +37,9 @@ function validate(value, validation) { validation(value); } -module.exports = { validate }; +function noop(value) {} + +module.exports = { validate, noop }; "#)] extern "C" { #[wasm_bindgen(catch, js_name = "validate")] @@ -49,6 +53,9 @@ extern "C" { value: SimpleData, validation: &dyn Fn(&SimpleData), ) -> Result<(), JsValue>; + + #[wasm_bindgen(catch, js_name = "noop")] + pub fn do_not_serialize(value: CantBeSerialized) -> Result<(), JsValue>; } #[wasm_bindgen_test] @@ -68,3 +75,29 @@ fn test_convert_simple_value_type() { }) .unwrap_throw(); } + +// Test that the error message encountered during serialization is propagated to the caller +#[derive(Debug, PartialEq, Deserialize, Tsify, Clone)] +#[tsify(into_wasm_abi)] +struct CantBeSerialized { + value: i32, +} + +impl Serialize for CantBeSerialized { + fn serialize(&self, _serializer: S) -> Result + where + S: serde::Serializer, + { + Err(serde::ser::Error::custom( + "This type can't be serialized NO_SERIALIZE", + )) + } +} + +#[wasm_bindgen_test] +#[should_panic(expected = "NO_SERIALIZE")] +fn test_data_that_cant_be_serialized_throws_an_appropriate_error() { + let val = CantBeSerialized { value: 42 }; + + let _ = do_not_serialize(val); +} diff --git a/tsify-next-macros/src/wasm_bindgen.rs b/tsify-next-macros/src/wasm_bindgen.rs index 45dd823..e0dc094 100644 --- a/tsify-next-macros/src/wasm_bindgen.rs +++ b/tsify-next-macros/src/wasm_bindgen.rs @@ -101,7 +101,20 @@ fn expand_into_wasm_abi(cont: &Container) -> TokenStream { #[inline] fn into_abi(self) -> Self::Abi { - self.into_js().unwrap_throw().into_abi() + // wasm_bindgen doesn't forward the error message from the `into_js` result. + // https://github.com/rustwasm/wasm-bindgen/issues/2732 + // Until that issue is fixed, we don't directly use `unwrap_throw()` and instead build our + // own error message. + // Convert to `self.into_js().unwrap_throw().into_abi()` when fixed. + match self.into_js() { + Ok(js) => js.into_abi(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = format!("(Converting type failed) {} ({}:{}:{})", err, loc.file(), loc.line(), loc.column()); + // In theory, `wasm_bindgen::throw_str(&msg)` should work, but the error emitted by `wasm_bindgen::throw_str` cannot be picked up by `#[should_panic(expect = ...)]` in tests, so we use a regular panic. + panic!("{}", msg); + } + } } } @@ -115,7 +128,20 @@ fn expand_into_wasm_abi(cont: &Container) -> TokenStream { impl #impl_generics From<#ident #ty_generics> for JsValue #where_clause { #[inline] fn from(value: #ident #ty_generics) -> Self { - value.into_js().unwrap_throw().into() + // wasm_bindgen doesn't forward the error message from the `into_js` result. + // https://github.com/rustwasm/wasm-bindgen/issues/2732 + // Until that issue is fixed, we don't directly use `unwrap_throw()` and instead build our + // own error message. + // Convert to `value.into_js().unwrap_throw().into()` when fixed. + match value.into_js() { + Ok(js) => js.into(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = format!("(Converting type failed) {} ({}:{}:{})", err, loc.file(), loc.line(), loc.column()); + // In theory, `wasm_bindgen::throw_str(&msg)` should work, but the error emitted by `wasm_bindgen::throw_str` cannot be picked up by `#[should_panic(expect = ...)]` in tests, so we use a regular panic. + panic!("{}", msg); + } + } } } } From 3c008ad24a9465b82dcf48b0135fd73ae2b5674a Mon Sep 17 00:00:00 2001 From: Jason Siefken Date: Mon, 29 Apr 2024 21:46:48 -0400 Subject: [PATCH 2/4] Updated .expanded.rs files --- tests/expand/borrow.expanded.rs | 58 +++++++++++- tests/expand/generic_enum.expanded.rs | 58 +++++++++++- tests/expand/generic_struct.expanded.rs | 116 +++++++++++++++++++++++- tests/expandtest.rs | 4 + 4 files changed, 228 insertions(+), 8 deletions(-) diff --git a/tests/expand/borrow.expanded.rs b/tests/expand/borrow.expanded.rs index a5d6010..14dd9b5 100644 --- a/tests/expand/borrow.expanded.rs +++ b/tests/expand/borrow.expanded.rs @@ -192,7 +192,34 @@ const _: () = { type Abi = ::Abi; #[inline] fn into_abi(self) -> Self::Abi { - self.into_js().unwrap_throw().into_abi() + match self.into_js() { + Ok(js) => js.into_abi(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = { + let res = ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ); + res + }; + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } } } impl<'a> OptionIntoWasmAbi for Borrow<'a> @@ -210,7 +237,34 @@ const _: () = { { #[inline] fn from(value: Borrow<'a>) -> Self { - value.into_js().unwrap_throw().into() + match value.into_js() { + Ok(js) => js.into(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = { + let res = ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ); + res + }; + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } } } impl<'a> FromWasmAbi for Borrow<'a> diff --git a/tests/expand/generic_enum.expanded.rs b/tests/expand/generic_enum.expanded.rs index b7a4dd6..97730a5 100644 --- a/tests/expand/generic_enum.expanded.rs +++ b/tests/expand/generic_enum.expanded.rs @@ -198,7 +198,34 @@ const _: () = { type Abi = ::Abi; #[inline] fn into_abi(self) -> Self::Abi { - self.into_js().unwrap_throw().into_abi() + match self.into_js() { + Ok(js) => js.into_abi(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = { + let res = ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ); + res + }; + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } } } impl OptionIntoWasmAbi for GenericEnum @@ -216,7 +243,34 @@ const _: () = { { #[inline] fn from(value: GenericEnum) -> Self { - value.into_js().unwrap_throw().into() + match value.into_js() { + Ok(js) => js.into(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = { + let res = ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ); + res + }; + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } } } impl FromWasmAbi for GenericEnum diff --git a/tests/expand/generic_struct.expanded.rs b/tests/expand/generic_struct.expanded.rs index 71bfa9e..d8e5957 100644 --- a/tests/expand/generic_struct.expanded.rs +++ b/tests/expand/generic_struct.expanded.rs @@ -197,7 +197,34 @@ const _: () = { type Abi = ::Abi; #[inline] fn into_abi(self) -> Self::Abi { - self.into_js().unwrap_throw().into_abi() + match self.into_js() { + Ok(js) => js.into_abi(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = { + let res = ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ); + res + }; + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } } } impl OptionIntoWasmAbi for GenericStruct @@ -215,7 +242,34 @@ const _: () = { { #[inline] fn from(value: GenericStruct) -> Self { - value.into_js().unwrap_throw().into() + match value.into_js() { + Ok(js) => js.into(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = { + let res = ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ); + res + }; + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } } } impl FromWasmAbi for GenericStruct @@ -460,7 +514,34 @@ const _: () = { type Abi = ::Abi; #[inline] fn into_abi(self) -> Self::Abi { - self.into_js().unwrap_throw().into_abi() + match self.into_js() { + Ok(js) => js.into_abi(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = { + let res = ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ); + res + }; + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } } } impl OptionIntoWasmAbi for GenericNewtype @@ -478,7 +559,34 @@ const _: () = { { #[inline] fn from(value: GenericNewtype) -> Self { - value.into_js().unwrap_throw().into() + match value.into_js() { + Ok(js) => js.into(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = { + let res = ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ); + res + }; + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } } } impl FromWasmAbi for GenericNewtype diff --git a/tests/expandtest.rs b/tests/expandtest.rs index e1d3052..30d5ad5 100644 --- a/tests/expandtest.rs +++ b/tests/expandtest.rs @@ -1,3 +1,7 @@ +//! Generates expanded code for tests in `tests/expand/` directory. +//! To update the expected output, run with `MACROTEST=overwrite cargo test` +//! or delete the `.expanded.rs` files. + #[test] fn expandtest() { macrotest::expand_args("tests/expand/*.rs", ["--features", "tsify-next/json"]); From 92fbbeba6af7f06e1363e153559d89f843647009 Mon Sep 17 00:00:00 2001 From: Jason Siefken Date: Mon, 29 Apr 2024 21:48:06 -0400 Subject: [PATCH 3/4] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 019add8..54b1f86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # tsify-next Changelog +## v0.5.3 + +- Propagate errors encountered during serialization. +- More fixes for missing `From` trait implementations. + ## v0.5.2 - Fix missing trait bounds for implemented `From` traits. From 110d3d135d4872c0266599729513fc9fba4270f7 Mon Sep 17 00:00:00 2001 From: Jason Siefken Date: Mon, 29 Apr 2024 22:06:15 -0400 Subject: [PATCH 4/4] Version bump --- Cargo.toml | 4 ++-- README.md | 2 +- tsify-next-macros/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 36d5d6b..a75cf4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tsify-next" -version = "0.5.2" +version = "0.5.3" edition = "2021" authors = [ "Madono Haru ", @@ -14,7 +14,7 @@ keywords = ["wasm", "wasm-bindgen", "typescript"] categories = ["wasm"] [dependencies] -tsify-next-macros = { path = "tsify-next-macros", version = "0.5.2" } +tsify-next-macros = { path = "tsify-next-macros", version = "0.5.3" } wasm-bindgen = { version = "0.2.86", optional = true } serde = { version = "1.0", optional = true } serde_json = { version = "1.0", optional = true } diff --git a/README.md b/README.md index a794088..13f4db2 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Click to show Cargo.toml. ```toml [dependencies] -tsify-next = "0.5.2" +tsify-next = "0.5.3" serde = { version = "1.0", features = ["derive"] } wasm-bindgen = { version = "0.2" } ``` diff --git a/tsify-next-macros/Cargo.toml b/tsify-next-macros/Cargo.toml index 55cb04e..c79ad19 100644 --- a/tsify-next-macros/Cargo.toml +++ b/tsify-next-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tsify-next-macros" -version = "0.5.2" +version = "0.5.3" edition = "2021" authors = [ "Madono Haru ",