diff options
Diffstat (limited to 'js/src/jit-test/tests/wasm/exceptions/throw-to-js.js')
-rw-r--r-- | js/src/jit-test/tests/wasm/exceptions/throw-to-js.js | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/exceptions/throw-to-js.js b/js/src/jit-test/tests/wasm/exceptions/throw-to-js.js new file mode 100644 index 0000000000..6ddc389cc1 --- /dev/null +++ b/js/src/jit-test/tests/wasm/exceptions/throw-to-js.js @@ -0,0 +1,343 @@ +// Tests for throwing exceptions to JS from Wasm. + +function assertWasmThrowsExn(thunk) { + let thrown = false; + + try { + thunk(); + } catch (exn) { + thrown = true; + assertEq(exn instanceof WebAssembly.Exception, true); + } + + assertEq(thrown, true, "missing exception"); +} + +// Test that handler-less trys don't catch anything. +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (type (func (param))) + (tag $exn (type 0)) + (func (export "f") + try (throw $exn) end))` + ).exports.f() +); + +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (type (func (param))) + (tag $exn (type 0)) + (func $g (throw $exn)) + (func (export "f") + try (call $g) end) +)` + ).exports.f() +); + +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (type (func (param))) + (tag $exn (type 0)) + (func (export "f") + try try (throw $exn) end end))` + ).exports.f() +); + +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (tag $exn (param)) + (func (export "f") + try + try + throw $exn + delegate 0 + end))` + ).exports.f() +); + +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (tag $exn (param)) + (func (export "f") + try + try + throw $exn + delegate 1 + end))` + ).exports.f() +); + +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (tag $exn (param)) + (func (export "f") + block + try + throw $exn + delegate 0 + end))` + ).exports.f() +); + +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (tag $exn (param)) + (func (export "f") + loop + try + throw $exn + delegate 0 + end))` + ).exports.f() +); + +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (tag $exn (param)) + (func (export "f") + (i32.const 1) + if + try + throw $exn + delegate 0 + end))` + ).exports.f() +); + +// Test throwing simple empty exceptions to JS. +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (type (func (param))) + (tag $exn (type 0)) + (func (export "f") + (throw $exn)))` + ).exports.f() +); + +// Test that wasm preserves the values of non-object exceptions that pass +// through it back to JS. +assertThrowsValue( + () => + wasmEvalText( + `(module + (tag $exn) + (import "m" "import" (func $import)) + (func (export "f") + try + (call $import) + catch $exn + ;; this block shouldn't be reached + end))`, + { + m: { + import: () => { + throw 42; + }, + }, + } + ).exports.f(), + 42 +); + +// Like previous test, but using a rethrow instruction instead. +assertThrowsValue( + () => + wasmEvalText( + `(module + (import "m" "import" (func $import)) + (func (export "f") + try + (call $import) + catch_all + (rethrow 0) + end))`, + { + m: { + import: () => { + throw 42; + }, + }, + } + ).exports.f(), + 42 +); + +// Test for throwing to JS and then back to Wasm. +{ + var wasmThrower; + let exports = wasmEvalText( + `(module + (type (func (param i32))) + (tag $exn (type 0)) + (import "m" "import" (func $import (result i32))) + (func (export "thrower") + (i32.const 42) + (throw $exn)) + (func (export "catcher") (result i32) + try (result i32) + (call $import) + catch $exn + end))`, + { + m: { + import: () => { + return wasmThrower(); + }, + }, + } + ).exports; + + wasmThrower = exports.thrower; + assertEq(exports.catcher(), 42); +} + +// Tests for checking the tags of exceptions. +{ + let exports = wasmEvalText( + `(module + (type (func (param i32))) + (tag $exn (export "exn") (type 0)) + (func (export "thrower") + (i32.const 42) + (throw $exn)))` + ).exports; + + let imports = { + store: { + throws: () => { + return exports.thrower(); + }, + exn: exports.exn, + }, + }; + + // This passes the exception tag check and the exception is caught. + assertEq( + wasmEvalText( + `(module + (type (func (param i32))) + (import "store" "throws" (func $thrower (result i32))) + (import "store" "exn" (tag $exn (type 0))) + (func (export "catches") (result i32) + try (result i32) + (call $thrower) + catch $exn + (i32.const 15) + (i32.sub) + end))`, + imports + ).exports.catches(), + 27 + ); + + // This fails the exception tag check, despite the local exception having + // a matching signature. + assertWasmThrowsExn(() => + wasmEvalText( + `(module + (type (func (param i32))) + (import "store" "throws" (func $thrower (result i32))) + (tag $exn (type 0)) + (func (export "catchesFail") (result i32) + try (result i32) + (call $thrower) + catch $exn ;; This should not recognise $exn, thus not unpack 42. + end))`, + imports + ).exports.catchesFail() + ); +} + +// Test that JS finally block executes after a Wasm throw. +assertEq( + (() => { + try { + wasmEvalText( + `(module + (type (func (param))) + (tag $exn (type 0)) + (func (export "f") + (throw $exn)))` + ).exports.f(); + } finally { + return true; + } + return false; + })(), + true +); + +// Test that a wasm trap that passes through JS cannot be caught in Wasm. +{ + let throwTrap = wasmEvalText(`(module (func (export "f") unreachable))`) + .exports.f; + let catcher = wasmEvalText( + `(module + (type (func)) + (tag $exn (type 0)) + (import "m" "f" (func $foreign (param) (result))) + (func (export "f") + try + call $foreign + catch $exn + catch_all + end))`, + { + m: { + // JS frame that goes between the two wasm frames and just rethrows. + f: () => { + try { + throwTrap(); + } catch (e) { + throw e; + } + }, + }, + } + ).exports.f; + + assertErrorMessage( + () => catcher(), + WebAssembly.RuntimeError, + "unreachable executed" + ); +} + +// Test delegate throwing out of function. +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (tag $exn (param)) + (func (export "f") (result i32) + try (result i32) + throw $exn + delegate 0))` + ).exports.f() +); + +assertWasmThrowsExn(() => + wasmEvalText( + `(module + (tag $exn (param)) + (func (export "f") (result i32) + try (result i32) + i32.const 0 + if + i32.const 1 + return + else + throw $exn + end + i32.const 0 + delegate 0))` + ).exports.f() +); |