Skip to content

Commit 147411b

Browse files
Update and improve tests
- Update tests that relied on the old API, where export wrappers were allowed to return "non-promisified" results. - Add new tests ported from the V8 test suite - Avoid dependency on wasm exception handling
1 parent 48fbac8 commit 147411b

File tree

2 files changed

+129
-26
lines changed

2 files changed

+129
-26
lines changed

test/js-api/js-promise-integration/js-promise-integration.any.js

+128-26
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// META: script=/wasm/jsapi/wasm-module-builder.js
33

44
function ToPromising(wasm_export) {
5-
let sig = WebAssembly.Function.type(wasm_export);
5+
let sig = wasm_export.type();
66
assert_true(sig.parameters.length > 0);
77
assert_equals('externref', sig.parameters[0]);
88
let wrapper_sig = {
@@ -55,16 +55,16 @@ test(() => {
5555
{promising: 'first'}));
5656

5757
// Check the wrapper signatures.
58-
let export_sig = WebAssembly.Function.type(export_wrapper);
58+
let export_sig = export_wrapper.type();
5959
assert_array_equals(['i32'], export_sig.parameters);
6060
assert_array_equals(['externref'], export_sig.results);
6161

62-
let import_sig = WebAssembly.Function.type(import_wrapper);
62+
let import_sig = import_wrapper.type();
6363
assert_array_equals(['externref', 'i32'], import_sig.parameters);
6464
assert_array_equals([], import_sig.results);
6565

6666
let void_export_wrapper = ToPromising(instance.exports.void_export);
67-
let void_export_sig = WebAssembly.Function.type(void_export_wrapper);
67+
let void_export_sig = void_export_wrapper.type();
6868
assert_array_equals([], void_export_sig.parameters);
6969
assert_array_equals(['externref'], void_export_sig.results);
7070
}, "Test import and export type checking");
@@ -134,11 +134,14 @@ promise_test(async () => {
134134

135135
test(() => {
136136
let builder = new WasmModuleBuilder();
137+
builder.addGlobal(kWasmI32, true).exportAs('g');
137138
import_index = builder.addImport('m', 'import', kSig_i_r);
138139
builder.addFunction("test", kSig_i_r)
139140
.addBody([
140141
kExprLocalGet, 0,
141-
kExprCallFunction, import_index, // suspend
142+
kExprCallFunction, import_index,
143+
kExprGlobalSet, 0,
144+
kExprGlobalGet, 0,
142145
]).exportFunc();
143146
function js_import() {
144147
return 42
@@ -149,19 +152,21 @@ test(() => {
149152
{suspending: 'first'});
150153
let instance = builder.instantiate({m: {import: wasm_js_import}});
151154
let wrapped_export = ToPromising(instance.exports.test);
152-
assert_equals(42, wrapped_export());
155+
wrapped_export();
156+
// The global was updated synchronously.
157+
assert_equals(42, instance.exports.g.value);
153158
}, "Do not suspend if the import's return value is not a Promise");
154159

155160
test(t => {
156161
let tag = new WebAssembly.Tag({parameters: []});
157162
let builder = new WasmModuleBuilder();
158163
import_index = builder.addImport('m', 'import', kSig_i_r);
159-
tag_index = builder.addImportedException('m', 'tag', kSig_v_v);
164+
js_throw_index = builder.addImport('m', 'js_throw', kSig_v_v);
160165
builder.addFunction("test", kSig_i_r)
161166
.addBody([
162167
kExprLocalGet, 0,
163168
kExprCallFunction, import_index,
164-
kExprThrow, tag_index
169+
kExprCallFunction, js_throw_index,
165170
]).exportFunc();
166171
function js_import() {
167172
return Promise.resolve();
@@ -170,29 +175,30 @@ test(t => {
170175
{parameters: ['externref'], results: ['i32']},
171176
js_import,
172177
{suspending: 'first'});
178+
function js_throw() {
179+
throw new Error();
180+
}
173181

174-
let instance = builder.instantiate({m: {import: wasm_js_import, tag: tag}});
182+
let instance = builder.instantiate({m: {import: wasm_js_import, js_throw}});
175183
let wrapped_export = ToPromising(instance.exports.test);
176184
let export_promise = wrapped_export();
177185
assert_true(export_promise instanceof Promise);
178-
promise_rejects(t, new WebAssembly.Exception(tag, []), export_promise);
186+
promise_rejects(t, new Error(), export_promise);
179187
}, "Throw after the first suspension");
180188

181-
promise_test(async () => {
189+
// TODO: Use wasm exception handling to check that the exception can be caught in wasm.
190+
191+
test(t => {
182192
let tag = new WebAssembly.Tag({parameters: ['i32']});
183193
let builder = new WasmModuleBuilder();
184194
import_index = builder.addImport('m', 'import', kSig_i_r);
185-
tag_index = builder.addImportedException('m', 'tag', kSig_v_i);
186195
builder.addFunction("test", kSig_i_r)
187196
.addBody([
188-
kExprTry, kWasmI32,
189197
kExprLocalGet, 0,
190198
kExprCallFunction, import_index,
191-
kExprCatch, tag_index,
192-
kExprEnd,
193199
]).exportFunc();
194200
function js_import() {
195-
return Promise.reject(new WebAssembly.Exception(tag, [42]));
201+
return Promise.reject(new Error());
196202
};
197203
let wasm_js_import = new WebAssembly.Function(
198204
{parameters: ['externref'], results: ['i32']},
@@ -203,7 +209,7 @@ promise_test(async () => {
203209
let wrapped_export = ToPromising(instance.exports.test);
204210
let export_promise = wrapped_export();
205211
assert_true(export_promise instanceof Promise);
206-
assert_equals(42, await export_promise);
212+
promise_rejects(t, new Error(), export_promise);
207213
}, "Rejecting promise");
208214

209215
async function TestNestedSuspenders(suspend) {
@@ -214,8 +220,8 @@ async function TestNestedSuspenders(suspend) {
214220
// the outer wasm function, which returns a Promise. The inner Promise
215221
// resolves first, which resumes the inner continuation. Then the outer
216222
// promise resolves which resumes the outer continuation.
217-
// If 'suspend' is false, the inner JS function returns a regular value and
218-
// no computation is suspended.
223+
// If 'suspend' is false, the inner and outer JS functions return a regular
224+
// value and no computation is suspended.
219225
let builder = new WasmModuleBuilder();
220226
inner_index = builder.addImport('m', 'inner', kSig_i_r);
221227
outer_index = builder.addImport('m', 'outer', kSig_i_r);
@@ -238,19 +244,15 @@ async function TestNestedSuspenders(suspend) {
238244
let export_inner;
239245
let outer = new WebAssembly.Function(
240246
{parameters: ['externref'], results: ['i32']},
241-
() => export_inner(),
247+
() => suspend ? export_inner() : 42,
242248
{suspending: 'first'});
243249

244250
let instance = builder.instantiate({m: {inner, outer}});
245251
export_inner = ToPromising(instance.exports.inner);
246252
let export_outer = ToPromising(instance.exports.outer);
247253
let result = export_outer();
248-
if (suspend) {
249-
assert_true(result instanceof Promise);
250-
assert_equals(42, await result);
251-
} else {
252-
assert_equals(43, result);
253-
}
254+
assert_true(result instanceof Promise);
255+
assert_equals(42, await result);
254256
}
255257

256258
test(() => {
@@ -283,3 +285,103 @@ test(() => {
283285
assert_throws(WebAssembly.RuntimeError, () => instance.exports.test(s));
284286
}
285287
}, "Call import with an invalid suspender");
288+
289+
test(t => {
290+
let builder = new WasmModuleBuilder();
291+
builder.addFunction("test", kSig_i_r)
292+
.addBody([
293+
kExprLocalGet, 0,
294+
kExprCallFunction, 0
295+
]).exportFunc();
296+
let instance = builder.instantiate();
297+
let wrapper = ToPromising(instance.exports.test);
298+
promise_rejects(t, new RangeError(), wrapper());
299+
}, "Stack overflow");
300+
301+
test (() => {
302+
let builder = new WasmModuleBuilder();
303+
let import_index = builder.addImport('m', 'import', kSig_i_r);
304+
builder.addFunction("test", kSig_i_r)
305+
.addBody([
306+
kExprLocalGet, 0,
307+
kExprCallFunction, import_index, // suspend
308+
]).exportFunc();
309+
builder.addFunction("return_suspender", kSig_r_r)
310+
.addBody([
311+
kExprLocalGet, 0
312+
]).exportFunc();
313+
let js_import = new WebAssembly.Function(
314+
{parameters: ['externref'], results: ['i32']},
315+
() => Promise.resolve(42),
316+
{suspending: 'first'});
317+
let instance = builder.instantiate({m: {import: js_import}});
318+
let suspender = ToPromising(instance.exports.return_suspender)();
319+
for (s of [suspender, null, undefined, {}]) {
320+
assert_throws(WebAssembly.RuntimeError, () => instance.exports.test(s));
321+
}
322+
}, "Pass an invalid suspender");
323+
324+
// TODO: Test suspension with funcref.
325+
326+
test(t => {
327+
// The call stack of this test looks like:
328+
// export1 -> import1 -> export2 -> import2
329+
// Where export1 is "promising" and import2 is "suspending". Returning a
330+
// promise from import2 should trap because of the JS import in the middle.
331+
let builder = new WasmModuleBuilder();
332+
let import1_index = builder.addImport("m", "import1", kSig_i_v);
333+
let import2_index = builder.addImport("m", "import2", kSig_i_r);
334+
builder.addGlobal(kWasmAnyRef, true);
335+
builder.addFunction("export1", kSig_i_r)
336+
.addBody([
337+
// export1 -> import1 (unwrapped)
338+
kExprLocalGet, 0,
339+
kExprGlobalSet, 0,
340+
kExprCallFunction, import1_index,
341+
]).exportFunc();
342+
builder.addFunction("export2", kSig_i_v)
343+
.addBody([
344+
// export2 -> import2 (suspending)
345+
kExprGlobalGet, 0,
346+
kExprCallFunction, import2_index,
347+
]).exportFunc();
348+
let instance;
349+
function import1() {
350+
// import1 -> export2 (unwrapped)
351+
instance.exports.export2();
352+
}
353+
function import2() {
354+
return Promise.resolve(0);
355+
}
356+
import2 = new WebAssembly.Function(
357+
{parameters: ['externref'], results: ['i32']},
358+
import2,
359+
{suspending: 'first'});
360+
instance = builder.instantiate(
361+
{'m':
362+
{'import1': import1,
363+
'import2': import2
364+
}});
365+
// export1 (promising)
366+
let wrapper = new WebAssembly.Function(
367+
{parameters: [], results: ['externref']},
368+
instance.exports.export1,
369+
{promising: 'first'});
370+
promise_rejects(t, new WebAssembly.RuntimeError(), wrapper());
371+
}, "Test that trying to suspend JS frames traps");
372+
373+
test(() => {
374+
let builder = new WasmModuleBuilder();
375+
import_index = builder.addImport('m', 'import', kSig_i_r);
376+
builder.addFunction("test", kSig_i_r)
377+
.addBody([
378+
kExprLocalGet, 0,
379+
kExprCallFunction, import_index, // suspend
380+
]).exportFunc();
381+
let js_import = new WebAssembly.Function(
382+
{parameters: ['externref'], results: ['i32']},
383+
() => 42,
384+
{suspending: 'first'});
385+
let instance = builder.instantiate({m: {import: js_import}});
386+
assert_equals(42, instance.exports.test(null));
387+
}, "Pass an invalid suspender to the import and return a non-promise");

test/js-api/wasm-module-builder.js

+1
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,7 @@ class WasmModuleBuilder {
10231023
section.emit_u32v(global.function_index);
10241024
} else {
10251025
section.emit_u8(kExprRefNull);
1026+
section.emit_u8(kWasmAnyRef);
10261027
}
10271028
break;
10281029
}

0 commit comments

Comments
 (0)