diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/jit-test/tests/wasm/exceptions | |
parent | Initial commit. (diff) | |
download | firefox-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.txt | 1 | ||||
-rw-r--r-- | js/src/jit-test/tests/wasm/exceptions/events.js | 160 | ||||
-rw-r--r-- | js/src/jit-test/tests/wasm/exceptions/import-export.js | 111 | ||||
-rw-r--r-- | js/src/jit-test/tests/wasm/exceptions/validation.js | 341 |
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(); |