2
2
// META: script=/wasm/jsapi/wasm-module-builder.js
3
3
4
4
function ToPromising ( wasm_export ) {
5
- let sig = WebAssembly . Function . type ( wasm_export ) ;
5
+ let sig = wasm_export . type ( ) ;
6
6
assert_true ( sig . parameters . length > 0 ) ;
7
7
assert_equals ( 'externref' , sig . parameters [ 0 ] ) ;
8
8
let wrapper_sig = {
@@ -55,16 +55,16 @@ test(() => {
55
55
{ promising : 'first' } ) ) ;
56
56
57
57
// Check the wrapper signatures.
58
- let export_sig = WebAssembly . Function . type ( export_wrapper ) ;
58
+ let export_sig = export_wrapper . type ( ) ;
59
59
assert_array_equals ( [ 'i32' ] , export_sig . parameters ) ;
60
60
assert_array_equals ( [ 'externref' ] , export_sig . results ) ;
61
61
62
- let import_sig = WebAssembly . Function . type ( import_wrapper ) ;
62
+ let import_sig = import_wrapper . type ( ) ;
63
63
assert_array_equals ( [ 'externref' , 'i32' ] , import_sig . parameters ) ;
64
64
assert_array_equals ( [ ] , import_sig . results ) ;
65
65
66
66
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 ( ) ;
68
68
assert_array_equals ( [ ] , void_export_sig . parameters ) ;
69
69
assert_array_equals ( [ 'externref' ] , void_export_sig . results ) ;
70
70
} , "Test import and export type checking" ) ;
@@ -134,11 +134,14 @@ promise_test(async () => {
134
134
135
135
test ( ( ) => {
136
136
let builder = new WasmModuleBuilder ( ) ;
137
+ builder . addGlobal ( kWasmI32 , true ) . exportAs ( 'g' ) ;
137
138
import_index = builder . addImport ( 'm' , 'import' , kSig_i_r ) ;
138
139
builder . addFunction ( "test" , kSig_i_r )
139
140
. addBody ( [
140
141
kExprLocalGet , 0 ,
141
- kExprCallFunction , import_index , // suspend
142
+ kExprCallFunction , import_index ,
143
+ kExprGlobalSet , 0 ,
144
+ kExprGlobalGet , 0 ,
142
145
] ) . exportFunc ( ) ;
143
146
function js_import ( ) {
144
147
return 42
@@ -149,19 +152,21 @@ test(() => {
149
152
{ suspending : 'first' } ) ;
150
153
let instance = builder . instantiate ( { m : { import : wasm_js_import } } ) ;
151
154
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 ) ;
153
158
} , "Do not suspend if the import's return value is not a Promise" ) ;
154
159
155
160
test ( t => {
156
161
let tag = new WebAssembly . Tag ( { parameters : [ ] } ) ;
157
162
let builder = new WasmModuleBuilder ( ) ;
158
163
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 ) ;
160
165
builder . addFunction ( "test" , kSig_i_r )
161
166
. addBody ( [
162
167
kExprLocalGet , 0 ,
163
168
kExprCallFunction , import_index ,
164
- kExprThrow , tag_index
169
+ kExprCallFunction , js_throw_index ,
165
170
] ) . exportFunc ( ) ;
166
171
function js_import ( ) {
167
172
return Promise . resolve ( ) ;
@@ -170,29 +175,30 @@ test(t => {
170
175
{ parameters : [ 'externref' ] , results : [ 'i32' ] } ,
171
176
js_import ,
172
177
{ suspending : 'first' } ) ;
178
+ function js_throw ( ) {
179
+ throw new Error ( ) ;
180
+ }
173
181
174
- let instance = builder . instantiate ( { m : { import : wasm_js_import , tag : tag } } ) ;
182
+ let instance = builder . instantiate ( { m : { import : wasm_js_import , js_throw } } ) ;
175
183
let wrapped_export = ToPromising ( instance . exports . test ) ;
176
184
let export_promise = wrapped_export ( ) ;
177
185
assert_true ( export_promise instanceof Promise ) ;
178
- promise_rejects ( t , new WebAssembly . Exception ( tag , [ ] ) , export_promise ) ;
186
+ promise_rejects ( t , new Error ( ) , export_promise ) ;
179
187
} , "Throw after the first suspension" ) ;
180
188
181
- promise_test ( async ( ) => {
189
+ // TODO: Use wasm exception handling to check that the exception can be caught in wasm.
190
+
191
+ test ( t => {
182
192
let tag = new WebAssembly . Tag ( { parameters : [ 'i32' ] } ) ;
183
193
let builder = new WasmModuleBuilder ( ) ;
184
194
import_index = builder . addImport ( 'm' , 'import' , kSig_i_r ) ;
185
- tag_index = builder . addImportedException ( 'm' , 'tag' , kSig_v_i ) ;
186
195
builder . addFunction ( "test" , kSig_i_r )
187
196
. addBody ( [
188
- kExprTry , kWasmI32 ,
189
197
kExprLocalGet , 0 ,
190
198
kExprCallFunction , import_index ,
191
- kExprCatch , tag_index ,
192
- kExprEnd ,
193
199
] ) . exportFunc ( ) ;
194
200
function js_import ( ) {
195
- return Promise . reject ( new WebAssembly . Exception ( tag , [ 42 ] ) ) ;
201
+ return Promise . reject ( new Error ( ) ) ;
196
202
} ;
197
203
let wasm_js_import = new WebAssembly . Function (
198
204
{ parameters : [ 'externref' ] , results : [ 'i32' ] } ,
@@ -203,7 +209,7 @@ promise_test(async () => {
203
209
let wrapped_export = ToPromising ( instance . exports . test ) ;
204
210
let export_promise = wrapped_export ( ) ;
205
211
assert_true ( export_promise instanceof Promise ) ;
206
- assert_equals ( 42 , await export_promise ) ;
212
+ promise_rejects ( t , new Error ( ) , export_promise ) ;
207
213
} , "Rejecting promise" ) ;
208
214
209
215
async function TestNestedSuspenders ( suspend ) {
@@ -214,8 +220,8 @@ async function TestNestedSuspenders(suspend) {
214
220
// the outer wasm function, which returns a Promise. The inner Promise
215
221
// resolves first, which resumes the inner continuation. Then the outer
216
222
// 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.
219
225
let builder = new WasmModuleBuilder ( ) ;
220
226
inner_index = builder . addImport ( 'm' , 'inner' , kSig_i_r ) ;
221
227
outer_index = builder . addImport ( 'm' , 'outer' , kSig_i_r ) ;
@@ -238,19 +244,15 @@ async function TestNestedSuspenders(suspend) {
238
244
let export_inner ;
239
245
let outer = new WebAssembly . Function (
240
246
{ parameters : [ 'externref' ] , results : [ 'i32' ] } ,
241
- ( ) => export_inner ( ) ,
247
+ ( ) => suspend ? export_inner ( ) : 42 ,
242
248
{ suspending : 'first' } ) ;
243
249
244
250
let instance = builder . instantiate ( { m : { inner, outer} } ) ;
245
251
export_inner = ToPromising ( instance . exports . inner ) ;
246
252
let export_outer = ToPromising ( instance . exports . outer ) ;
247
253
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 ) ;
254
256
}
255
257
256
258
test ( ( ) => {
@@ -283,3 +285,103 @@ test(() => {
283
285
assert_throws ( WebAssembly . RuntimeError , ( ) => instance . exports . test ( s ) ) ;
284
286
}
285
287
} , "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" ) ;
0 commit comments