// META: global=window,worker,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}`); // According to the spec discussion, the current `WebAssembly.Exception` does not have `[[ErrorData]]` semantically. // - https://github.com/WebAssembly/spec/issues/1914 // - https://webassembly.github.io/spec/js-api/#exceptions // - https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-properties-of-error-instances assert_false(Error.isError(e), `Error.isError(WebAssembly.Exception) should be false due to lacking [[ErrorData]]`); } } promise_test(async () => { const kSig_v_r = makeSig([kWasmExternRef], []); const builder = new WasmModuleBuilder(); const tagIndex = builder.addTag(kSig_v_r); builder.addFunction("throw_param", kSig_v_r) .addBody([ kExprLocalGet, 0, kExprThrow, tagIndex, ]) .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 tagIndex = builder.addTag(kSig_v_a); builder.addFunction("throw_null", kSig_v_v) .addBody([ kExprRefNull, kAnyFuncCode, kExprThrow, tagIndex, ]) .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 tagIndex = builder.addTag(kSig_v_i); builder.addFunction("throw_int", kSig_v_v) .addBody([ ...wasmI32Const(7), kExprThrow, tagIndex, ]) .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 tagIndex= builder.addTag(kSig_v_r); builder.addFunction("catch_exception", kSig_r_v) .addBody([ kExprTry, kWasmStmt, kExprCallFunction, fnIndex, kExprCatch, tagIndex, 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([ kExprTry, kWasmStmt, kExprCallFunction, fnIndex, kExprCatchAll, kExprRethrow, 0x00, 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");