-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
TryFromJsValue::try_from_js_value()
invalidates to converted value on failure
#4289
Comments
try_from_js_value
on a #[wasm_bindgen] structstry_from_js_value
on #[wasm_bindgen] structs
I ran into this in an identical way recently, so to jump in as well -- In fairness when I did look this up I realized that there is this warning on docs.rs on this module and on If I understood this correctly, the issue seems to be that... with the way that That ends up meaning that if you Below is the generated code for the moving parts in this flow. Initially I wondered if the I too would love to see a solution for this, because as far as I'm aware there is no other way to take back or clone out a Rust type passed to JS at present, nor even to check its type like this. My temporary solution was a switch statement with a number of #[automatically_derived]
impl wasm_bindgen::convert::TryFromJsValue for Label {
type Error = wasm_bindgen::JsValue;
fn try_from_js_value(
value: wasm_bindgen::JsValue,
) -> wasm_bindgen::__rt::core::result::Result<Self, Self::Error> {
let idx = wasm_bindgen::convert::IntoWasmAbi::into_abi(&value);
#[link(wasm_import_module = "__wbindgen_placeholder__")]
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
extern "C" {
fn __wbg_label_unwrap(ptr: u32) -> u32;
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
unsafe fn __wbg_label_unwrap(_: u32) -> u32 {
{
core::panicking::panic_fmt(core::const_format_args!(
"cannot convert from JsValue outside of the Wasm target"
));
}
}
let ptr = unsafe { __wbg_label_unwrap(idx) };
if ptr == 0 {
wasm_bindgen::__rt::core::result::Result::Err(value)
} else {
#[allow(clippy::mem_forget)]
wasm_bindgen::__rt::core::mem::forget(value);
unsafe {
wasm_bindgen::__rt::core::result::Result::Ok(
<Self as wasm_bindgen::convert::FromWasmAbi>::from_abi(ptr),
)
}
}
}
}
#[automatically_derived]
impl wasm_bindgen::convert::FromWasmAbi for Label {
type Abi = u32;
unsafe fn from_abi(js: u32) -> Self {
use wasm_bindgen::__rt::alloc::rc::Rc;
use wasm_bindgen::__rt::core::result::Result::{Err, Ok};
use wasm_bindgen::__rt::{assert_not_null, WasmRefCell};
let ptr = js as *mut WasmRefCell<Label>;
assert_not_null(ptr);
let rc = Rc::from_raw(ptr);
match Rc::try_unwrap(rc) {
Ok(cell) => cell.into_inner(),
Err(_) => wasm_bindgen::throw_str(
"attempted to take ownership of Rust value while it was borrowed",
),
}
}
} export function __wbg_label_unwrap() { return logError(function (arg0) {
const ret = Label.__unwrap(arg0);
_assertNum(ret);
return ret;
}, arguments) };
export class Label {
constructor() {
throw new Error('cannot invoke `new` directly');
}
static __wrap(ptr) {
ptr = ptr >>> 0;
const obj = Object.create(Label.prototype);
obj.__wbg_ptr = ptr;
LabelFinalization.register(obj, obj.__wbg_ptr, obj);
return obj;
}
static __unwrap(jsValue) {
if (!(jsValue instanceof Label)) {
return 0;
}
return jsValue.__destroy_into_raw();
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
LabelFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_label_free(ptr, 0);
}
// ... snip
} |
I assume you just didn't know about the In any case, I assume the issue is resolved. Let me know if you are having trouble using |
@daxpedda Are you suggesting that I need to re-import the generated JavaScript type and check against that, then use To expand on what I'm trying to do I have an enum like this in Rust: pub enum Shape {
Square {
side_length: f64,
}
Circle {
radius: f64,
}
} The pattern I want to use is one I've used before when writing Python bindings: create a type for each variant, and use run-time type checks to work out which variant an argument is. So I create two wrapped type like these: #[wasm_bindgen]
struct SquareShape {
side_length: f64,
}
#[wasm_bindgen]
struct CircleShape {
radius: f64,
} Then in a wrapped function I want to determine which one a value is. What I wanted was something that would look like this: #[wasm_bindgen]
pub fn do_something_with_shape(shape: JsValue) -> Result<(), JsError> {
let real_shape = if let Some(square) = cast_ref::<SquareShape>(&shape) {
wrapped_crate::Shape::Square {
side_length: square.side_length,
}
} else if let Some(circle) = cast_ref::<CircleShape>(&shape) {
wrapped_crate::Shape::Circle {
radius: circle.radius,
}
} else {
return Err(...);
};
wrapped_crate::do_something_with_shape(real_shape);
Ok(JsError::new("expected a SquareShape or CircleShape"))
} The gap here is what is |
Apologies, I didn't take a close look at your issue before responding. You should be able to find more information and some workarounds there. |
@daxpedda I feel like you've still misunderstood my intentions when I created this issue. This is not a feature request, or a question like #2231. I have read that case; in fact it's what suggested that It is a bug report for Assuming that it isn't easy to fix, how about changing the documentation from "no stability guarantees" to something like "don't call this yourself", or explaining that catching the error isn't a good idea? I realise that if this is, in fact, caused by the |
We might want to simply move it to the However, the way this API is currently implemented is very easy to mis-use and the signature should change or it should simply be marked as Ideally, we actually address #2231 and fix Thank you for your persistence here, my apologies for not more carefully reading your responses! |
try_from_js_value
on #[wasm_bindgen] structsTryFromJsValue::try_from_js_value()
invalidates to converted value on failure
Describe the Bug
I wanted to have function that accepts a
JsValue
argument and checked at run-time which of several #[wasm_bindgen] structs it was and does something slightly different for each one. I instead got a hard to diagnose panic in a different crate caused by::js_sys::Reflect::set
returning a leftover error.Steps to Reproduce
The code I wrote looked like this:
Shortly after this I made some
wgpu
calls, and one of them panicked out of nowhere on an assertion that said "setting properties should never fail".Changing the order of the types checked fixed the problem, until I tried the other type.
Expected Behavior
Any error set by
try_from_js_value
should be contained in its return value, and not be able to affect other code. I should not be required to return an error whentry_from_js_value
fails; checking multiple types should be a valid.Alternatively, this pattern should be prevented by the compiler somehow, or worst case documented as causing problems.
Actual Behavior
A subsequent call to
js_sys::Reflect::set
insidewgpu::backend::webgpu::webgpu_sys::gen_GpuBufferDescriptor::GpuBufferDescriptor::size
failed and caused a panic. This assertion is debug only, in release mode I instead got an error from WebGPU due to the missing field.This was running on Chrome on Windows in case that matters.
The text was updated successfully, but these errors were encountered: