Description
Describe the Bug
wasm-bindgen
does not generate the static __wrap(ptr)
method for a JavaScript class corresponding to a Rust struct if the only way instances are created is via a #[wasm_bindgen(constructor)]
on the Rust side. This occurs even though the generated JavaScript class correctly manages the WASM pointer's lifetime using FinalizationRegistry
and includes free()
/ __destroy_into_raw()
methods.
The lack of __wrap
seems inconsistent, as the generated JS class is fundamentally wrapping a WASM resource, regardless of whether instances are created solely via the constructor or also returned from other functions.
Steps to Reproduce
- Define a Rust struct and mark it with
#[wasm_bindgen]
:use wasm_bindgen::prelude::*; #[wasm_bindgen] pub struct MyThing { // internal data } #[wasm_bindgen] impl MyThing { #[wasm_bindgen(constructor)] pub fn new() -> Self { // Logic to create the thing MyThing { /* ... */ } } // Add some methods that use `&self` or `&mut self` #[wasm_bindgen] pub fn do_something(&self) { // ... } } // IMPORTANT: Do NOT add any other function like this: // #[wasm_bindgen] // pub fn get_a_thing() -> MyThing { /* ... */ }
- Compile the Rust code to WASM and generate JavaScript bindings using
wasm-bindgen
. - Inspect the generated JavaScript file (e.g.,
my_module.js
). - Observe that the
MyThing
class in the JavaScript file is missing thestatic __wrap(ptr)
method, although it will havefree()
,__destroy_into_raw()
, and the correspondingFinalizationRegistry
.
Expected Behavior
The generated JavaScript MyThing
class should include the static __wrap(ptr)
method. Its presence indicates that the class wraps a WASM pointer, which is true even if the only creation path is the constructor. The generation of lifetime management code (FinalizationRegistry
, free
) further implies that __wrap
should logically be present.
Actual Behavior
The generated JavaScript MyThing
class lacks the static __wrap(ptr)
method.
Additional Context
This issue was encountered when creating bindings for a Provider
struct in a Rust library. Instances were only ever created via #[wasm_bindgen(constructor)]
. Other structs in the same library, which had functions returning instances (in addition to potentially having constructors), did have the __wrap
method generated correctly.
Furthermore, generating __wrap
consistently is beneficial in specific environments. For instance, in Unity WebGL builds, JavaScript object references might not persist reliably across certain engine events or callbacks. A common pattern is to use __destroy_into_raw()
to obtain the raw WASM pointer (which can be stored as a simple number), and later use __wrap()
to reconstruct the JavaScript wrapper object when it's needed again. The absence of __wrap
prevents this manual lifetime management pattern for classes that only have a #[wasm_bindgen(constructor)]
.
A workaround is to add a dummy #[wasm_bindgen]
function to the Rust impl
block that simply returns Self
or a clone, for example:
#[wasm_bindgen]
impl MyThing {
// ... other methods ...
// Dummy method to force __wrap generation
#[wasm_bindgen(js_name = internalGetSelf)]
pub fn internal_get_self(&self) -> Self {
// Return self or a clone as appropriate
MyThing { /* ... */ } // Or self.clone() if Clone is implemented
}
}
Adding this unnecessary function forces wasm-bindgen
to generate the __wrap
method in the JavaScript output. This suggests the generation logic for __wrap
is currently tied only to non-constructor functions returning the type.