From bd5da44d5d5baf14a8b23656542dfa124f06ad16 Mon Sep 17 00:00:00 2001 From: Oliver T Date: Fri, 18 Aug 2023 16:26:52 -0400 Subject: [PATCH 1/5] 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 --- crates/cli-support/src/js/binding.rs | 48 ++++++++++++--- crates/cli/tests/reference/builder.d.ts | 11 ++++ crates/cli/tests/reference/builder.js | 61 +++++++++++++++++++ crates/cli/tests/reference/builder.rs | 11 ++++ crates/cli/tests/reference/builder.wat | 10 +++ crates/cli/tests/reference/constructor.d.ts | 10 +++ crates/cli/tests/reference/constructor.js | 52 ++++++++++++++++ crates/cli/tests/reference/constructor.rs | 13 ++++ crates/cli/tests/reference/constructor.wat | 10 +++ crates/cli/tests/wasm-bindgen/main.rs | 27 ++++++++ .../attributes/on-rust-exports/constructor.md | 38 ++++++++++++ 11 files changed, 281 insertions(+), 10 deletions(-) create mode 100644 crates/cli/tests/reference/builder.d.ts create mode 100644 crates/cli/tests/reference/builder.js create mode 100644 crates/cli/tests/reference/builder.rs create mode 100644 crates/cli/tests/reference/builder.wat create mode 100644 crates/cli/tests/reference/constructor.d.ts create mode 100644 crates/cli/tests/reference/constructor.js create mode 100644 crates/cli/tests/reference/constructor.rs create mode 100644 crates/cli/tests/reference/constructor.wat diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index 83a04f92fb6..6a204fc9d1b 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -166,7 +166,12 @@ impl<'a, 'b> Builder<'a, 'b> { // more JIT-friendly. The generated code should be equivalent to the // wasm interface types stack machine, however. for instr in instructions { - instruction(&mut js, &instr.instr, &mut self.log_error)?; + instruction( + &mut js, + &instr.instr, + &mut self.log_error, + &self.constructor, + )?; } assert_eq!(js.stack.len(), adapter.results.len()); @@ -174,7 +179,11 @@ impl<'a, 'b> Builder<'a, 'b> { 0 => {} 1 => { let val = js.pop(); - js.prelude(&format!("return {};", val)); + if self.constructor.is_some() { + js.prelude(&format!("this.__wbg_ptr = {}", val)); + } else { + js.prelude(&format!("return {};", val)); + } } // TODO: this should be pretty trivial to support (commented out @@ -537,7 +546,12 @@ impl<'a, 'b> JsBuilder<'a, 'b> { } } -fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> Result<(), Error> { +fn instruction( + js: &mut JsBuilder, + instr: &Instruction, + log_error: &mut bool, + constructor: &Option, +) -> Result<(), Error> { match instr { Instruction::ArgGet(n) => { let arg = js.arg(*n).to_string(); @@ -987,18 +1001,32 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::RustFromI32 { class } => { - js.cx.require_class_wrap(class); let val = js.pop(); - js.push(format!("{}.__wrap({})", class, val)); + if let Some(name) = constructor { + if name != class { + bail!("constructor for `{}` cannot return `{}`", name, class); + } + js.push(format!("{} >>> 0;", val)); + } else { + js.cx.require_class_wrap(class); + js.push(format!("{}.__wrap({})", class, val)); + } } Instruction::OptionRustFromI32 { class } => { - js.cx.require_class_wrap(class); let val = js.pop(); - js.push(format!( - "{0} === 0 ? undefined : {1}.__wrap({0})", - val, class, - )) + if let Some(name) = constructor { + if name != class { + bail!("constructor for `{}` cannot return `{}`", name, class); + } + js.push(format!("{val} === 0 ? 0 : ({val} >>> 0);")); + } else { + js.cx.require_class_wrap(class); + js.push(format!( + "{0} === 0 ? undefined : {1}.__wrap({0})", + val, class, + )) + } } Instruction::CachedStringLoad { diff --git a/crates/cli/tests/reference/builder.d.ts b/crates/cli/tests/reference/builder.d.ts new file mode 100644 index 00000000000..ede348c9632 --- /dev/null +++ b/crates/cli/tests/reference/builder.d.ts @@ -0,0 +1,11 @@ +/* tslint:disable */ +/* eslint-disable */ +/** +*/ +export class ClassBuilder { + free(): void; +/** +* @returns {ClassBuilder} +*/ + static builder(): ClassBuilder; +} diff --git a/crates/cli/tests/reference/builder.js b/crates/cli/tests/reference/builder.js new file mode 100644 index 00000000000..3402667dd0d --- /dev/null +++ b/crates/cli/tests/reference/builder.js @@ -0,0 +1,61 @@ +let wasm; +export function __wbg_set_wasm(val) { + wasm = val; +} + + +const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; + +let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +let cachedUint8Memory0 = null; + +function getUint8Memory0() { + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} +/** +*/ +export class ClassBuilder { + + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(ClassBuilder.prototype); + obj.__wbg_ptr = ptr; + + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_classbuilder_free(ptr); + } + /** + * @returns {ClassBuilder} + */ + static builder() { + const ret = wasm.classbuilder_builder(); + return ClassBuilder.__wrap(ret); + } +} + +export function __wbindgen_throw(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +}; + diff --git a/crates/cli/tests/reference/builder.rs b/crates/cli/tests/reference/builder.rs new file mode 100644 index 00000000000..317f20cad92 --- /dev/null +++ b/crates/cli/tests/reference/builder.rs @@ -0,0 +1,11 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct ClassBuilder(()); + +#[wasm_bindgen] +impl ClassBuilder { + pub fn builder() -> ClassBuilder { + ClassBuilder(()) + } +} diff --git a/crates/cli/tests/reference/builder.wat b/crates/cli/tests/reference/builder.wat new file mode 100644 index 00000000000..cd38fc7fc6a --- /dev/null +++ b/crates/cli/tests/reference/builder.wat @@ -0,0 +1,10 @@ +(module + (type (;0;) (func (result i32))) + (type (;1;) (func (param i32))) + (func $__wbg_classbuilder_free (;0;) (type 1) (param i32)) + (func $classbuilder_builder (;1;) (type 0) (result i32)) + (memory (;0;) 17) + (export "memory" (memory 0)) + (export "__wbg_classbuilder_free" (func $__wbg_classbuilder_free)) + (export "classbuilder_builder" (func $classbuilder_builder)) +) diff --git a/crates/cli/tests/reference/constructor.d.ts b/crates/cli/tests/reference/constructor.d.ts new file mode 100644 index 00000000000..f35e8c72fa1 --- /dev/null +++ b/crates/cli/tests/reference/constructor.d.ts @@ -0,0 +1,10 @@ +/* tslint:disable */ +/* eslint-disable */ +/** +*/ +export class ClassConstructor { + free(): void; +/** +*/ + constructor(); +} diff --git a/crates/cli/tests/reference/constructor.js b/crates/cli/tests/reference/constructor.js new file mode 100644 index 00000000000..f92a8140703 --- /dev/null +++ b/crates/cli/tests/reference/constructor.js @@ -0,0 +1,52 @@ +let wasm; +export function __wbg_set_wasm(val) { + wasm = val; +} + + +const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; + +let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +let cachedUint8Memory0 = null; + +function getUint8Memory0() { + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} +/** +*/ +export class ClassConstructor { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_classconstructor_free(ptr); + } + /** + */ + constructor() { + const ret = wasm.classconstructor_new(); + this.__wbg_ptr = ret >>> 0; + } +} + +export function __wbindgen_throw(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +}; + diff --git a/crates/cli/tests/reference/constructor.rs b/crates/cli/tests/reference/constructor.rs new file mode 100644 index 00000000000..ca4bac080b3 --- /dev/null +++ b/crates/cli/tests/reference/constructor.rs @@ -0,0 +1,13 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct ClassConstructor(()); + +#[wasm_bindgen] +impl ClassConstructor { + + #[wasm_bindgen(constructor)] + pub fn new() -> ClassConstructor { + ClassConstructor(()) + } +} diff --git a/crates/cli/tests/reference/constructor.wat b/crates/cli/tests/reference/constructor.wat new file mode 100644 index 00000000000..a5596470321 --- /dev/null +++ b/crates/cli/tests/reference/constructor.wat @@ -0,0 +1,10 @@ +(module + (type (;0;) (func (result i32))) + (type (;1;) (func (param i32))) + (func $__wbg_classconstructor_free (;0;) (type 1) (param i32)) + (func $classconstructor_new (;1;) (type 0) (result i32)) + (memory (;0;) 17) + (export "memory" (memory 0)) + (export "__wbg_classconstructor_free" (func $__wbg_classconstructor_free)) + (export "classconstructor_new" (func $classconstructor_new)) +) diff --git a/crates/cli/tests/wasm-bindgen/main.rs b/crates/cli/tests/wasm-bindgen/main.rs index 055fad5ed1f..e2adb031640 100644 --- a/crates/cli/tests/wasm-bindgen/main.rs +++ b/crates/cli/tests/wasm-bindgen/main.rs @@ -431,3 +431,30 @@ fn function_table_preserved() { .wasm_bindgen(""); cmd.assert().success(); } + +#[test] +fn constructor_cannot_return_foreign_struct() { + let (mut cmd, _out_dir) = Project::new("constructor_cannot_return_foreign_struct") + .file( + "src/lib.rs", + r#" + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + pub struct Foo(()); + + #[wasm_bindgen] + pub struct Bar(()); + + #[wasm_bindgen] + impl Foo { + #[wasm_bindgen(constructor)] + pub fn new() -> Bar { + Bar(()) + } + } + "#, + ) + .wasm_bindgen("--target web"); + cmd.assert().failure(); +} diff --git a/guide/src/reference/attributes/on-rust-exports/constructor.md b/guide/src/reference/attributes/on-rust-exports/constructor.md index 1d6fcf4f625..4b154b7cc45 100644 --- a/guide/src/reference/attributes/on-rust-exports/constructor.md +++ b/guide/src/reference/attributes/on-rust-exports/constructor.md @@ -32,3 +32,41 @@ import { Foo } from './my_module'; const f = new Foo(); console.log(f.get_contents()); ``` + +## Caveats + +Starting from v0.2.48 there is a bug in the `wasm-bindgen` 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 from a Rust struct such as: + +```rust +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct Parent { + msg: String, +} + +#[wasm_bindgen] +impl Parent { + #[wasm_bindgen(constructor)] + fn new() -> Self { + Parent { + msg: String::from("Hello from Parent!"), + } + } +} +``` + +You will need to reset the prototype of `this` back to the `Child` class prototype after calling the `Parent`'s constructor via `super`. + +```js +import { Parent } from './my_module'; + +class Child extends Parent { + constructor() { + super(); + Object.setPrototypeOf(this, Child.prototype); + } +} +``` + +This is no longer required as of v0.2.88. From cecb629cd8a02244221456b4a78b5275c505527d Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 23 Aug 2023 20:54:02 -0400 Subject: [PATCH 2/5] Fix typo Co-authored-by: Liam Murphy --- guide/src/reference/attributes/on-rust-exports/constructor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/src/reference/attributes/on-rust-exports/constructor.md b/guide/src/reference/attributes/on-rust-exports/constructor.md index 4b154b7cc45..3e5fc2d4deb 100644 --- a/guide/src/reference/attributes/on-rust-exports/constructor.md +++ b/guide/src/reference/attributes/on-rust-exports/constructor.md @@ -35,7 +35,7 @@ console.log(f.get_contents()); ## Caveats -Starting from v0.2.48 there is a bug in the `wasm-bindgen` 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 from a Rust struct such as: +Starting from v0.2.48 there is a bug in `wasm-bindgen` 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 from a Rust struct such as: ```rust use wasm_bindgen::prelude::*; From c9bab0a437735167da9d4f0ac1c5388fcdde6c66 Mon Sep 17 00:00:00 2001 From: Oliver T Date: Thu, 24 Aug 2023 08:35:35 -0400 Subject: [PATCH 3/5] Disallow returning JS primitives from constructors Signed-off-by: Oliver T --- CHANGELOG.md | 8 +++++ crates/cli-support/src/js/binding.rs | 39 +++++++++-------------- crates/cli-support/src/wit/mod.rs | 24 +++++++++++++- crates/cli/tests/reference/constructor.js | 1 + crates/cli/tests/wasm-bindgen/main.rs | 11 +++---- 5 files changed, 51 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5964d2a9943..c97c392b16e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,11 @@ bindgen placeholder. [#3233](https://github.com/rustwasm/wasm-bindgen/pull/3233) +* Changed constructor implementation in generated JS bindings, it is now + possible to override methods from generated JS classes using inheritance. + When exported constructors return `Self`. + [#3562](https://github.com/rustwasm/wasm-bindgen/pull/3562) + ### Fixed * Fixed bindings and comments for `Atomics.wait`. @@ -66,6 +71,9 @@ * Use fully qualified paths in the `wasm_bindgen_test` macro. [#3549](https://github.com/rustwasm/wasm-bindgen/pull/3549) +* Fixed bug allowing JS primitives to be returned from exported constructors. + [#3562](https://github.com/rustwasm/wasm-bindgen/pull/3562) + ## [0.2.87](https://github.com/rustwasm/wasm-bindgen/compare/0.2.86...0.2.87) Released 2023-06-12. diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index 6a204fc9d1b..467220d05ff 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -179,11 +179,7 @@ impl<'a, 'b> Builder<'a, 'b> { 0 => {} 1 => { let val = js.pop(); - if self.constructor.is_some() { - js.prelude(&format!("this.__wbg_ptr = {}", val)); - } else { - js.prelude(&format!("return {};", val)); - } + js.prelude(&format!("return {};", val)); } // TODO: this should be pretty trivial to support (commented out @@ -1002,31 +998,26 @@ fn instruction( Instruction::RustFromI32 { class } => { let val = js.pop(); - if let Some(name) = constructor { - if name != class { - bail!("constructor for `{}` cannot return `{}`", name, class); + match constructor { + Some(name) if name == class => { + js.prelude(&format!("this.__wbg_ptr = {} >>> 0;", val)); + js.push(String::from("this")); + } + Some(_) | None => { + js.cx.require_class_wrap(class); + js.push(format!("{}.__wrap({})", class, val)); } - js.push(format!("{} >>> 0;", val)); - } else { - js.cx.require_class_wrap(class); - js.push(format!("{}.__wrap({})", class, val)); } } Instruction::OptionRustFromI32 { class } => { + assert!(constructor.is_none()); let val = js.pop(); - if let Some(name) = constructor { - if name != class { - bail!("constructor for `{}` cannot return `{}`", name, class); - } - js.push(format!("{val} === 0 ? 0 : ({val} >>> 0);")); - } else { - js.cx.require_class_wrap(class); - js.push(format!( - "{0} === 0 ? undefined : {1}.__wrap({0})", - val, class, - )) - } + js.cx.require_class_wrap(class); + js.push(format!( + "{0} === 0 ? undefined : {1}.__wrap({0})", + val, class, + )); } Instruction::CachedStringLoad { diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index 9d7b18e84c0..1fa3d1a1df7 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -484,7 +484,29 @@ impl<'a> Context<'a> { Some(class) => { let class = class.to_string(); match export.method_kind { - decode::MethodKind::Constructor => AuxExportKind::Constructor(class), + decode::MethodKind::Constructor => { + let msg = "Trying to return JS primitive from constructor, return value will be ignored. Use a builder instead (remove `constructor` attribute)."; + match descriptor.ret { + Descriptor::I8 + | Descriptor::U8 + | Descriptor::ClampedU8 + | Descriptor::I16 + | Descriptor::U16 + | Descriptor::I32 + | Descriptor::U32 + | Descriptor::F32 + | Descriptor::F64 + | Descriptor::I64 + | Descriptor::U64 + | Descriptor::Boolean + | Descriptor::Char + | Descriptor::CachedString + | Descriptor::Option(_) + | Descriptor::Unit => bail!(msg), + _ => {} + } + AuxExportKind::Constructor(class) + } decode::MethodKind::Operation(op) => { if !op.is_static { // Make the first argument be the index of the receiver. diff --git a/crates/cli/tests/reference/constructor.js b/crates/cli/tests/reference/constructor.js index f92a8140703..1973256e15c 100644 --- a/crates/cli/tests/reference/constructor.js +++ b/crates/cli/tests/reference/constructor.js @@ -43,6 +43,7 @@ export class ClassConstructor { constructor() { const ret = wasm.classconstructor_new(); this.__wbg_ptr = ret >>> 0; + return this; } } diff --git a/crates/cli/tests/wasm-bindgen/main.rs b/crates/cli/tests/wasm-bindgen/main.rs index e2adb031640..f2502d8c313 100644 --- a/crates/cli/tests/wasm-bindgen/main.rs +++ b/crates/cli/tests/wasm-bindgen/main.rs @@ -433,8 +433,8 @@ fn function_table_preserved() { } #[test] -fn constructor_cannot_return_foreign_struct() { - let (mut cmd, _out_dir) = Project::new("constructor_cannot_return_foreign_struct") +fn constructor_cannot_return_option_struct() { + let (mut cmd, _out_dir) = Project::new("constructor_cannot_return_option_struct") .file( "src/lib.rs", r#" @@ -443,14 +443,11 @@ fn constructor_cannot_return_foreign_struct() { #[wasm_bindgen] pub struct Foo(()); - #[wasm_bindgen] - pub struct Bar(()); - #[wasm_bindgen] impl Foo { #[wasm_bindgen(constructor)] - pub fn new() -> Bar { - Bar(()) + pub fn new() -> Option { + Some(Foo(())) } } "#, From 783fbf7bb4d0eae121b022c05d978a59c9080a4a Mon Sep 17 00:00:00 2001 From: Oliver T Date: Thu, 24 Aug 2023 16:57:56 -0400 Subject: [PATCH 4/5] Added missing String in match Signed-off-by: Oliver T --- crates/cli-support/src/wit/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index 1fa3d1a1df7..1991812f1f3 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -501,6 +501,7 @@ impl<'a> Context<'a> { | Descriptor::Boolean | Descriptor::Char | Descriptor::CachedString + | Descriptor::String | Descriptor::Option(_) | Descriptor::Unit => bail!(msg), _ => {} From a7d253b52fa96108c83bad03931739d495c46d58 Mon Sep 17 00:00:00 2001 From: Oliver T Date: Fri, 25 Aug 2023 13:08:39 -0400 Subject: [PATCH 5/5] Handle nested descriptors Signed-off-by: Oliver T --- crates/cli-support/src/wit/mod.rs | 52 ++++++++++++++++++------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index 1991812f1f3..38402aafe9d 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -485,27 +485,7 @@ impl<'a> Context<'a> { let class = class.to_string(); match export.method_kind { decode::MethodKind::Constructor => { - let msg = "Trying to return JS primitive from constructor, return value will be ignored. Use a builder instead (remove `constructor` attribute)."; - match descriptor.ret { - Descriptor::I8 - | Descriptor::U8 - | Descriptor::ClampedU8 - | Descriptor::I16 - | Descriptor::U16 - | Descriptor::I32 - | Descriptor::U32 - | Descriptor::F32 - | Descriptor::F64 - | Descriptor::I64 - | Descriptor::U64 - | Descriptor::Boolean - | Descriptor::Char - | Descriptor::CachedString - | Descriptor::String - | Descriptor::Option(_) - | Descriptor::Unit => bail!(msg), - _ => {} - } + verify_constructor_return(&class, &descriptor.ret)?; AuxExportKind::Constructor(class) } decode::MethodKind::Operation(op) => { @@ -1447,6 +1427,36 @@ impl<'a> Context<'a> { } } +/// Verifies exported constructor return value is not a JS primitive type +fn verify_constructor_return(class: &str, ret: &Descriptor) -> Result<(), Error> { + match ret { + Descriptor::I8 + | Descriptor::U8 + | Descriptor::ClampedU8 + | Descriptor::I16 + | Descriptor::U16 + | Descriptor::I32 + | Descriptor::U32 + | Descriptor::F32 + | Descriptor::F64 + | Descriptor::I64 + | Descriptor::U64 + | Descriptor::Boolean + | Descriptor::Char + | Descriptor::CachedString + | Descriptor::String + | Descriptor::Option(_) + | Descriptor::Enum { .. } + | Descriptor::Unit => { + bail!("The constructor for class `{}` tries to return a JS primitive type, which would cause the return value to be ignored. Use a builder instead (remove the `constructor` attribute).", class); + } + Descriptor::Result(ref d) | Descriptor::Ref(ref d) | Descriptor::RefMut(ref d) => { + verify_constructor_return(class, d) + } + _ => Ok(()), + } +} + /// Extract all of the `Program`s encoded in our custom section. /// /// `program_storage` is used to squirrel away the raw bytes of the custom