Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
This works, but blanket disallows returning something other than `Self` from a constructor, which could be a breaking change.
  • Loading branch information
Liamolucko committed Dec 24, 2022
1 parent 7c6adba commit 052281f
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 5 deletions.
9 changes: 8 additions & 1 deletion crates/cli-support/src/js/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,14 @@ impl<'a, 'b> Builder<'a, 'b> {
0 => {}
1 => {
let val = js.pop();
js.prelude(&format!("return {};", val));
if self.constructor.is_some() {
// Instead of returning, initialize `this`.
// We should have already switched the return type to `u32` earlier.
assert_eq!(adapter.results, [AdapterType::U32]);
js.prelude(&format!("this.ptr = {};", val));
} else {
js.prelude(&format!("return {};", val));
}
}

// TODO: this should be pretty trivial to support (commented out
Expand Down
12 changes: 10 additions & 2 deletions crates/cli-support/src/wit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::intrinsic::Intrinsic;
use crate::{decode, PLACEHOLDER_MODULE};
use anyhow::{anyhow, bail, Error};
use std::collections::{HashMap, HashSet};
use std::str;
use std::{mem, str};
use walrus::MemoryId;
use walrus::{ExportId, FunctionId, ImportId, Module};
use wasm_bindgen_shared::struct_function_export_name;
Expand Down Expand Up @@ -423,7 +423,15 @@ 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 old_ret = mem::replace(&mut descriptor.ret, Descriptor::U32);
if !matches!(old_ret, Descriptor::RustStruct(ref ret_class) if ret_class == &class)
{
bail!("Constructor for `{class}` does not return an instance of `{class}`");
}

AuxExportKind::Constructor(class)
}
decode::MethodKind::Operation(op) => {
if !op.is_static {
// Make the first argument be the index of the receiver.
Expand Down
43 changes: 43 additions & 0 deletions tests/wasm/classes.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,49 @@ exports.js_simple = () => {
r3.free();
};

exports.js_subclasses = () => {
class Subclass extends wasm.ClassesSimple {
constructor() {
super();
this.value = 42;
}

getValue() {
return this.value;
}
}

const subclass = new Subclass();
assert(subclass instanceof Subclass);
assert(subclass instanceof wasm.ClassesSimple);
assert.strictEqual(subclass.getValue(), 42);
assert.strictEqual(subclass.add(0), 0);
assert.strictEqual(subclass.add(1), 1);
assert.strictEqual(subclass.add(1), 2);
subclass.free();

class OverridingSubclass extends wasm.ClassesSimple {
constructor() {
super();
this.value = 42;
}

getValue() {
return this.value;
}

add(value) {
this.value += value;
}
}

const overrider = new OverridingSubclass();
assert.strictEqual(overrider.getValue(), 42);
assert.strictEqual(overrider.add(3), undefined);
assert.strictEqual(overrider.getValue(), 45);
overrider.free();
};

exports.js_strings = () => {
const r = wasm.ClassesStrings1.new();
r.set(3);
Expand Down
6 changes: 6 additions & 0 deletions tests/wasm/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use wasm_bindgen_test::*;
#[wasm_bindgen(module = "tests/wasm/classes.js")]
extern "C" {
fn js_simple();
fn js_subclasses();
fn js_strings();
fn js_exceptions();
fn js_pass_one_to_another();
Expand Down Expand Up @@ -41,6 +42,11 @@ fn simple() {
js_simple();
}

#[wasm_bindgen_test]
fn subclasses() {
js_subclasses();
}

#[wasm_bindgen]
pub struct ClassesSimple {
contents: u32,
Expand Down
2 changes: 1 addition & 1 deletion tests/wasm/futures.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ exports.call_exports = async function() {
await assert.rejects(wasm.async_throw_jserror(), /async message/);
await assert.rejects(wasm.async_throw_custom_error(), /custom error/);
assert.strictEqual("Hi, Jim!", await wasm.async_take_reference("Jim"));
const foo = await new wasm.AsyncStruct();
const foo = new wasm.AsyncStruct();
assert.strictEqual(42, await foo.method());
await wasm.async_take_js_reference(42);
const buffer = new Int32Array([1, 2, 3, 4]);
Expand Down
2 changes: 1 addition & 1 deletion tests/wasm/futures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ pub struct AsyncStruct;
#[wasm_bindgen]
impl AsyncStruct {
#[wasm_bindgen(constructor)]
pub async fn new() -> AsyncStruct {
pub fn new() -> AsyncStruct {
AsyncStruct
}

Expand Down

0 comments on commit 052281f

Please sign in to comment.