From 4c93161c9d3fb1e20126ce1903be33308b494ba1 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 4 Jul 2024 12:17:16 -0700 Subject: [PATCH] [js-api] Add tests for WebAssembly.JSTag (#319) This adds tests for `WebAssembly.JSTag`. The tests are adapted from https://github.com/v8/v8/blob/main/test/mjsunit/wasm/exnref-api.js. Confirmed that they run successfully with web test infrastructure in Chrome with `--js-flags=--experimental-wasm-exnref` command line argument. The only thing failing there was the shadowrealm test, which I think we should add a separate expectation files like the existing `***.tentative.any.shadowrealm-expected.txt` in https://github.com/chromium/chromium/tree/main/third_party/blink/web_tests/external/wpt/wasm/jsapi/exception. But given that we don't host these files in this EH repo, I'll just upload the js files. --- test/js-api/exception/jsTag.tentative.any.js | 121 +++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 test/js-api/exception/jsTag.tentative.any.js diff --git a/test/js-api/exception/jsTag.tentative.any.js b/test/js-api/exception/jsTag.tentative.any.js new file mode 100644 index 00000000..c5db5537 --- /dev/null +++ b/test/js-api/exception/jsTag.tentative.any.js @@ -0,0 +1,121 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/wasm-module-builder.js + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Exception(WebAssembly.JSTag, [{}])) +}, "Creating a WebAssembly.Exception with JSTag explicitly is not allowed"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const jsTag = builder.addImportedTag("module", "JSTag", kSig_v_r); + + const throwRefFn = builder.addImport("module", "throw_ref", kSig_r_r); + const sig_r_v = builder.addType(kSig_r_v); + const kSig_re_v = makeSig([], [kExternRefCode, kExnRefCode]); + const sig_re_v = builder.addType(kSig_re_v); + + // Calls throw_ref, catches an exception with 'try_table - catch JSTag', and + // returns it + builder.addFunction("catch_js_tag_and_return", kSig_r_r) + .addBody([ + kExprBlock, sig_r_v, + kExprTryTable, sig_r_v, 1, + kCatchNoRef, jsTag, 0, + kExprLocalGet, 0, + kExprCallFunction, throwRefFn, + kExprEnd, + kExprEnd, + ]) + .exportFunc(); + + // Calls throw_ref, catches an exception with 'try_table - catch_ref JSTag', + // and returns it + builder.addFunction("catch_ref_js_tag_and_return", kSig_r_r) + .addBody([ + kExprBlock, sig_re_v, + kExprTryTable, sig_r_v, 1, + kCatchRef, jsTag, 0, + kExprLocalGet, 0, + kExprCallFunction, throwRefFn, + kExprEnd, + kExprReturn, + kExprEnd, + kExprDrop, + ]) + .exportFunc(); + + // Calls throw_ref, catches an exception with 'try_table - catch_ref JSTag', + // and rethrows it (with throw_ref) + builder.addFunction("catch_ref_js_tag_and_throw_ref", kSig_r_r) + .addBody([ + kExprBlock, sig_re_v, + kExprTryTable, sig_r_v, 1, + kCatchRef, jsTag, 0, + kExprLocalGet, 0, + kExprCallFunction, throwRefFn, + kExprEnd, + kExprReturn, + kExprEnd, + kExprThrowRef, + ]) + .exportFunc(); + + function throw_ref(x) { + throw x; + } + const buffer = builder.toBuffer(); + const {instance} = await WebAssembly.instantiate(buffer, { + module: { throw_ref, JSTag: WebAssembly.JSTag } + }); + + const obj = {}; + const wasmTag = new WebAssembly.Tag({parameters:['externref']}); + const exn = new WebAssembly.Exception(wasmTag, [obj]); + + // Test catch w/ return: + // This throws obj as a JS exception so it should be caught by the program and + // be returned as the original obj. + assert_equals(obj, instance.exports.catch_js_tag_and_return(obj)); + // This is a WebAssembly.Exception, so the exception should just pass through + // the program without being caught. + assert_throws_exactly(exn, () => instance.exports.catch_js_tag_and_return(exn)); + + // Test catch_ref w/ return: + // This throws obj as a JS exception so it should be caught by the program and + // be returned as the original obj. + assert_equals(obj, instance.exports.catch_ref_js_tag_and_return(obj)); + // This is a WebAssembly.Exception, so the exception should just pass through + // the program without being caught. + assert_throws_exactly(exn, () => instance.exports.catch_ref_js_tag_and_return(exn)); + + // Test catch_ref w/ throw_ref: + // This throws obj as a JS exception so it should be caught by the program and + // be rethrown. + assert_throws_exactly(obj, () => instance.exports.catch_ref_js_tag_and_throw_ref(obj)); + // This is a WebAssembly.Exception, so the exception should just pass through + // the program without being caught. + assert_throws_exactly(exn, () => instance.exports.catch_ref_js_tag_and_throw_ref(exn)); +}, "JS tag catching tests"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const jsTag = builder.addImportedTag("module", "JSTag", kSig_v_r); + + // Throw a JS object with WebAssembly.JSTag and check that we can catch it + // as-is from JavaScript. + builder.addFunction("throw_js_tag", kSig_v_r) + .addBody([ + kExprLocalGet, 0, + kExprThrow, jsTag, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + const {instance} = await WebAssembly.instantiate(buffer, { + module: { JSTag: WebAssembly.JSTag } + }); + + const obj = {}; + assert_throws_exactly(obj, () => instance.exports.throw_js_tag(obj)); +}, "JS tag throwing test");