| // META: global=window,dedicatedworker,jsshell,shadowrealm | |
| // META: script=/wasm/jsapi/wasm-module-builder.js | |
| function assert_throws_wasm(fn, message) { | |
| try { | |
| fn(); | |
| assert_not_reached(`expected to throw with ${message}`); | |
| } catch (e) { | |
| assert_true(e instanceof WebAssembly.Exception, `Error should be a WebAssembly.Exception with ${message}`); | |
| } | |
| } | |
| promise_test(async () => { | |
| const kSig_v_r = makeSig([kWasmExternRef], []); | |
| const builder = new WasmModuleBuilder(); | |
| const tagIndexExternref = builder.addTag(kSig_v_r); | |
| builder.addFunction("throw_param", kSig_v_r) | |
| .addBody([ | |
| kExprLocalGet, 0, | |
| kExprThrow, tagIndexExternref, | |
| ]) | |
| .exportFunc(); | |
| const buffer = builder.toBuffer(); | |
| const {instance} = await WebAssembly.instantiate(buffer, {}); | |
| const values = [ | |
| undefined, | |
| null, | |
| true, | |
| false, | |
| "test", | |
| Symbol(), | |
| 0, | |
| 1, | |
| 4.2, | |
| NaN, | |
| Infinity, | |
| {}, | |
| () => {}, | |
| ]; | |
| for (const v of values) { | |
| assert_throws_wasm(() => instance.exports.throw_param(v), String(v)); | |
| } | |
| }, "Wasm function throws argument"); | |
| promise_test(async () => { | |
| const builder = new WasmModuleBuilder(); | |
| const tagIndexAnyref = builder.addTag(kSig_v_a); | |
| builder.addFunction("throw_null", kSig_v_v) | |
| .addBody([ | |
| kExprRefNull, kAnyFuncCode, | |
| kExprThrow, tagIndexAnyref, | |
| ]) | |
| .exportFunc(); | |
| const buffer = builder.toBuffer(); | |
| const {instance} = await WebAssembly.instantiate(buffer, {}); | |
| assert_throws_wasm(() => instance.exports.throw_null()); | |
| }, "Wasm function throws null"); | |
| promise_test(async () => { | |
| const builder = new WasmModuleBuilder(); | |
| const tagIndexI32 = builder.addTag(kSig_v_i); | |
| builder.addFunction("throw_int", kSig_v_v) | |
| .addBody([ | |
| ...wasmI32Const(7), | |
| kExprThrow, tagIndexI32, | |
| ]) | |
| .exportFunc(); | |
| const buffer = builder.toBuffer(); | |
| const {instance} = await WebAssembly.instantiate(buffer, {}); | |
| assert_throws_wasm(() => instance.exports.throw_int()); | |
| }, "Wasm function throws integer"); | |
| promise_test(async () => { | |
| const builder = new WasmModuleBuilder(); | |
| const fnIndex = builder.addImport("module", "fn", kSig_v_v); | |
| const tagIndexExternref = builder.addTag(kSig_v_r); | |
| builder.addFunction("catch_exception", kSig_r_v) | |
| .addBody([ | |
| kExprBlock, kWasmVoid, | |
| kExprBlock, kExternRefCode, | |
| kExprTryTable, kWasmVoid, 1, | |
| kCatchNoRef, tagIndexExternref, 0, | |
| kExprCallFunction, fnIndex, | |
| kExprEnd, | |
| kExprBr, 1, | |
| kExprEnd, | |
| kExprReturn, | |
| kExprEnd, | |
| kExprRefNull, kExternRefCode, | |
| ]) | |
| .exportFunc(); | |
| const buffer = builder.toBuffer(); | |
| const error = new Error(); | |
| const fn = () => { throw error }; | |
| const {instance} = await WebAssembly.instantiate(buffer, { | |
| module: { fn } | |
| }); | |
| assert_throws_exactly(error, () => instance.exports.catch_exception()); | |
| }, "Imported JS function throws"); | |
| promise_test(async () => { | |
| const builder = new WasmModuleBuilder(); | |
| const fnIndex = builder.addImport("module", "fn", kSig_v_v); | |
| builder.addFunction("catch_and_rethrow", kSig_r_v) | |
| .addBody([ | |
| kExprBlock, kWasmVoid, | |
| kExprBlock, kExnRefCode, | |
| kExprTryTable, kWasmVoid, 1, | |
| kCatchAllRef, 0, | |
| kExprCallFunction, fnIndex, | |
| kExprEnd, | |
| kExprBr, 1, | |
| kExprEnd, | |
| kExprThrowRef, | |
| kExprEnd, | |
| kExprRefNull, kExternRefCode, | |
| ]) | |
| .exportFunc(); | |
| const buffer = builder.toBuffer(); | |
| const error = new Error(); | |
| const fn = () => { throw error }; | |
| const {instance} = await WebAssembly.instantiate(buffer, { | |
| module: { fn } | |
| }); | |
| assert_throws_exactly(error, () => instance.exports.catch_and_rethrow()); | |
| }, "Imported JS function throws, Wasm catches and rethrows"); | |
| promise_test(async () => { | |
| const builder = new WasmModuleBuilder(); | |
| const fnIndex = builder.addImport("module", "fn", kSig_v_v); | |
| const tagI32 = new WebAssembly.Tag({ parameters: ["i32"] }); | |
| const tagIndexI32 = builder.addImportedTag("module", "tagI32", kSig_v_i); | |
| const exn = new WebAssembly.Exception(tagI32, [42]); | |
| const kSig_ie_v = makeSig([], [kWasmI32, kExnRefCode]); | |
| const sig_ie_v = builder.addType(kSig_ie_v); | |
| builder.addFunction("all_catch_clauses", kSig_i_v) | |
| .addBody([ | |
| kExprBlock, kWasmVoid, | |
| kExprBlock, kExnRefCode, | |
| kExprBlock, sig_ie_v, | |
| kExprBlock, kWasmVoid, | |
| kExprBlock, kWasmI32, | |
| kExprTryTable, kWasmVoid, 4, | |
| kCatchNoRef, tagIndexI32, 0, | |
| kCatchAllNoRef, 1, | |
| kCatchRef, tagIndexI32, 2, | |
| kCatchAllRef, 3, | |
| kExprCallFunction, fnIndex, | |
| kExprEnd, | |
| kExprBr, 4, | |
| kExprEnd, | |
| kExprReturn, | |
| kExprEnd, | |
| kExprBr, 2, | |
| kExprEnd, | |
| kExprDrop, | |
| kExprDrop, | |
| kExprBr, 1, | |
| kExprEnd, | |
| kExprDrop, | |
| kExprEnd, | |
| kExprI32Const, 0, | |
| ]) | |
| .exportFunc(); | |
| const buffer = builder.toBuffer(); | |
| const fn = () => { | |
| throw exn; | |
| }; | |
| const {instance} = await WebAssembly.instantiate(buffer, { | |
| module: { fn, tagI32: tagI32 } | |
| }); | |
| const result = instance.exports.all_catch_clauses(); | |
| assert_equals(result, 42); | |
| }, "try-table uses all four kinds of catch clauses, one of which catches an exception"); |