wasm-bindgen
wasm-bindgen copied to clipboard
__wrap not generated when only #[wasm_bindgen(constructor)] returns Self
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
MyThingclass 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.