Source code
Revision control
Copy as Markdown
Other Tools
// 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()
);