Skip to content

Commit e2c8d90

Browse files
authored
[js-api] Re-add old tests to test/js-api/exception (WebAssembly#315)
These two tests used to be in https://github.com/WebAssembly/exception-handling/tree/main/test/js-api/exception and were moved into https://github.com/WebAssembly/exception-handling/tree/main/test/legacy/exceptions/js-api in WebAssembly#305. I'm planning new version of these tests that use `try_table` and `throw_ref` in https://github.com/WebAssembly/exception-handling/tree/main/test/js-api/exception, but it wouldn't require to rewrite the whole tests, but copying these tests into the this directory and modify them in a single PR makes Github think these are brand-new files, resulting in a large diff containing the whole files that is difficult to review (and which has been reviewed and in the repo for a long time already). So I'm making a PR that only re-adds these file here so that I can add changes to these files in another PR.
1 parent 2f5a7c5 commit e2c8d90

File tree

2 files changed

+290
-0
lines changed

2 files changed

+290
-0
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// META: global=window,dedicatedworker,jsshell,shadowrealm
2+
// META: script=/wasm/jsapi/wasm-module-builder.js
3+
4+
function assert_throws_wasm(fn, message) {
5+
try {
6+
fn();
7+
assert_not_reached(`expected to throw with ${message}`);
8+
} catch (e) {
9+
assert_true(e instanceof WebAssembly.Exception, `Error should be a WebAssembly.Exception with ${message}`);
10+
}
11+
}
12+
13+
promise_test(async () => {
14+
const kSig_v_r = makeSig([kWasmExternRef], []);
15+
const builder = new WasmModuleBuilder();
16+
const tagIndex = builder.addTag(kSig_v_r);
17+
builder.addFunction("throw_param", kSig_v_r)
18+
.addBody([
19+
kExprLocalGet, 0,
20+
kExprThrow, tagIndex,
21+
])
22+
.exportFunc();
23+
const buffer = builder.toBuffer();
24+
const {instance} = await WebAssembly.instantiate(buffer, {});
25+
const values = [
26+
undefined,
27+
null,
28+
true,
29+
false,
30+
"test",
31+
Symbol(),
32+
0,
33+
1,
34+
4.2,
35+
NaN,
36+
Infinity,
37+
{},
38+
() => {},
39+
];
40+
for (const v of values) {
41+
assert_throws_wasm(() => instance.exports.throw_param(v), String(v));
42+
}
43+
}, "Wasm function throws argument");
44+
45+
promise_test(async () => {
46+
const builder = new WasmModuleBuilder();
47+
const tagIndex = builder.addTag(kSig_v_a);
48+
builder.addFunction("throw_null", kSig_v_v)
49+
.addBody([
50+
kExprRefNull, kAnyFuncCode,
51+
kExprThrow, tagIndex,
52+
])
53+
.exportFunc();
54+
const buffer = builder.toBuffer();
55+
const {instance} = await WebAssembly.instantiate(buffer, {});
56+
assert_throws_wasm(() => instance.exports.throw_null());
57+
}, "Wasm function throws null");
58+
59+
promise_test(async () => {
60+
const builder = new WasmModuleBuilder();
61+
const tagIndex = builder.addTag(kSig_v_i);
62+
builder.addFunction("throw_int", kSig_v_v)
63+
.addBody([
64+
...wasmI32Const(7),
65+
kExprThrow, tagIndex,
66+
])
67+
.exportFunc();
68+
const buffer = builder.toBuffer();
69+
const {instance} = await WebAssembly.instantiate(buffer, {});
70+
assert_throws_wasm(() => instance.exports.throw_int());
71+
}, "Wasm function throws integer");
72+
73+
promise_test(async () => {
74+
const builder = new WasmModuleBuilder();
75+
const fnIndex = builder.addImport("module", "fn", kSig_v_v);
76+
const tagIndex= builder.addTag(kSig_v_r);
77+
builder.addFunction("catch_exception", kSig_r_v)
78+
.addBody([
79+
kExprTry, kWasmVoid,
80+
kExprCallFunction, fnIndex,
81+
kExprCatch, tagIndex,
82+
kExprReturn,
83+
kExprEnd,
84+
kExprRefNull, kExternRefCode,
85+
])
86+
.exportFunc();
87+
88+
const buffer = builder.toBuffer();
89+
90+
const error = new Error();
91+
const fn = () => { throw error };
92+
const {instance} = await WebAssembly.instantiate(buffer, {
93+
module: { fn }
94+
});
95+
assert_throws_exactly(error, () => instance.exports.catch_exception());
96+
}, "Imported JS function throws");
97+
98+
promise_test(async () => {
99+
const builder = new WasmModuleBuilder();
100+
const fnIndex = builder.addImport("module", "fn", kSig_v_v);
101+
builder.addFunction("catch_and_rethrow", kSig_r_v)
102+
.addBody([
103+
kExprTry, kWasmVoid,
104+
kExprCallFunction, fnIndex,
105+
kExprCatchAll,
106+
kExprRethrow, 0x00,
107+
kExprEnd,
108+
kExprRefNull, kExternRefCode,
109+
])
110+
.exportFunc();
111+
112+
const buffer = builder.toBuffer();
113+
114+
const error = new Error();
115+
const fn = () => { throw error };
116+
const {instance} = await WebAssembly.instantiate(buffer, {
117+
module: { fn }
118+
});
119+
assert_throws_exactly(error, () => instance.exports.catch_and_rethrow());
120+
}, "Imported JS function throws, Wasm catches and rethrows");
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// META: global=window,dedicatedworker,jsshell,shadowrealm
2+
// META: script=/wasm/jsapi/assertions.js
3+
// META: script=/wasm/jsapi/wasm-module-builder.js
4+
5+
test(() => {
6+
const builder = new WasmModuleBuilder();
7+
8+
// Tag defined in JavaScript and imported into Wasm
9+
const jsTag = new WebAssembly.Tag({ parameters: ["i32"] });
10+
const jsTagIndex = builder.addImportedTag("module", "jsTag", kSig_v_i);
11+
const jsTagExn = new WebAssembly.Exception(jsTag, [42]);
12+
const jsTagExnSamePayload = new WebAssembly.Exception(jsTag, [42]);
13+
const jsTagExnDiffPayload = new WebAssembly.Exception(jsTag, [53]);
14+
const throwJSTagExnIndex = builder.addImport("module", "throwJSTagExn", kSig_v_v);
15+
16+
// Tag defined in Wasm and exported to JS
17+
const wasmTagIndex = builder.addTag(kSig_v_i);
18+
builder.addExportOfKind("wasmTag", kExternalTag, wasmTagIndex);
19+
const throwWasmTagExnIndex = builder.addImport("module", "throwWasmTagExn", kSig_v_v);
20+
// Will be assigned after an instance is created
21+
let wasmTagExn = null;
22+
let wasmTagExnSamePayload = null;
23+
let wasmTagExnDiffPayload = null;
24+
25+
const imports = {
26+
module: {
27+
throwJSTagExn: function() { throw jsTagExn; },
28+
throwWasmTagExn: function() { throw wasmTagExn; },
29+
jsTag: jsTag
30+
}
31+
};
32+
33+
// Call a JS function that throws an exception using a JS-defined tag, catches
34+
// it with a 'catch' instruction, and rethrows it.
35+
builder
36+
.addFunction("catch_js_tag_rethrow", kSig_v_v)
37+
.addBody([
38+
kExprTry, kWasmVoid,
39+
kExprCallFunction, throwJSTagExnIndex,
40+
kExprCatch, jsTagIndex,
41+
kExprDrop,
42+
kExprRethrow, 0x00,
43+
kExprEnd
44+
])
45+
.exportFunc();
46+
47+
// Call a JS function that throws an exception using a Wasm-defined tag,
48+
// catches it with a 'catch' instruction, and rethrows it.
49+
builder
50+
.addFunction("catch_wasm_tag_rethrow", kSig_v_v)
51+
.addBody([
52+
kExprTry, kWasmVoid,
53+
kExprCallFunction, throwWasmTagExnIndex,
54+
kExprCatch, wasmTagIndex,
55+
kExprDrop,
56+
kExprRethrow, 0x00,
57+
kExprEnd
58+
])
59+
.exportFunc();
60+
61+
// Call a JS function that throws an exception using a JS-defined tag, catches
62+
// it with a 'catch_all' instruction, and rethrows it.
63+
builder
64+
.addFunction("catch_all_js_tag_rethrow", kSig_v_v)
65+
.addBody([
66+
kExprTry, kWasmVoid,
67+
kExprCallFunction, throwJSTagExnIndex,
68+
kExprCatchAll,
69+
kExprRethrow, 0x00,
70+
kExprEnd
71+
])
72+
.exportFunc();
73+
74+
// Call a JS function that throws an exception using a Wasm-defined tag,
75+
// catches it with a 'catch_all' instruction, and rethrows it.
76+
builder
77+
.addFunction("catch_all_wasm_tag_rethrow", kSig_v_v)
78+
.addBody([
79+
kExprTry, kWasmVoid,
80+
kExprCallFunction, throwWasmTagExnIndex,
81+
kExprCatchAll,
82+
kExprRethrow, 0x00,
83+
kExprEnd
84+
])
85+
.exportFunc();
86+
87+
// Call a JS function that throws an exception, catches it with a 'catch'
88+
// instruction, and returns its i32 payload.
89+
builder
90+
.addFunction("catch_js_tag_return_payload", kSig_i_v)
91+
.addBody([
92+
kExprTry, kWasmI32,
93+
kExprCallFunction, throwJSTagExnIndex,
94+
kExprI32Const, 0x00,
95+
kExprCatch, jsTagIndex,
96+
kExprReturn,
97+
kExprEnd
98+
])
99+
.exportFunc();
100+
101+
// Call a JS function that throws an exception, catches it with a 'catch'
102+
// instruction, and throws a new exception using that payload.
103+
builder
104+
.addFunction("catch_js_tag_throw_payload", kSig_v_v)
105+
.addBody([
106+
kExprTry, kWasmVoid,
107+
kExprCallFunction, throwJSTagExnIndex,
108+
kExprCatch, jsTagIndex,
109+
kExprThrow, jsTagIndex,
110+
kExprEnd
111+
])
112+
.exportFunc();
113+
114+
const buffer = builder.toBuffer();
115+
116+
WebAssembly.instantiate(buffer, imports).then(result => {
117+
// The exception object's identity should be preserved across 'rethrow's in
118+
// Wasm code. Do tests with a tag defined in JS.
119+
try {
120+
result.instance.exports.catch_js_tag_rethrow();
121+
} catch (e) {
122+
assert_equals(e, jsTagExn);
123+
// Even if they have the same payload, they are different objects, so they
124+
// shouldn't compare equal.
125+
assert_not_equals(e, jsTagExnSamePayload);
126+
assert_not_equals(e, jsTagExnDiffPayload);
127+
}
128+
try {
129+
result.instance.exports.catch_all_js_tag_rethrow();
130+
} catch (e) {
131+
assert_equals(e, jsTagExn);
132+
assert_not_equals(e, jsTagExnSamePayload);
133+
assert_not_equals(e, jsTagExnDiffPayload);
134+
}
135+
136+
// Do the same tests with a tag defined in Wasm.
137+
const wasmTag = result.instance.exports.wasmTag;
138+
wasmTagExn = new WebAssembly.Exception(wasmTag, [42]);
139+
wasmTagExnSamePayload = new WebAssembly.Exception(wasmTag, [42]);
140+
wasmTagExnDiffPayload = new WebAssembly.Exception(wasmTag, [53]);
141+
try {
142+
result.instance.exports.catch_wasm_tag_rethrow();
143+
} catch (e) {
144+
assert_equals(e, wasmTagExn);
145+
assert_not_equals(e, wasmTagExnSamePayload);
146+
assert_not_equals(e, wasmTagExnDiffPayload);
147+
}
148+
try {
149+
result.instance.exports.catch_all_wasm_tag_rethrow();
150+
} catch (e) {
151+
assert_equals(e, wasmTagExn);
152+
assert_not_equals(e, wasmTagExnSamePayload);
153+
assert_not_equals(e, wasmTagExnDiffPayload);
154+
}
155+
156+
// This function catches the exception and returns its i32 payload, which
157+
// should match the original payload.
158+
assert_equals(result.instance.exports.catch_js_tag_return_payload(), 42);
159+
160+
// This function catches the exception and throws a new exception using the
161+
// its payload. Even if the payload is reused, the exception objects should
162+
// not compare equal.
163+
try {
164+
result.instance.exports.catch_js_tag_throw_payload();
165+
} catch (e) {
166+
assert_equals(e.getArg(jsTag, 0), 42);
167+
assert_not_equals(e, jsTagExn);
168+
}
169+
});
170+
}, "Identity check");

0 commit comments

Comments
 (0)