Skip to content

Commit 9a4e8cb

Browse files
fgmccabechromium-wpt-export-bot
authored andcommitted
[wpt][webassembly][jspi] Add WPT tests for JSPI
Adding JavaScript Promise Integration tests from spec repo to WPT BYPASS_LARGE_CHANGE_WARNING Bug: 42202153 Change-Id: Ic691c83df8861456de69ab01fd048bf0c4472967 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5922662 Reviewed-by: Shu-yu Guo <[email protected]> Commit-Queue: Francis McCabe <[email protected]> Reviewed-by: Andreas Haas <[email protected]> Cr-Commit-Position: refs/heads/main@{#1370246}
1 parent b246c5f commit 9a4e8cb

File tree

4 files changed

+544
-0
lines changed

4 files changed

+544
-0
lines changed

wasm/jsapi/jspi/README.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This suite tests JSPI.
2+
3+
The tests are based on wasm spec tests.
Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
// META: global=window,dedicatedworker,jsshell
2+
// META: script=/wasm/jsapi/wasm-module-builder.js
3+
4+
// Test for invalid wrappers
5+
test(() => {
6+
assert_throws_js(TypeError, () => WebAssembly.promising({}),
7+
"Argument 0 must be a function");
8+
assert_throws_js(TypeError, () => WebAssembly.promising(() => {}),
9+
"Argument 0 must be a WebAssembly exported function");
10+
assert_throws_js(TypeError, () => WebAssembly.Suspending(() => {}),
11+
"WebAssembly.Suspending must be invoked with 'new'");
12+
assert_throws_js(TypeError, () => new WebAssembly.Suspending({}),
13+
"Argument 0 must be a function");
14+
15+
function asmModule() {
16+
"use asm";
17+
18+
function x(v) {
19+
v = v | 0;
20+
}
21+
return x;
22+
}
23+
assert_throws_js(TypeError, () => WebAssembly.promising(asmModule()),
24+
"Argument 0 must be a WebAssembly exported function");
25+
},"Valid use of API");
26+
27+
test(() => {
28+
let builder = new WasmModuleBuilder();
29+
builder.addGlobal(kWasmI32, true).exportAs('g');
30+
builder.addFunction("test", kSig_i_v)
31+
.addBody([
32+
kExprI32Const, 42,
33+
kExprGlobalSet, 0,
34+
kExprI32Const, 0
35+
]).exportFunc();
36+
let instance = builder.instantiate();
37+
let wrapper = WebAssembly.promising(instance.exports.test);
38+
wrapper();
39+
assert_equals(42, instance.exports.g.value);
40+
},"Promising function always entered");
41+
42+
promise_test(async () => {
43+
let builder = new WasmModuleBuilder();
44+
let import_index = builder.addImport('m', 'import', kSig_i_v);
45+
builder.addFunction("test", kSig_i_i)
46+
.addBody([
47+
kExprCallFunction, import_index, // suspend
48+
]).exportFunc();
49+
let js_import = () => 42;
50+
let instance = builder.instantiate({
51+
m: {
52+
import: js_import
53+
}
54+
});
55+
let wrapped_export = WebAssembly.promising(instance.exports.test);
56+
let export_promise = wrapped_export();
57+
assert_true(export_promise instanceof Promise);
58+
assert_equals(await export_promise, 42);
59+
}, "Always get a Promise");
60+
61+
promise_test(async () => {
62+
let builder = new WasmModuleBuilder();
63+
let import_index = builder.addImport('m', 'import', kSig_i_i);
64+
builder.addFunction("test", kSig_i_i)
65+
.addBody([
66+
kExprLocalGet, 0,
67+
kExprCallFunction, import_index, // suspend
68+
]).exportFunc();
69+
let js_import = new WebAssembly.Suspending(() => Promise.resolve(42));
70+
let instance = builder.instantiate({
71+
m: {
72+
import: js_import
73+
}
74+
});
75+
let wrapped_export = WebAssembly.promising(instance.exports.test);
76+
let export_promise = wrapped_export();
77+
assert_true(export_promise instanceof Promise);
78+
assert_equals(await export_promise, 42);
79+
}, "Suspend once");
80+
81+
promise_test(async () => {
82+
let builder = new WasmModuleBuilder();
83+
builder.addGlobal(kWasmI32, true).exportAs('g');
84+
let import_index = builder.addImport('m', 'import', kSig_i_v);
85+
// void test() {
86+
// for (i = 0; i < 5; ++i) {
87+
// g = g + await import();
88+
// }
89+
// }
90+
builder.addFunction("test", kSig_v_i)
91+
.addLocals({
92+
i32_count: 1
93+
})
94+
.addBody([
95+
kExprI32Const, 5,
96+
kExprLocalSet, 1,
97+
kExprLoop, kWasmStmt,
98+
kExprCallFunction, import_index, // suspend
99+
kExprGlobalGet, 0,
100+
kExprI32Add,
101+
kExprGlobalSet, 0,
102+
kExprLocalGet, 1,
103+
kExprI32Const, 1,
104+
kExprI32Sub,
105+
kExprLocalTee, 1,
106+
kExprBrIf, 0,
107+
kExprEnd,
108+
]).exportFunc();
109+
let i = 0;
110+
111+
function js_import() {
112+
return Promise.resolve(++i);
113+
};
114+
let wasm_js_import = new WebAssembly.Suspending(js_import);
115+
let instance = builder.instantiate({
116+
m: {
117+
import: wasm_js_import
118+
}
119+
});
120+
let wrapped_export = WebAssembly.promising(instance.exports.test);
121+
let export_promise = wrapped_export();
122+
assert_equals(instance.exports.g.value, 0);
123+
assert_true(export_promise instanceof Promise);
124+
await export_promise;
125+
assert_equals(instance.exports.g.value, 15);
126+
}, "Suspend/resume in a loop");
127+
128+
promise_test(async () => {
129+
let builder = new WasmModuleBuilder();
130+
let import_index = builder.addImport('m', 'import', kSig_i_v);
131+
builder.addFunction("test", kSig_i_v)
132+
.addBody([
133+
kExprCallFunction, import_index, // suspend
134+
]).exportFunc();
135+
let js_import = new WebAssembly.Suspending(() => Promise.resolve(42));
136+
let instance = builder.instantiate({
137+
m: {
138+
import: js_import
139+
}
140+
});
141+
let wrapped_export = WebAssembly.promising(instance.exports.test);
142+
assert_equals(await wrapped_export(), 42);
143+
144+
// Also try with a JS function with a mismatching arity.
145+
js_import = new WebAssembly.Suspending((unused) => Promise.resolve(42));
146+
instance = builder.instantiate({
147+
m: {
148+
import: js_import
149+
}
150+
});
151+
wrapped_export = WebAssembly.promising(instance.exports.test);
152+
assert_equals(await wrapped_export(), 42);
153+
154+
// Also try with a proxy.
155+
js_import = new WebAssembly.Suspending(new Proxy(() => Promise.resolve(42), {}));
156+
instance = builder.instantiate({
157+
m: {
158+
import: js_import
159+
}
160+
});
161+
wrapped_export = WebAssembly.promising(instance.exports.test);
162+
assert_equals(await wrapped_export(), 42);
163+
},"Suspending with mismatched args and via Proxy");
164+
165+
function recordAbeforeB() {
166+
let AbeforeB = [];
167+
let setA = () => {
168+
AbeforeB.push("A")
169+
}
170+
let setB = () => {
171+
AbeforeB.push("B")
172+
}
173+
let isAbeforeB = () =>
174+
AbeforeB[0] == "A" && AbeforeB[1] == "B";
175+
176+
let isBbeforeA = () =>
177+
AbeforeB[0] == "B" && AbeforeB[1] == "A";
178+
179+
return {
180+
setA: setA,
181+
setB: setB,
182+
isAbeforeB: isAbeforeB,
183+
isBbeforeA: isBbeforeA,
184+
}
185+
}
186+
187+
promise_test(async () => {
188+
let builder = new WasmModuleBuilder();
189+
let AbeforeB = recordAbeforeB();
190+
let import42_index = builder.addImport('m', 'import42', kSig_i_i);
191+
let importSetA_index = builder.addImport('m', 'setA', kSig_v_v);
192+
builder.addFunction("test", kSig_i_i)
193+
.addBody([
194+
kExprLocalGet, 0,
195+
kExprCallFunction, import42_index, // suspend?
196+
kExprCallFunction, importSetA_index
197+
]).exportFunc();
198+
let import42 = new WebAssembly.Suspending(() => Promise.resolve(42));
199+
let instance = builder.instantiate({
200+
m: {
201+
import42: import42,
202+
setA: AbeforeB.setA
203+
}
204+
});
205+
206+
let wrapped_export = WebAssembly.promising(instance.exports.test);
207+
208+
let exported_promise = wrapped_export();
209+
210+
AbeforeB.setB();
211+
212+
assert_equals(await exported_promise, 42);
213+
214+
assert_true(AbeforeB.isBbeforeA());
215+
}, "Make sure we actually suspend");
216+
217+
promise_test(async () => {
218+
let builder = new WasmModuleBuilder();
219+
let AbeforeB = recordAbeforeB();
220+
let import42_index = builder.addImport('m', 'import42', kSig_i_i);
221+
let importSetA_index = builder.addImport('m', 'setA', kSig_v_v);
222+
builder.addFunction("test", kSig_i_i)
223+
.addBody([
224+
kExprLocalGet, 0,
225+
kExprCallFunction, import42_index, // suspend?
226+
kExprCallFunction, importSetA_index
227+
]).exportFunc();
228+
let import42 = new WebAssembly.Suspending(() => 42);
229+
let instance = builder.instantiate({
230+
m: {
231+
import42: import42,
232+
setA: AbeforeB.setA
233+
}
234+
});
235+
236+
let wrapped_export = WebAssembly.promising(instance.exports.test);
237+
238+
let exported_promise = wrapped_export();
239+
AbeforeB.setB();
240+
assert_true(AbeforeB.isAbeforeB());
241+
242+
assert_equals(await exported_promise, 42);
243+
}, "Do not suspend if the import's return value is not a Promise");
244+
245+
promise_test(async (t) => {
246+
let tag = new WebAssembly.Tag({
247+
parameters: ['i32']
248+
});
249+
let builder = new WasmModuleBuilder();
250+
let import_index = builder.addImport('m', 'import', kSig_i_i);
251+
let tag_index = builder.addImportedTag('m', 'tag', kSig_v_i);
252+
builder.addFunction("test", kSig_i_i)
253+
.addBody([
254+
kExprTry, kWasmI32,
255+
kExprLocalGet, 0,
256+
kExprCallFunction, import_index,
257+
kExprCatch, tag_index,
258+
kExprEnd
259+
]).exportFunc();
260+
261+
function js_import(unused) {
262+
return Promise.reject(new WebAssembly.Exception(tag, [42]));
263+
};
264+
let wasm_js_import = new WebAssembly.Suspending(js_import);
265+
266+
let instance = builder.instantiate({
267+
m: {
268+
import: wasm_js_import,
269+
tag: tag
270+
}
271+
});
272+
let wrapped_export = WebAssembly.promising(instance.exports.test);
273+
let export_promise = wrapped_export();
274+
assert_true(export_promise instanceof Promise);
275+
assert_equals(await export_promise, 42);
276+
}, "Catch rejected promise");
277+
278+
async function TestSandwich(suspend) {
279+
// Set up a 'sandwich' scenario. The call chain looks like:
280+
// top (wasm) -> outer (js) -> bottom (wasm) -> inner (js)
281+
// If 'suspend' is true, the inner JS function returns a Promise, which
282+
// suspends the bottom wasm function, which returns a Promise, which suspends
283+
// the top wasm function, which returns a Promise. The inner Promise
284+
// resolves first, which resumes the bottom continuation. Then the outer
285+
// promise resolves which resumes the top continuation.
286+
// If 'suspend' is false, the bottom JS function returns a regular value and
287+
// no computation is suspended.
288+
let builder = new WasmModuleBuilder();
289+
let inner_index = builder.addImport('m', 'inner', kSig_i_i);
290+
let outer_index = builder.addImport('m', 'outer', kSig_i_i);
291+
builder.addFunction("top", kSig_i_i)
292+
.addBody([
293+
kExprLocalGet, 0,
294+
kExprCallFunction, outer_index
295+
]).exportFunc();
296+
builder.addFunction("bottom", kSig_i_i)
297+
.addBody([
298+
kExprLocalGet, 0,
299+
kExprCallFunction, inner_index
300+
]).exportFunc();
301+
302+
let inner = new WebAssembly.Suspending(() => suspend ? Promise.resolve(42) : 43);
303+
304+
let export_inner;
305+
let outer = new WebAssembly.Suspending(() => export_inner());
306+
307+
let instance = builder.instantiate({
308+
m: {
309+
inner,
310+
outer
311+
}
312+
});
313+
export_inner = WebAssembly.promising(instance.exports.bottom);
314+
let export_top = WebAssembly.promising(instance.exports.top);
315+
let result = export_top();
316+
assert_true(result instanceof Promise);
317+
if (suspend)
318+
assert_equals(await result, 42);
319+
else
320+
assert_equals(await result, 43);
321+
}
322+
323+
promise_test(async () => {
324+
TestSandwich(true);
325+
}, "Test sandwich with suspension");
326+
327+
promise_test(async () => {
328+
TestSandwich(false);
329+
}, "Test sandwich with no suspension");
330+
331+
test(() => {
332+
// Check that a promising function with no return is allowed.
333+
let builder = new WasmModuleBuilder();
334+
builder.addFunction("export", kSig_v_v).addBody([]).exportFunc();
335+
let instance = builder.instantiate();
336+
let export_wrapper = WebAssembly.promising(instance.exports.export);
337+
let export_sig = export_wrapper.type();
338+
assert_array_equals(export_sig.parameters, []);
339+
assert_array_equals(export_sig.results, ['externref']);
340+
},"Promising with no return");
341+
342+
promise_test(async () => {
343+
let builder1 = new WasmModuleBuilder();
344+
let import_index = builder1.addImport('m', 'import', kSig_i_v);
345+
builder1.addFunction("f", kSig_i_v)
346+
.addBody([
347+
kExprCallFunction, import_index, // suspend
348+
kExprI32Const, 1,
349+
kExprI32Add,
350+
]).exportFunc();
351+
let js_import = new WebAssembly.Suspending(() => Promise.resolve(1));
352+
let instance1 = builder1.instantiate({
353+
m: {
354+
import: js_import
355+
}
356+
});
357+
let builder2 = new WasmModuleBuilder();
358+
import_index = builder2.addImport('m', 'import', kSig_i_v);
359+
builder2.addFunction("main", kSig_i_v)
360+
.addBody([
361+
kExprCallFunction, import_index,
362+
kExprI32Const, 1,
363+
kExprI32Add,
364+
]).exportFunc();
365+
let instance2 = builder2.instantiate({
366+
m: {
367+
import: instance1.exports.f
368+
}
369+
});
370+
let wrapped_export = WebAssembly.promising(instance2.exports.main);
371+
assert_equals(await wrapped_export(), 3);
372+
},"Suspend two modules");

0 commit comments

Comments
 (0)