Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace TryFrom with custom TryFromJsValue trait #3709

Merged
merged 8 commits into from
Nov 27, 2023
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -16,6 +16,9 @@
* The DWARF section is now correctly modified instead of leaving it in a broken state.
[#3483](https://github.com/rustwasm/wasm-bindgen/pull/3483)

* Fixed an issue where `#[wasm_bindgen]` automatically derived the `TryFrom` trait for any struct, preventing custom `TryFrom<JsValue>` implementations. It has been updated to utilize a new `TryFromJsValue` trait instead.
[#3709](https://github.com/rustwasm/wasm-bindgen/pull/3709)

* Update the TypeScript signature of `__wbindgen_thread_destroy` to indicate that it's parameters are optional.
[#3703](https://github.com/rustwasm/wasm-bindgen/pull/3703)

Expand Down
12 changes: 7 additions & 5 deletions crates/backend/src/codegen.rs
Expand Up @@ -297,10 +297,10 @@ impl ToTokens for ast::Struct {
}

#[allow(clippy::all)]
impl #wasm_bindgen::__rt::core::convert::TryFrom<#wasm_bindgen::JsValue> for #name {
impl #wasm_bindgen::convert::TryFromJsValue for #name {
type Error = #wasm_bindgen::JsValue;

fn try_from(value: #wasm_bindgen::JsValue)
fn try_from_js_value(value: #wasm_bindgen::JsValue)
-> #wasm_bindgen::__rt::std::result::Result<Self, Self::Error> {
let idx = #wasm_bindgen::convert::IntoWasmAbi::into_abi(&value);

Expand Down Expand Up @@ -819,6 +819,7 @@ impl ToTokens for ast::ImportType {

#[automatically_derived]
const _: () = {
use #wasm_bindgen::convert::TryFromJsValue;
use #wasm_bindgen::convert::{IntoWasmAbi, FromWasmAbi};
use #wasm_bindgen::convert::{OptionIntoWasmAbi, OptionFromWasmAbi};
use #wasm_bindgen::convert::{RefFromWasmAbi, LongRefFromWasmAbi};
Expand Down Expand Up @@ -1452,11 +1453,12 @@ impl ToTokens for ast::Enum {
}

#[allow(clippy::all)]
impl #wasm_bindgen::__rt::core::convert::TryFrom<#wasm_bindgen::JsValue> for #enum_name {
impl #wasm_bindgen::convert::TryFromJsValue for #enum_name {
type Error = #wasm_bindgen::JsValue;

fn try_from(value: #wasm_bindgen::JsValue)
-> #wasm_bindgen::__rt::std::result::Result<Self, <#enum_name as #wasm_bindgen::__rt::core::convert::TryFrom<JsValue>>::Error> {
fn try_from_js_value(value: #wasm_bindgen::JsValue)
-> #wasm_bindgen::__rt::std::result::Result<Self, <#enum_name as #wasm_bindgen::convert::TryFromJsValue>::Error> {
use #wasm_bindgen::__rt::core::convert::TryFrom;
let js = f64::try_from(&value)? as u32;

#wasm_bindgen::__rt::std::result::Result::Ok(
Expand Down
6 changes: 3 additions & 3 deletions src/convert/impls.rs
Expand Up @@ -2,13 +2,13 @@ use core::char;
use core::mem::{self, ManuallyDrop};

use crate::convert::traits::{WasmAbi, WasmPrimitive};
use crate::convert::TryFromJsValue;
use crate::convert::{FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, RefFromWasmAbi};
use crate::convert::{OptionFromWasmAbi, OptionIntoWasmAbi, ReturnWasmAbi};
use crate::{Clamped, JsError, JsValue, UnwrapThrowExt};

if_std! {
use std::boxed::Box;
use std::convert::{TryFrom, TryInto};
use std::fmt::Debug;
use std::vec::Vec;
}
Expand Down Expand Up @@ -415,7 +415,7 @@ if_std! {
js_vals.into_abi()
}

pub unsafe fn js_value_vector_from_abi<T: TryFrom<JsValue>>(js: <Box<[JsValue]> as FromWasmAbi>::Abi) -> Box<[T]> where T::Error: Debug {
pub unsafe fn js_value_vector_from_abi<T: TryFromJsValue>(js: <Box<[JsValue]> as FromWasmAbi>::Abi) -> Box<[T]> where T::Error: Debug {
let js_vals = <Vec<JsValue> as FromWasmAbi>::from_abi(js);

let mut result = Vec::with_capacity(js_vals.len());
Expand All @@ -430,7 +430,7 @@ if_std! {
// we're talking about, it can only see functions that actually make it to the
// final wasm binary (i.e., not inlined functions). All of those internal
// iterator functions get inlined in release mode, and so they don't show up.
result.push(value.try_into().expect_throw("array contains a value of the wrong type"));
result.push(T::try_from_js_value(value).expect_throw("array contains a value of the wrong type"));
}
result.into_boxed_slice()
}
Expand Down
16 changes: 16 additions & 0 deletions src/convert/traits.rs
Expand Up @@ -2,6 +2,7 @@ use core::borrow::Borrow;
use core::ops::{Deref, DerefMut};

use crate::describe::*;
use crate::JsValue;

/// A trait for anything that can be converted into a type that can cross the
/// wasm ABI directly, eg `u32` or `f64`.
Expand Down Expand Up @@ -249,3 +250,18 @@ impl<T: WasmAbi> WasmRet<T> {
T::join(self.prim1, self.prim2, self.prim3, self.prim4)
}
}

/// [`TryFromJsValue`] is a trait for converting a JavaScript value ([`JsValue`])
/// into a Rust type. It is used by the [`wasm_bindgen`](wasm_bindgen_macro::wasm_bindgen)
/// proc-macro to allow conversion to user types.
///
/// Types implementing this trait must specify their conversion logic from
/// [`JsValue`] to the Rust type, handling any potential errors that may occur
/// during the conversion process.
pub trait TryFromJsValue: Sized {
/// The type returned in the event of a conversion error.
type Error;

/// Performs the conversion.
fn try_from_js_value(value: JsValue) -> Result<Self, Self::Error>;
}
13 changes: 12 additions & 1 deletion src/lib.rs
Expand Up @@ -18,7 +18,7 @@ use core::ops::{
};
use core::u32;

use crate::convert::{FromWasmAbi, WasmRet, WasmSlice};
use crate::convert::{FromWasmAbi, TryFromJsValue, WasmRet, WasmSlice};

macro_rules! if_std {
($($i:item)*) => ($(
Expand Down Expand Up @@ -815,6 +815,17 @@ if_std! {
}
}
}

impl TryFromJsValue for String {
type Error = JsValue;

fn try_from_js_value(value: JsValue) -> Result<Self, Self::Error> {
match value.as_string() {
Some(s) => Ok(s),
None => Err(value),
}
}
}
}

impl From<bool> for JsValue {
Expand Down