-
-
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
Remove class wrap for constructors in Rust exports #3561
Conversation
After rustwasm#1594 constructors of Rust exported structs started using class wrapping when generating JS shims. Wrapping erases prototype information from the object instance in JS and as a result it is not possible to override methods (via inheritance) of the generated class. Additionally, some checks to ensure constructors always return an instance of `Self` were lost. This PR fixes the above two issues by embedding the kind of export into the `CallExport` instruction and monitoring for constructor exports calls during the export adapter creation. Once a constructor export call is detected and validated a new instruction `SelfFromI32` is pushed into the stack that avoids class wrapping. Fixes rustwasm#3213 Signed-off-by: Oliver T <geronimooliver00@gmail.com>
Signed-off-by: Oliver T <geronimooliver00@gmail.com>
ret.outgoing(&signature.ret)?; | ||
if let Instruction::CallExport(_, AuxExportKind::Constructor(ref class)) = call { | ||
if let Descriptor::RustStruct(ref name) = signature.ret { | ||
if class != name { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How useful is this check really? I mean all you have to do to bypass it is to wrap your foreign return type inside an Option
or any other parameterized type and you've gone back to the old behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should reject Option<T>
too - only the Rust struct that the constructor's for should be allowed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a bit of a different idea for how to implement this, which was to replace the generated return {}
with this.__wbg_ptr = {}
for constructors. return
statements get inserted here, and you can already check whether the function's a constructor using self.constructor
, so it doesn't require piping extra state around. I should probably have mentioned this when you asked for pointers; sorry about that.
Anyway, this implementation is also fine so you can stick with it if you want, but getting rid of the return
entirely is a bit cleaner IMO.
|
||
## Caveats | ||
|
||
Starting from v0.2.48 there is a bug in the `wasm-bindgen-cli-support` crate which breaks inheritance of exported Rust structs from JavaScript side (see [#3213](https://github.com/rustwasm/wasm-bindgen/issues/3213)). If you want to inherit a Rust struct such as: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think anyone cares about which internal crate caused the problem; you should probably just say wasm-bindgen
or 'the wasm-bindgen
CLI'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure if it was worth even including this. But yeah good point, I'll make these changes.
Oops, that wouldn't actually work, that'd try to assign an object to |
Hmm, I had not thought about this!
I was thinking of instead passing the fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool, constructor: &Option<String>) -> Result<(), Error> {
match instr {
Instruction::RustFromI32 { class } => {
if let Some(name) = constructor {
if name != class {
bail!("constructor for `{}` cannot return `{}`", class, name);
}
let val = js.pop();
js.push(format!("{} >>> 0;", val));
} else {
js.cx.require_class_wrap(class);
let val = js.pop();
js.push(format!("{}.__wrap({})", class, val));
}
}
}
} What do you think? I'd also have to modify the return like you mentioned above. And, thank you for looking into this! It is indeed much cleaner. I'll probably make another PR based off from this one. |
After rustwasm#1594 constructors of Rust exported structs started using class wrapping when generating JS shims. Wrapping erases prototype information from the object instance in JS and as a result it is not possible to override methods (via inheritance) of the generated class. Additionally, some checks to ensure constructors always return an instance of `Self` were lost. This PR is rebased from rustwasm#3561, it passes the constructor information from the `Builder` into the instruction translation function which is then used to modify the generated bindings. The `return` statement is also removed instead the value is directly attached to the instance. Signed-off-by: Oliver T <geronimooliver00@gmail.com>
* Remove class wrap for constructors in Rust exports After #1594 constructors of Rust exported structs started using class wrapping when generating JS shims. Wrapping erases prototype information from the object instance in JS and as a result it is not possible to override methods (via inheritance) of the generated class. Additionally, some checks to ensure constructors always return an instance of `Self` were lost. This PR is rebased from #3561, it passes the constructor information from the `Builder` into the instruction translation function which is then used to modify the generated bindings. The `return` statement is also removed instead the value is directly attached to the instance. Signed-off-by: Oliver T <geronimooliver00@gmail.com> * Fix typo Co-authored-by: Liam Murphy <liampm32@gmail.com> * Disallow returning JS primitives from constructors Signed-off-by: Oliver T <geronimooliver00@gmail.com> * Added missing String in match Signed-off-by: Oliver T <geronimooliver00@gmail.com> * Handle nested descriptors Signed-off-by: Oliver T <geronimooliver00@gmail.com> --------- Signed-off-by: Oliver T <geronimooliver00@gmail.com> Co-authored-by: Liam Murphy <liampm32@gmail.com>
After #1594 constructors of Rust exported structs started using class wrapping when generating JS shims. Wrapping erases prototype information from the object instance in JS and as a result it is not possible to override methods (via inheritance) of the generated class.
Additionally, some checks to ensure constructors always return an instance of
Self
were lost.This PR fixes the above two issues by embedding the kind of export into the
CallExport
instruction and monitoring for constructor exports calls during the export adapter creation.Once a constructor export call is detected and validated a new instruction
SelfFromI32
is pushed into the stack that avoids class wrapping.Fixes #3213