1+ // This script can be customized by setting the following variables in code that
2+ // runs before this script.
3+ //
4+ // The binary to be run. (If not set, we get the filename from argv and read
5+ // from it.)
6+ var binary ;
7+ // A second binary to be linked in and run as well. (Can also be read from
8+ // argv.)
9+ var secondBinary ;
10+ // Whether we are fuzzing JSPI. In addition to this being set, the "async" and
11+ // "await" keywords must be taken out of the /* KEYWORD */ comments (which they
12+ // are normally in, so as not to affect normal fuzzing).
13+ var JSPI ;
14+
115// Shell integration: find argv and set up readBinary().
216var argv ;
317var readBinary ;
@@ -25,9 +39,6 @@ if (typeof process === 'object' && typeof require === 'function') {
2539 } ;
2640}
2741
28- // The binary to be run. This may be set already (by code that runs before this
29- // script), and if not, we get the filename from argv.
30- var binary ;
3142if ( ! binary ) {
3243 binary = readBinary ( argv [ 0 ] ) ;
3344}
@@ -43,7 +54,6 @@ if (argv.length > 0 && argv[argv.length - 1].startsWith('exports:')) {
4354
4455// If a second parameter is given, it is a second binary that we will link in
4556// with it.
46- var secondBinary ;
4757if ( argv [ 1 ] ) {
4858 secondBinary = readBinary ( argv [ 1 ] ) ;
4959}
@@ -163,9 +173,9 @@ function callFunc(func) {
163173// Calls a given function in a try-catch, swallowing JS exceptions, and return 1
164174// if we did in fact swallow an exception. Wasm traps are not swallowed (see
165175// details below).
166- function tryCall ( func ) {
176+ /* async */ function tryCall ( func ) {
167177 try {
168- func ( ) ;
178+ /* await */ func ( ) ;
169179 return 0 ;
170180 } catch ( e ) {
171181 // We only want to catch exceptions, not wasm traps: traps should still
@@ -243,19 +253,39 @@ var imports = {
243253 } ,
244254
245255 // Export operations.
246- 'call-export' : ( index ) => {
247- callFunc ( exportList [ index ] . value ) ;
256+ 'call-export' : /* async */ ( index ) => {
257+ /* await */ callFunc ( exportList [ index ] . value ) ;
248258 } ,
249- 'call-export-catch' : ( index ) => {
250- return tryCall ( ( ) => callFunc ( exportList [ index ] . value ) ) ;
259+ 'call-export-catch' : /* async */ ( index ) => {
260+ return tryCall ( /* async */ ( ) => /* await */ callFunc ( exportList [ index ] . value ) ) ;
251261 } ,
252262
253263 // Funcref operations.
254- 'call-ref' : ( ref ) => {
255- callFunc ( ref ) ;
264+ 'call-ref' : /* async */ ( ref ) => {
265+ // This is a direct function reference, and just like an export, it must
266+ // be wrapped for JSPI.
267+ ref = wrapExportForJSPI ( ref ) ;
268+ /* await */ callFunc ( ref ) ;
269+ } ,
270+ 'call-ref-catch' : /* async */ ( ref ) => {
271+ ref = wrapExportForJSPI ( ref ) ;
272+ return tryCall ( /* async */ ( ) => /* await */ callFunc ( ref ) ) ;
256273 } ,
257- 'call-ref-catch' : ( ref ) => {
258- return tryCall ( ( ) => callFunc ( ref ) ) ;
274+
275+ // Sleep a given amount of ms (when JSPI) and return a given id after that.
276+ 'sleep' : ( ms , id ) => {
277+ if ( ! JSPI ) {
278+ return id ;
279+ }
280+ return new Promise ( ( resolve , reject ) => {
281+ setTimeout ( ( ) => {
282+ resolve ( id ) ;
283+ } , 0 ) ; // TODO: Use the ms in some reasonable, deterministic manner.
284+ // Rather than actually setTimeout on them we could manage
285+ // a queue of pending sleeps manually, and order them based
286+ // on the "ms" (which would not be literal ms, but just
287+ // how many time units to wait).
288+ } ) ;
259289 } ,
260290 } ,
261291 // Emscripten support.
@@ -274,6 +304,22 @@ if (typeof WebAssembly.Tag !== 'undefined') {
274304 } ;
275305}
276306
307+ // If JSPI is available, wrap the imports and exports.
308+ if ( JSPI ) {
309+ for ( var name of [ 'sleep' , 'call-export' , 'call-export-catch' , 'call-ref' ,
310+ 'call-ref-catch' ] ) {
311+ imports [ 'fuzzing-support' ] [ name ] =
312+ new WebAssembly . Suspending ( imports [ 'fuzzing-support' ] [ name ] ) ;
313+ }
314+ }
315+
316+ function wrapExportForJSPI ( value ) {
317+ if ( JSPI && typeof value === 'function' ) {
318+ value = WebAssembly . promising ( value ) ;
319+ }
320+ return value ;
321+ }
322+
277323// If a second binary will be linked in then set up the imports for
278324// placeholders. Any import like (import "placeholder" "0" (func .. will be
279325// provided by the secondary module, and must be called using an indirection.
@@ -312,13 +358,14 @@ function build(binary) {
312358 // keep the ability to call anything that was ever exported.)
313359 for ( var key in instance . exports ) {
314360 var value = instance . exports [ key ] ;
361+ value = wrapExportForJSPI ( value ) ;
315362 exports [ key ] = value ;
316363 exportList . push ( { name : key , value : value } ) ;
317364 }
318365}
319366
320367// Run the code by calling exports.
321- function callExports ( ) {
368+ /* async */ function callExports ( ) {
322369 // Call the exports we were told, or if we were not given an explicit list,
323370 // call them all.
324371 var relevantExports = exportsToCall || exportList ;
@@ -342,7 +389,7 @@ function callExports() {
342389
343390 try {
344391 console . log ( '[fuzz-exec] calling ' + name ) ;
345- var result = callFunc ( value ) ;
392+ var result = /* await */ callFunc ( value ) ;
346393 if ( typeof result !== 'undefined' ) {
347394 console . log ( '[fuzz-exec] note result: ' + name + ' => ' + printed ( result ) ) ;
348395 }
0 commit comments