summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/function-references
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/function-references')
-rw-r--r--js/src/jit-test/tests/wasm/function-references/as-non-null.js13
-rw-r--r--js/src/jit-test/tests/wasm/function-references/binary.js52
-rw-r--r--js/src/jit-test/tests/wasm/function-references/br-non-null.js76
-rw-r--r--js/src/jit-test/tests/wasm/function-references/br-null.js58
-rw-r--r--js/src/jit-test/tests/wasm/function-references/call_ref.js100
-rw-r--r--js/src/jit-test/tests/wasm/function-references/directives.txt1
-rw-r--r--js/src/jit-test/tests/wasm/function-references/disabled.js17
-rw-r--r--js/src/jit-test/tests/wasm/function-references/nnl-test.js172
-rw-r--r--js/src/jit-test/tests/wasm/function-references/non-nullable-table.js79
-rw-r--r--js/src/jit-test/tests/wasm/function-references/non-nullable.js176
-rw-r--r--js/src/jit-test/tests/wasm/function-references/reftype-parse.js51
11 files changed, 795 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/function-references/as-non-null.js b/js/src/jit-test/tests/wasm/function-references/as-non-null.js
new file mode 100644
index 0000000000..1280c8ee06
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/as-non-null.js
@@ -0,0 +1,13 @@
+// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
+
+let {checkNonNull} = wasmEvalText(`(module
+ (func (export "checkNonNull") (param externref) (result (ref extern))
+ local.get 0
+ ref.as_non_null
+ )
+)`).exports;
+
+assertErrorMessage(() => checkNonNull(null), WebAssembly.RuntimeError, /dereferencing null pointer/);
+for (let val of WasmNonNullExternrefValues) {
+ assertEq(checkNonNull(val), val, `is non-null`);
+}
diff --git a/js/src/jit-test/tests/wasm/function-references/binary.js b/js/src/jit-test/tests/wasm/function-references/binary.js
new file mode 100644
index 0000000000..91cf807dc4
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/binary.js
@@ -0,0 +1,52 @@
+// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
+
+load(libdir + "wasm-binary.js");
+
+const v2vSig = {args:[], ret:VoidCode};
+const v2vSigSection = sigSection([v2vSig]);
+
+function checkInvalid(binary, errorMessage) {
+ assertErrorMessage(() => new WebAssembly.Module(binary),
+ WebAssembly.CompileError,
+ errorMessage);
+}
+
+// The immediate of ref.null is a heap type, not a general reference type
+
+const invalidRefNullHeapBody = moduleWithSections([
+ v2vSigSection,
+ declSection([0]),
+ bodySection([
+ funcBody({locals:[], body:[
+ RefNullCode,
+ OptRefCode,
+ AnyFuncCode,
+ DropCode,
+ ]})
+ ])
+]);
+checkInvalid(invalidRefNullHeapBody, /invalid heap type/);
+
+const invalidRefNullHeapElem = moduleWithSections([
+ generalElemSection([
+ {
+ flag: PassiveElemExpr,
+ typeCode: AnyFuncCode,
+ elems: [
+ [RefNullCode, OptRefCode, AnyFuncCode, EndCode]
+ ]
+ }
+ ])
+]);
+checkInvalid(invalidRefNullHeapElem, /invalid heap type/);
+
+const invalidRefNullHeapGlobal = moduleWithSections([
+ globalSection([
+ {
+ valType: AnyFuncCode,
+ flag: 0,
+ initExpr: [RefNullCode, OptRefCode, AnyFuncCode, EndCode]
+ }
+ ])
+]);
+checkInvalid(invalidRefNullHeapGlobal, /invalid heap type/);
diff --git a/js/src/jit-test/tests/wasm/function-references/br-non-null.js b/js/src/jit-test/tests/wasm/function-references/br-non-null.js
new file mode 100644
index 0000000000..371cdaa40a
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/br-non-null.js
@@ -0,0 +1,76 @@
+// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
+
+// br_on_non_null from constant
+wasmValidateText(`(module
+ (func
+ block (result (ref extern))
+ ref.null extern
+ br_on_non_null 0
+ return
+ end
+ drop
+ )
+)`);
+
+// br_on_non_null from parameter
+wasmValidateText(`(module
+ (func (param externref) (result (ref extern))
+ local.get 0
+ br_on_non_null 0
+ unreachable
+ )
+)`);
+
+// br_on_null with multiple results
+wasmValidateText(`(module
+ (func (param (ref null extern) (ref extern)) (result i32 i32 i32 (ref extern))
+ i32.const 0
+ i32.const 1
+ i32.const 2
+ local.get 0
+ br_on_non_null 0
+ local.get 1
+ )
+)`);
+
+// no block type
+wasmFailValidateText(`(module
+ (func
+ block
+ ref.null extern
+ br_on_non_null 0
+ end
+ )
+)`, /type mismatch: target block type expected to be \[_, ref\]/);
+
+// in dead code
+wasmValidateText(`(module
+ (type $t (func))
+ (func (result funcref)
+ ref.null $t
+ return
+ br_on_non_null 0
+ )
+)`);
+
+wasmFailValidateText(`(module
+ (func
+ return
+ br_on_non_null 0
+ )
+)`, /type mismatch: target block type expected to be \[_, ref\]/);
+
+// Test the branch takes the correct path and results are passed correctly
+let {ifNull} = wasmEvalText(`(module
+ (func (export "ifNull") (param externref externref) (result externref)
+ local.get 0
+ br_on_non_null 0
+ local.get 1
+ )
+)`).exports;
+
+const DefaultTestVal = "default!test";
+assertEq(ifNull(null, DefaultTestVal), DefaultTestVal);
+for (let val of WasmNonNullExternrefValues) {
+ assertEq(ifNull(val, DefaultTestVal), val);
+}
diff --git a/js/src/jit-test/tests/wasm/function-references/br-null.js b/js/src/jit-test/tests/wasm/function-references/br-null.js
new file mode 100644
index 0000000000..26d1013de2
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/br-null.js
@@ -0,0 +1,58 @@
+// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
+
+// br_on_null from constant
+wasmValidateText(`(module
+ (func
+ ref.null extern
+ br_on_null 0
+ drop
+ )
+)`);
+
+// br_on_null from parameter
+wasmValidateText(`(module
+ (func (param externref)
+ local.get 0
+ br_on_null 0
+ drop
+ )
+)`);
+
+// br_on_null with single result
+wasmValidateText(`(module
+ (func (result i32)
+ i32.const 0
+ ref.null extern
+ br_on_null 0
+ drop
+ )
+)`);
+
+// br_on_null with multiple results
+wasmValidateText(`(module
+ (func (result i32 i32 i32)
+ i32.const 0
+ i32.const 1
+ i32.const 2
+ ref.null extern
+ br_on_null 0
+ drop
+ )
+)`);
+
+// Test the branch takes the correct path and results are passed correctly
+let {isNull} = wasmEvalText(`(module
+ (func (export "isNull") (param externref) (result i32)
+ i32.const 1
+ local.get 0
+ br_on_null 0
+ drop
+ drop
+ i32.const 0
+ )
+)`).exports;
+
+assertEq(isNull(null), 1, `null is null`);
+for (let val of WasmNonNullExternrefValues) {
+ assertEq(isNull(val), 0, `${typeof(val)} is not null`);
+}
diff --git a/js/src/jit-test/tests/wasm/function-references/call_ref.js b/js/src/jit-test/tests/wasm/function-references/call_ref.js
new file mode 100644
index 0000000000..205f5329a9
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/call_ref.js
@@ -0,0 +1,100 @@
+// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
+
+let { plusOne } = wasmEvalText(`(module
+ (; forward declaration so that ref.func works ;)
+ (elem declare $plusOneRef)
+ (type $t (func (param i32) (result i32)))
+
+ (func $plusOneRef (param i32) (result i32)
+ (i32.add
+ local.get 0
+ i32.const 1)
+ )
+
+ (func (export "plusOne") (param i32) (result i32)
+ local.get 0
+ ref.func $plusOneRef
+ call_ref $t
+ )
+)`).exports;
+
+assertEq(plusOne(3), 4);
+
+// pass non-funcref type
+wasmFailValidateText(`(module
+ (type $t (func (param i32)))
+ (func (param $a i32)
+ local.get $a
+ call_ref $t
+ )
+)`, /type mismatch: expression has type i32 but expected \(ref null \d+\)/);
+
+wasmFailValidateText(`(module
+ (type $t (func (param externref)))
+ (func (param $a (ref extern))
+ local.get $a
+ call_ref $t
+ )
+)`, /type mismatch: expression has type \(ref extern\) but expected \(ref null \d+\)/);
+
+// pass (non-subtype of) funcref
+wasmFailValidateText(`(module
+ (type $t (func (param i32) (result i32)))
+ (func (param funcref)
+ local.get 0
+ call_ref $t
+ )
+)`, /type mismatch: expression has type funcref but expected \(ref null \d+\)/);
+
+// signature mismatch
+wasmFailValidateText(`(module
+ (type $t (func (param i32) (result i32)))
+ (elem declare $plusOneRef)
+ (func $plusOneRef (param f32) (result f32)
+ (f32.add
+ local.get 0
+ f32.const 1.0)
+ )
+
+ (func (export "plusOne") (param i32) (result i32)
+ local.get 0
+ ref.func $plusOneRef
+ call_ref $t
+ )
+)`, /type mismatch: expression has type \(ref \d+\) but expected \(ref null \d+\)/);
+
+// Cross-instance calls
+let { loadInt } = wasmEvalText(`(module
+ (memory 1 1)
+ (data (i32.const 0) "\\04\\00\\00\\00")
+ (func (export "loadInt") (result i32)
+ i32.const 0
+ i32.load offset=0
+ )
+)`).exports;
+
+let { callLoadInt } = wasmEvalText(`(module
+ (type $t (func (result i32)))
+ (elem declare 0)
+ (import "" "loadInt" (func (result i32)))
+ (func (export "callLoadInt") (result i32)
+ ref.func 0
+ call_ref $t
+ )
+)`, {"": { loadInt, }}).exports;
+
+assertEq(loadInt(), 4);
+assertEq(callLoadInt(), 4);
+
+// Null call.
+assertErrorMessage(function() {
+ let { nullCall } = wasmEvalText(`(module
+ (type $t (func (param i32) (result i32)))
+ (func (export "nullCall") (param i32) (result i32)
+ local.get 0
+ ref.null $t
+ call_ref $t
+ )
+ )`).exports;
+ nullCall(3);
+}, WebAssembly.RuntimeError, /dereferencing null pointer/);
diff --git a/js/src/jit-test/tests/wasm/function-references/directives.txt b/js/src/jit-test/tests/wasm/function-references/directives.txt
new file mode 100644
index 0000000000..f7d2135421
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/directives.txt
@@ -0,0 +1 @@
+|jit-test| test-also=--wasm-compiler=optimizing --wasm-function-references; test-also=--wasm-compiler=baseline --wasm-function-references; include:wasm.js
diff --git a/js/src/jit-test/tests/wasm/function-references/disabled.js b/js/src/jit-test/tests/wasm/function-references/disabled.js
new file mode 100644
index 0000000000..70a66a937d
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/disabled.js
@@ -0,0 +1,17 @@
+// |jit-test| skip-if: wasmFunctionReferencesEnabled()
+
+const { CompileError, validate } = WebAssembly;
+
+const UNRECOGNIZED_OPCODE_OR_BAD_TYPE = /unrecognized opcode|bad type|\(ref T\) types not enabled/;
+
+let simpleTests = [
+ `(module (func (param (ref 0)) (unreachable)))`,
+];
+
+// Test that use of function-references fails when function-references is disabled.
+
+for (let src of simpleTests) {
+ let bin = wasmTextToBinary(src);
+ assertEq(validate(bin), false);
+ wasmCompilationShouldFail(bin, UNRECOGNIZED_OPCODE_OR_BAD_TYPE);
+}
diff --git a/js/src/jit-test/tests/wasm/function-references/nnl-test.js b/js/src/jit-test/tests/wasm/function-references/nnl-test.js
new file mode 100644
index 0000000000..0036a7b30d
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/nnl-test.js
@@ -0,0 +1,172 @@
+// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
+
+// Generates combinations of different block types and operations for
+// non-defaultable locals (local.set / .tee / .get).
+// See the function references specification on the updated algorithm
+// for validating non-null references in locals.
+
+const KINDS = [
+ "block",
+ "loop",
+ "try",
+ "catch",
+ "if",
+ "else",
+]
+const INITIALIZED = [
+ "nowhere",
+ "outer",
+ "inner",
+ "outer-tee",
+ "inner-tee",
+];
+const USED = [
+ "outer",
+ "inner",
+ "after-inner",
+ "after-outer",
+];
+
+function generateBlock(kind, contents) {
+ switch (kind) {
+ case "block": {
+ return `block\n${contents}end\n`
+ }
+ case "loop": {
+ return `loop\n${contents}end\n`
+ }
+ case "try": {
+ return `try\n${contents}end\n`
+ }
+ case "catch": {
+ return `try\ncatch_all\n${contents}end\n`
+ }
+ case "if": {
+ return `i32.const 0\nif\n${contents}end\n`
+ }
+ case "else": {
+ return `i32.const 0\nif\nelse\n${contents}end\n`
+ }
+ }
+}
+
+// Generate a variation of the module below:
+//
+// (func
+// (block
+// $outer
+// (block
+// $inner
+// )
+// $after-inner
+// )
+// $after-outer
+// )
+//
+// Where a local is used and initialized at different points depending on the
+// parameters. The block kinds of the inner and outer block may also be
+// customized.
+function generateModule(outerBlockKind, innerBlockKind, initializedWhere, usedWhere) {
+ const INITIALIZE_STMT = '(local.set 0 ref.func 0)\n';
+ const INITIALIZE_STMT2 = '(drop (local.tee 0 ref.func 0))\n';
+ const USE_STMT = '(drop local.get 0)\n';
+
+ // inner block
+ let innerBlockContents = '';
+ if (initializedWhere === 'inner') {
+ innerBlockContents += INITIALIZE_STMT;
+ } else if (initializedWhere === 'inner-tee') {
+ innerBlockContents += INITIALIZE_STMT2;
+ }
+ if (usedWhere === 'inner') {
+ innerBlockContents += USE_STMT;
+ }
+ let innerBlock = generateBlock(innerBlockKind, innerBlockContents);
+
+ // outer block
+ let outerBlockContents = '';
+ if (initializedWhere === 'outer') {
+ outerBlockContents += INITIALIZE_STMT;
+ } else if (initializedWhere === 'outer-tee') {
+ outerBlockContents += INITIALIZE_STMT2;
+ }
+ if (usedWhere === 'outer') {
+ outerBlockContents += USE_STMT;
+ }
+ outerBlockContents += innerBlock;
+ if (usedWhere === 'after-inner') {
+ outerBlockContents += USE_STMT;
+ }
+ let outerBlock = generateBlock(outerBlockKind, outerBlockContents);
+
+ // after outer block
+ let afterOuterBlock = '';
+ if (usedWhere === 'after-outer') {
+ afterOuterBlock += USE_STMT;
+ }
+
+ return `(module
+ (type $t (func))
+ (func (export "test")
+ (local (ref $t))
+${outerBlock}${afterOuterBlock} )
+)`;
+}
+
+const LOGGING = false;
+
+for (let outer of KINDS) {
+ for (let inner of KINDS) {
+ for (let initialized of INITIALIZED) {
+ for (let used of USED) {
+ let text = generateModule(outer, inner, initialized, used);
+
+ let expectPass;
+ switch (initialized) {
+ case "outer":
+ case "outer-tee": {
+ // Defining the local in the outer block makes it valid
+ // in the outer block, the inner block, and after the
+ // inner block
+ expectPass = used !== "after-outer";
+ break;
+ }
+ case "inner":
+ case "inner-tee": {
+ // Defining the local in the inner block makes it valid
+ // in the inner block
+ //
+ // NOTE: an extension to typing could make this valid
+ // after the inner block in some cases
+ expectPass = used === "inner";
+ break;
+ }
+ case "nowhere": {
+ // Not defining the local makes it always invalid to
+ // use
+ expectPass = false;
+ break;
+ }
+ }
+
+ if (LOGGING) {
+ console.log();
+ console.log(`TEST: outer=${outer}, inner=${inner}, initialized=${initialized}, used=${used}`);
+ console.log(expectPass ? "EXPECT PASS" : "EXPECT FAIL");
+ console.log(text);
+ }
+
+ let binary = wasmTextToBinary(text);
+ assertEq(WebAssembly.validate(binary), expectPass);
+ if (!expectPass) {
+ // Check if the error message is right.
+ try {
+ new WebAssembly.Module(binary);
+ } catch (ex) {
+ assertEq(true, /local\.get read from unset local/.test(ex.message));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/wasm/function-references/non-nullable-table.js b/js/src/jit-test/tests/wasm/function-references/non-nullable-table.js
new file mode 100644
index 0000000000..97ab04713c
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/non-nullable-table.js
@@ -0,0 +1,79 @@
+// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
+
+// non-null table initialization
+var { get1, get2, get3, get4 } = wasmEvalText(`(module
+ (type $dummy (func))
+ (func $dummy)
+
+ (table $t1 10 funcref)
+ (table $t2 10 funcref (ref.func $dummy))
+ (table $t3 10 (ref $dummy) (ref.func $dummy))
+ (table $t4 10 (ref func) (ref.func $dummy))
+
+ (func (export "get1") (result funcref) (table.get $t1 (i32.const 1)))
+ (func (export "get2") (result funcref) (table.get $t2 (i32.const 4)))
+ (func (export "get3") (result funcref) (table.get $t3 (i32.const 7)))
+ (func (export "get4") (result funcref) (table.get $t4 (i32.const 5)))
+)`).exports;
+assertEq(get1(), null);
+assertEq(get2() != null, true);
+assertEq(get3() != null, true);
+assertEq(get4() != null, true);
+
+const sampleWasmFunction = get2();
+sampleWasmFunction();
+
+// Invalid initializers
+for (let i of [
+ `(table $t1 10 (ref $dummy) (ref.func $dummy1))`,
+ `(table $t2 5 10 (ref func))`,
+ `(table $t3 10 10 (ref func) (ref.null $dummy1))`,
+ `(table $t4 10 (ref $dummy))`,
+ '(table $t5 1 (ref $dummy) (ref.null $dummy))',
+]) {
+ wasmFailValidateText(`(module
+ (type $dummy (func))
+ (type $dummy1 (func (param i32)))
+ (func $dummy1 (param i32))
+
+ ${i}
+ )`, /(type mismatch|table with non-nullable references requires initializer)/);
+}
+
+var t1 = new WebAssembly.Table({initial: 10, element: {ref: 'func', nullable: false }}, sampleWasmFunction);
+assertEq(t1.get(2) != null, true);
+assertThrows(() => {
+ new WebAssembly.Table({initial: 10, element: {ref: 'func', nullable: false }});
+});
+assertThrows(() => {
+ new WebAssembly.Table({initial: 10, element: {ref: 'func', nullable: false }}, null);
+});
+
+var t2 = new WebAssembly.Table({initial: 6, maximum: 20, element: {ref: 'extern', nullable: false }}, {foo: "bar"});
+assertEq(t2.get(1).foo, "bar");
+assertThrows(() => { t2.get(7) });
+assertThrows(() => { t2.grow(9, null) });
+t2.grow(8, {t: "test"});
+assertEq(t2.get(3).foo, "bar");
+assertEq(t2.get(7).t, "test");
+assertThrows(() => {
+ new WebAssembly.Table({initial: 10, element: {ref: 'extern', nullable: false }}, null);
+});
+
+// Fail because tables come before globals in the binary format, so tables
+// cannot refer to globals.
+wasmFailValidateText(`(module
+ (global $g1 externref)
+ (table 10 externref (global.get $g1))
+)`, /global.get index out of range/);
+
+function assertThrows(f) {
+ var ok = false;
+ try {
+ f();
+ } catch (exc) {
+ ok = true;
+ }
+ if (!ok)
+ throw new TypeError("Assertion failed: " + f + " did not throw as expected");
+}
diff --git a/js/src/jit-test/tests/wasm/function-references/non-nullable.js b/js/src/jit-test/tests/wasm/function-references/non-nullable.js
new file mode 100644
index 0000000000..b8fd20fd83
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/non-nullable.js
@@ -0,0 +1,176 @@
+// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
+
+// non-null values are subtype of null values
+wasmValidateText(`(module
+ (func (param $a (ref extern))
+ local.get $a
+ (block (param (ref null extern))
+ drop
+ )
+ )
+)`);
+
+// null values are not subtype of non-null values
+wasmFailValidateText(`(module
+ (func (param $a (ref null extern))
+ local.get $a
+ (block (param (ref extern))
+ drop
+ )
+ )
+)`, /expression has type externref but expected \(ref extern\)/);
+
+// can have non-defaultable local, but not use/get if unset.
+wasmValidateText(`(module
+ (func (local (ref extern)))
+)`);
+wasmFailValidateText(`(module
+ (func (local (ref extern))
+ local.get 0
+ drop
+ )
+)`, /local\.get read from unset local/);
+wasmFailValidateText(`(module
+ (func
+ (local (ref extern))
+ unreachable
+ block
+ local.get 0
+ drop
+ end
+ )
+)`, /local\.get read from unset local/);
+wasmFailValidateText(`(module
+ (func (param funcref) (result funcref) (local (ref func))
+ block
+ local.get 0
+ ref.as_non_null
+ local.set 1
+ end
+ local.get 1
+ )
+)`, /local\.get read from unset local/);
+wasmValidateText(`(module
+ (func (param $r (ref extern))
+ (local $var (ref extern))
+ local.get $r
+ ref.as_non_null
+ local.set $var
+ block block block
+ local.get $var
+ drop
+ end end end
+ )
+ (func
+ (param (ref null func) (ref null func) (ref func))
+ (result funcref)
+ (local (ref func) i32 (ref func) (ref null func))
+ local.get 0
+ ref.as_non_null
+ local.tee 3
+ block
+ local.get 6
+ ref.as_non_null
+ local.set 5
+ end
+ local.get 2
+ drop
+ local.tee 5
+ )
+)`);
+wasmFailValidateText(`(module
+ (elem declare 0)
+ (func
+ (local (ref func))
+ i32.const 0
+ if
+ ref.func 0
+ local.set 0
+ else
+ local.get 0
+ drop
+ end
+ )
+)`, /local\.get read from unset local/);
+wasmValidateText(`(module
+ (elem declare 0)
+ (func (result funcref)
+ (local (ref func) (ref func))
+ i32.const 0
+ if (result funcref)
+ ref.func 0
+ local.set 0
+ local.get 0
+ else
+ ref.func 0
+ local.tee 1
+ local.get 1
+ drop
+ end
+ )
+)`);
+
+// exported funcs can't take null in non-nullable params
+let {a} = wasmEvalText(`(module
+ (func (export "a") (param (ref extern)))
+)`).exports;
+assertErrorMessage(() => a(null), TypeError, /cannot pass null to non-nullable/);
+for (let val of WasmNonNullExternrefValues) {
+ a(val);
+}
+
+// imported funcs can't return null in non-nullable results
+function returnNull() {
+ return null;
+}
+function returnMultiNullReg() {
+ return [null, null];
+}
+function returnMultiNullStack() {
+ return [1, 2, 3, 4, 5, 6, 7, 8, null];
+}
+let {runNull, runMultiNullReg, runMultiNullStack} = wasmEvalText(`(module
+ (func $returnNull (import "" "returnNull") (result (ref extern)))
+ (func $returnMultiNullReg (import "" "returnMultiNullReg") (result (ref extern) (ref extern)))
+ (func $returnMultiNullStack (import "" "returnMultiNullStack") (result (ref extern) (ref extern) (ref extern) (ref extern) (ref extern) (ref extern) (ref extern) (ref extern) (ref extern)))
+ (func (export "runNull")
+ call $returnNull
+ unreachable
+ )
+ (func (export "runMultiNullReg")
+ call $returnMultiNullReg
+ unreachable
+ )
+ (func (export "runMultiNullStack")
+ call $returnMultiNullStack
+ unreachable
+ )
+)`, { "": { returnNull, returnMultiNullReg, returnMultiNullStack } }).exports;
+assertErrorMessage(() => runNull(), TypeError, /cannot pass null to non-nullable/);
+assertErrorMessage(() => runMultiNullReg(), TypeError, /cannot pass null to non-nullable/);
+assertErrorMessage(() => runMultiNullStack(), TypeError, /cannot pass null to non-nullable/);
+
+{
+ // can have non-nullable globals
+ wasmEvalText(`(module
+ (func $f)
+ (elem declare $f)
+ (global (ref func) ref.func $f)
+ )`);
+}
+
+// cannot have non-nullable tables without initializer
+wasmFailValidateText(`(module
+ (table (ref extern) (elem))
+)`, /table with non-nullable references requires initializer/);
+
+// Testing internal wasmLosslessInvoke to pass non-nullable as params and arguments.
+let {t} = wasmEvalText(`(module
+ (func (export "t") (param (ref extern)) (result (ref extern))
+ (local (ref extern))
+ (local.set 1 (local.get 0))
+ (local.get 1)
+ )
+)`).exports;
+const ret = wasmLosslessInvoke(t, {test: 1});
+assertEq(ret.value.test, 1);
diff --git a/js/src/jit-test/tests/wasm/function-references/reftype-parse.js b/js/src/jit-test/tests/wasm/function-references/reftype-parse.js
new file mode 100644
index 0000000000..643f753ec8
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/reftype-parse.js
@@ -0,0 +1,51 @@
+// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
+
+// RefType/ValueType as a simple string
+const t01 = new WebAssembly.Table({element: 'funcref', initial: 3});
+const t02 = new WebAssembly.Table({element: 'externref', initial: 8});
+const g01 = new WebAssembly.Global({value: 'funcref', mutable: true}, null);
+const g02 = new WebAssembly.Global({value: 'externref', mutable: true}, null);
+
+// Specify ToString() equivalents
+const t05 = new WebAssembly.Table({element: {toString() { return 'funcref' },}, initial: 11});
+const t06 = new WebAssembly.Table({element: ['externref'], initial: 7});
+
+assertErrorMessage(
+ () => new WebAssembly.Table({element: 'foo', initial: 1}),
+ TypeError, /bad value type/);
+assertErrorMessage(
+ () => new WebAssembly.Table({element: true, initial: 1}),
+ TypeError, /bad value type/);
+
+// RefType/ValueType can be specified as an {ref: 'func', ...} object
+const t11 = new WebAssembly.Table({element: {ref: 'func', nullable: true}, initial: 3});
+const t12 = new WebAssembly.Table({element: {ref: 'extern', nullable: true}, initial: 3});
+const t13 = new WebAssembly.Table({element: {ref: 'extern', nullable: false}, initial: 3}, {});
+
+assertErrorMessage(
+ () => new WebAssembly.Table({element: {ref: 'func', nullable: false}, initial: 1}, null),
+ TypeError, /cannot pass null to non-nullable WebAssembly reference/);
+assertErrorMessage(
+ () => new WebAssembly.Table({element: {ref: 'extern', nullable: false}, initial: 1}, null),
+ TypeError, /cannot pass null to non-nullable WebAssembly reference/);
+
+assertErrorMessage(
+ () => new WebAssembly.Table({element: {ref: 'bar', nullable: true}, initial: 1}),
+ TypeError, /bad value type/);
+
+const g11 = new WebAssembly.Global({value: {ref: 'func', nullable: true}, mutable: true});
+const g12 = new WebAssembly.Global({value: {ref: 'extern', nullable: true}, mutable: true});
+const g13 = new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}, {});
+const g14 = new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true});
+const g15 = new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}, void 0);
+
+assertErrorMessage(
+ () => new WebAssembly.Global({value: {ref: 'func', nullable: false}, mutable: true}),
+ TypeError, /cannot pass null to non-nullable WebAssembly reference/);
+assertErrorMessage(
+ () => new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}, null),
+ TypeError, /cannot pass null to non-nullable WebAssembly reference/);
+
+assertErrorMessage(
+ () => new WebAssembly.Global({value: {ref: 'bar', nullable: true}, mutable: true}),
+ TypeError, /bad value type/);