summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/exceptions
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/jit-test/tests/wasm/exceptions
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/wasm/exceptions')
-rw-r--r--js/src/jit-test/tests/wasm/exceptions/directives.txt1
-rw-r--r--js/src/jit-test/tests/wasm/exceptions/events.js160
-rw-r--r--js/src/jit-test/tests/wasm/exceptions/import-export.js111
-rw-r--r--js/src/jit-test/tests/wasm/exceptions/validation.js341
4 files changed, 613 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/exceptions/directives.txt b/js/src/jit-test/tests/wasm/exceptions/directives.txt
new file mode 100644
index 0000000000..c6a49cb4cc
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/exceptions/directives.txt
@@ -0,0 +1 @@
+|jit-test| --wasm-exceptions; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--test-wasm-await-tier2; include:wasm.js; skip-if: !wasmExceptionsEnabled()
diff --git a/js/src/jit-test/tests/wasm/exceptions/events.js b/js/src/jit-test/tests/wasm/exceptions/events.js
new file mode 100644
index 0000000000..bc8a37531c
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/exceptions/events.js
@@ -0,0 +1,160 @@
+// Tests for event section support
+
+load(libdir + "wasm-binary.js");
+
+function wasmEval(code, imports) {
+ new WebAssembly.Instance(new WebAssembly.Module(code), imports).exports;
+}
+
+function wasmError(code, errorType, regexp) {
+ assertErrorMessage(() => wasmEval(code, {}), errorType, regexp);
+}
+
+const emptyType = { args: [], ret: VoidCode };
+const badExnType = { args: [], ret: I32Code };
+
+wasmEvalText(`
+ (module
+ (type (func (param i32)))
+ (event $exn (type 0)))
+`);
+
+wasmError(
+ moduleWithSections([
+ sigSection([emptyType]),
+ memorySection(0),
+ { name: eventId, body: [] },
+ ]),
+ WebAssembly.CompileError,
+ /expected number of events/
+);
+
+wasmError(
+ moduleWithSections([
+ sigSection([emptyType]),
+ memorySection(0),
+ { name: eventId, body: [1, 1] },
+ ]),
+ WebAssembly.CompileError,
+ /illegal event kind/
+);
+
+wasmError(
+ moduleWithSections([
+ sigSection([emptyType]),
+ memorySection(0),
+ { name: eventId, body: [1, 0] },
+ ]),
+ WebAssembly.CompileError,
+ /expected function index in event/
+);
+
+wasmEval(
+ moduleWithSections([
+ sigSection([emptyType]),
+ memorySection(0),
+ eventSection([{ type: 0 }]),
+ ])
+);
+
+wasmError(
+ moduleWithSections([
+ sigSection([badExnType]),
+ memorySection(0),
+ eventSection([{ type: 0 }]),
+ ]),
+ WebAssembly.CompileError,
+ /exception function types must not return anything/
+);
+
+wasmError(
+ moduleWithSections([
+ sigSection([emptyType]),
+ memorySection(0),
+ eventSection([{ type: 1 }]),
+ ]),
+ WebAssembly.CompileError,
+ /function type index in event out of bounds/
+);
+
+wasmError(
+ moduleWithSections([
+ sigSection([emptyType]),
+ eventSection([{ type: 0 }]),
+ memorySection(0),
+ ]),
+ WebAssembly.CompileError,
+ /expected custom section/
+);
+
+(() => {
+ const body = [1];
+ body.push(...string("mod"));
+ body.push(...string("exn"));
+ body.push(...varU32(EventCode));
+
+ wasmError(
+ moduleWithSections([
+ sigSection([emptyType]),
+ { name: importId, body: body },
+ ]),
+ WebAssembly.CompileError,
+ /expected event kind/
+ );
+
+ body.push(...varU32(0));
+ wasmError(
+ moduleWithSections([
+ sigSection([emptyType]),
+ { name: importId, body: body },
+ ]),
+ WebAssembly.CompileError,
+ /expected function index in event/
+ );
+
+ body.push(...varU32(1));
+ wasmError(
+ moduleWithSections([
+ sigSection([emptyType]),
+ { name: importId, body: body },
+ ]),
+ WebAssembly.CompileError,
+ /function type index in event out of bounds/
+ );
+})();
+
+wasmEval(
+ moduleWithSections([
+ sigSection([emptyType]),
+ memorySection(0),
+ eventSection([{ type: 0 }]),
+ exportSection([{ eventIndex: 0, name: "exn" }]),
+ ])
+);
+
+wasmError(
+ moduleWithSections([
+ sigSection([emptyType]),
+ memorySection(0),
+ eventSection([{ type: 0 }]),
+ exportSection([{ eventIndex: 1, name: "exn" }]),
+ ]),
+ WebAssembly.CompileError,
+ /exported event index out of bounds/
+);
+
+(() => {
+ const body = [1];
+ body.push(...string("exn"));
+ body.push(...varU32(EventCode));
+ wasmError(
+ moduleWithSections([
+ sigSection([emptyType]),
+ memorySection(0),
+ eventSection([{ type: 0 }]),
+ { name: exportId, body: body },
+ ]),
+ WebAssembly.CompileError,
+ /expected event index/
+ );
+})();
diff --git a/js/src/jit-test/tests/wasm/exceptions/import-export.js b/js/src/jit-test/tests/wasm/exceptions/import-export.js
new file mode 100644
index 0000000000..58403db894
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/exceptions/import-export.js
@@ -0,0 +1,111 @@
+// Tests for Wasm exception import and export.
+
+// The WebAssembly.Exception constructor cannot be called for now until the
+// JS API specifies the behavior.
+function testException() {
+ assertErrorMessage(
+ () => new WebAssembly.Exception(),
+ WebAssembly.RuntimeError,
+ /cannot call WebAssembly.Exception/
+ );
+}
+
+function testImports() {
+ var mod = `
+ (module
+ (type (func (param i32 i32)))
+ (import "m" "exn" (event (type 0))))
+ `;
+
+ assertErrorMessage(
+ () => wasmEvalText(mod, { m: { exn: "not an exception" } }),
+ WebAssembly.LinkError,
+ /import object field 'exn' is not a Exception/
+ );
+}
+
+function testExports() {
+ var exports1 = wasmEvalText(`
+ (module (type (func)) (event (export "exn") (type 0)))
+ `).exports;
+
+ assertEq(typeof exports1.exn, "object");
+ assertEq(exports1.exn instanceof WebAssembly.Exception, true);
+
+ var exports2 = wasmEvalText(`
+ (module
+ (type (func (param i32 i32)))
+ (event (export "exn") (type 0)))
+ `).exports;
+
+ assertEq(typeof exports2.exn, "object");
+ assertEq(exports2.exn instanceof WebAssembly.Exception, true);
+}
+
+function testImportExport() {
+ var exports = wasmEvalText(`
+ (module
+ (type (func (param i32)))
+ (event (export "exn") (type 0)))
+ `).exports;
+
+ wasmEvalText(
+ `
+ (module
+ (type (func (param i32)))
+ (import "m" "exn" (event (type 0))))
+ `,
+ { m: exports }
+ );
+
+ assertErrorMessage(
+ () => {
+ wasmEvalText(
+ `
+ (module
+ (type (func (param)))
+ (import "m" "exn" (event (type 0))))
+ `,
+ { m: exports }
+ );
+ },
+ WebAssembly.LinkError,
+ /imported exception 'm.exn' signature mismatch/
+ );
+}
+
+// Test imports/exports descriptions.
+function testDescriptions() {
+ const imports = WebAssembly.Module.imports(
+ new WebAssembly.Module(
+ wasmTextToBinary(`
+ (module $m
+ (type (func))
+ (import "m" "e" (event (type 0))))
+ `)
+ )
+ );
+
+ const exports = WebAssembly.Module.exports(
+ new WebAssembly.Module(
+ wasmTextToBinary(`
+ (module
+ (type (func))
+ (event (export "e") (type 0)))
+ `)
+ )
+ );
+
+ assertEq(imports[0].module, "m");
+ assertEq(imports[0].name, "e");
+ assertEq(imports[0].kind, "event");
+
+ assertEq(exports[0].name, "e");
+ assertEq(exports[0].kind, "event");
+}
+
+testException();
+testImports();
+testExports();
+testImportExport();
+testDescriptions();
diff --git a/js/src/jit-test/tests/wasm/exceptions/validation.js b/js/src/jit-test/tests/wasm/exceptions/validation.js
new file mode 100644
index 0000000000..05d3528a5e
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/exceptions/validation.js
@@ -0,0 +1,341 @@
+// Test wasm type validation for exception handling instructions.
+
+load(libdir + "wasm-binary.js");
+
+function wasmValid(mod) {
+ assertEq(WebAssembly.validate(mod), true);
+}
+
+// Note: the pattern variable is ignored until compilation is implemented
+// for exception instructions, which allows checking error messages.
+function wasmInvalid(mod, pattern) {
+ assertEq(WebAssembly.validate(mod), false);
+}
+
+const emptyType = { args: [], ret: VoidCode };
+const i32Type = { args: [I32Code], ret: VoidCode };
+const toi32Type = { args: [], ret: I32Code };
+const i32Toi32Type = { args: [I32Code], ret: I32Code };
+const i32Toi64Type = { args: [I32Code], ret: I64Code };
+const i32i32Toi32Type = { args: [I32Code, I32Code], ret: I32Code };
+
+function testValidateDecode() {
+ // Try blocks must have a block type code.
+ wasmInvalid(
+ moduleWithSections([
+ sigSection([emptyType]),
+ declSection([0]),
+ eventSection([{ type: 0 }]),
+ bodySection([
+ funcBody({
+ locals: [],
+ body: [
+ TryCode,
+ // Missing type code.
+ I32ConstCode,
+ 0x01,
+ CatchCode,
+ 0x00,
+ EndCode,
+ DropCode,
+ ReturnCode,
+ ],
+ }),
+ ]),
+ ]),
+ /bad type/
+ );
+
+ // Catch must have an event index.
+ wasmInvalid(
+ moduleWithSections([
+ sigSection([emptyType]),
+ declSection([0]),
+ eventSection([{ type: 0 }]),
+ bodySection([
+ funcBody(
+ {
+ locals: [],
+ body: [
+ TryCode,
+ I32Code,
+ I32ConstCode,
+ 0x01,
+ CatchCode,
+ // Index missing.
+ ],
+ },
+ (withEndCode = false)
+ ),
+ ]),
+ ]),
+ /expected event index/
+ );
+
+ // Try blocks must have a second branch after the main body.
+ wasmInvalid(
+ moduleWithSections([
+ sigSection([emptyType]),
+ declSection([0]),
+ eventSection([{ type: 0 }]),
+ bodySection([
+ funcBody({
+ locals: [],
+ body: [
+ TryCode,
+ I32Code,
+ I32ConstCode,
+ 0x01,
+ // Missing instruction here.
+ EndCode,
+ DropCode,
+ ReturnCode,
+ ],
+ }),
+ ]),
+ ]),
+ /try without catch or unwind not allowed/
+ );
+}
+
+function testValidateThrow() {
+ valid = `(module
+ (type (func (param i32)))
+ (func $exn-zero
+ i32.const 0
+ throw $exn1)
+ (event $exn1 (type 0)))`;
+
+ invalid0 = `(module
+ (type (func (param i32)))
+ (func $exn-zero
+ throw $exn1)
+ (event $exn1 (type 0)))`;
+ error0 = /popping value from empty stack/;
+
+ invalid1 = `(module
+ (type (func (param i32)))
+ (func $exn-zero
+ i64.const 0
+ throw $exn1)
+ (event $exn1 (type 0)))`;
+ error1 = /expression has type i64 but expected i32/;
+
+ invalid2 = `(module
+ (type (func (param i32)))
+ (func $exn-zero
+ i32.const 0
+ throw 1)
+ (event $exn1 (type 0)))`;
+ error2 = /event index out of range/;
+
+ wasmValidateText(valid);
+ wasmFailValidateText(invalid0, error0);
+ wasmFailValidateText(invalid1, error1);
+ wasmFailValidateText(invalid2, error2);
+}
+
+function testValidateTryCatch() {
+ function mod_with(fbody) {
+ return moduleWithSections([
+ sigSection([emptyType, i32Type, i32i32Toi32Type]),
+ declSection([0]),
+ eventSection([{ type: 0 }, { type: 1 }]),
+ bodySection([
+ funcBody({
+ locals: [],
+ body: fbody,
+ }),
+ ]),
+ ]);
+ }
+
+ const body1 = [
+ // try (result i32)
+ TryCode,
+ I32Code,
+ // (i32.const 1)
+ I32ConstCode,
+ varU32(1),
+ // catch 1
+ CatchCode,
+ varU32(1),
+ ];
+
+ const valid1 = mod_with(body1.concat([EndCode, DropCode, ReturnCode]));
+ const invalid1 = mod_with(
+ body1.concat([I32ConstCode, varU32(2), EndCode, DropCode, ReturnCode])
+ );
+
+ const valid2 = mod_with([
+ // (i32.const 0) (i32.const 0)
+ I32ConstCode,
+ varU32(0),
+ I32ConstCode,
+ varU32(0),
+ // try (param i32 i32) (result i32) drop drop (i32.const 1)
+ TryCode,
+ varS32(2),
+ DropCode,
+ DropCode,
+ I32ConstCode,
+ varU32(1),
+ // catch 0 (i32.const 2) end drop return
+ CatchCode,
+ varU32(0),
+ I32ConstCode,
+ varU32(2),
+ EndCode,
+ DropCode,
+ ReturnCode,
+ ]);
+
+ wasmValid(valid1);
+ wasmInvalid(invalid1, /unused values not explicitly dropped/);
+ wasmValid(valid2);
+}
+
+function testValidateCatch() {
+ wasmInvalid(
+ moduleWithSections([
+ sigSection([emptyType]),
+ declSection([0]),
+ bodySection([
+ funcBody({
+ locals: [],
+ body: [TryCode, VoidCode, CatchCode, varU32(0), EndCode],
+ }),
+ ]),
+ ]),
+ /event index out of range/
+ );
+}
+
+function testValidateExnPayload() {
+ valid0 = moduleWithSections([
+ sigSection([i32Type, i32Toi32Type]),
+ declSection([1]),
+ // (event $exn (param i32))
+ eventSection([{ type: 0 }]),
+ bodySection([
+ // (func (param i32) (result i32) ...
+ funcBody({
+ locals: [],
+ body: [
+ // try (result i32) (local.get 0) (throw $exn) (i32.const 1)
+ TryCode,
+ I32Code,
+ LocalGetCode,
+ varU32(0),
+ ThrowCode,
+ varU32(0),
+ I32ConstCode,
+ varU32(1),
+ // catch $exn (i32.const 1) (i32.add) end
+ CatchCode,
+ varU32(0),
+ I32ConstCode,
+ varU32(1),
+ I32AddCode,
+ EndCode,
+ ],
+ }),
+ ]),
+ ]);
+
+ // This is to ensure the following sentence from the spec overview holds:
+ // > "the operand stack is popped back to the size the operand stack had
+ // > when the try block was entered"
+ valid1 = moduleWithSections([
+ sigSection([i32Type, toi32Type]),
+ declSection([1]),
+ // (event $exn (param i32))
+ eventSection([{ type: 0 }]),
+ bodySection([
+ // (func (result i32) ...
+ funcBody({
+ locals: [],
+ body: [
+ // try (result i32) (i32.const 0) (i32.const 1) (throw $exn) drop
+ TryCode,
+ I32Code,
+ I32ConstCode,
+ varU32(0),
+ I32ConstCode,
+ varU32(1),
+ ThrowCode,
+ varU32(0),
+ DropCode,
+ // catch $exn drop (i32.const 2) end
+ CatchCode,
+ varU32(0),
+ DropCode,
+ I32ConstCode,
+ varU32(2),
+ EndCode,
+ ],
+ }),
+ ]),
+ ]);
+
+ invalid0 = moduleWithSections([
+ sigSection([i32Type, i32Toi64Type]),
+ declSection([1]),
+ // (event $exn (param i32))
+ eventSection([{ type: 0 }]),
+ bodySection([
+ // (func (param i32) (result i64) ...
+ funcBody({
+ locals: [],
+ body: [
+ // try (result i64) (local.get 0) (throw $exn) (i64.const 0)
+ TryCode,
+ I64Code,
+ LocalGetCode,
+ varU32(0),
+ ThrowCode,
+ varU32(0),
+ I64ConstCode,
+ varU32(0),
+ // catch $exn end
+ CatchCode,
+ varU32(0),
+ EndCode,
+ ],
+ }),
+ ]),
+ ]);
+
+ invalid1 = moduleWithSections([
+ // (type (func))
+ sigSection([emptyType]),
+ declSection([0]),
+ // (event $exn (type 0))
+ eventSection([{ type: 0 }]),
+ bodySection([
+ // (func ...
+ funcBody({
+ locals: [],
+ body: [
+ // try catch 1 end
+ TryCode,
+ VoidCode,
+ CatchCode,
+ varU32(1),
+ EndCode,
+ ],
+ }),
+ ]),
+ ]);
+
+ wasmValid(valid0);
+ wasmValid(valid1);
+ wasmInvalid(invalid0, /has type i32 but expected i64/);
+ wasmInvalid(invalid1, /event index out of range/);
+}
+
+testValidateDecode();
+testValidateThrow();
+testValidateTryCatch();
+testValidateCatch();
+testValidateExnPayload();