@@ -387,27 +387,12 @@ function hashCombine(seed, value) {
387387/* async */ function callExports ( ordering ) {
388388 // Call the exports we were told, or if we were not given an explicit list,
389389 // call them all.
390- var relevantExports = exportsToCall || exportList ;
391-
392- if ( ordering !== undefined ) {
393- // Copy the list, and sort it in the simple Fisher-Yates manner.
394- // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
395- relevantExports = relevantExports . slice ( 0 ) ;
396- for ( var i = 0 ; i < relevantExports . length - 1 ; i ++ ) {
397- // Pick the index of the item to place at index |i|.
398- ordering = hashCombine ( ordering , i ) ;
399- // The number of items to pick from begins at the full length, then
400- // decreases with i.
401- var j = i + ( ordering % ( relevantExports . length - i ) ) ;
402- // Swap the item over here.
403- var t = relevantExports [ j ] ;
404- relevantExports [ j ] = relevantExports [ i ] ;
405- relevantExports [ i ] = t ;
406- }
407- }
390+ let relevantExports = exportsToCall || exportList ;
408391
409- for ( var e of relevantExports ) {
410- var name , value ;
392+ // Build the list of call tasks to run, one for each relevant export.
393+ let tasks = [ ] ;
394+ for ( let e of relevantExports ) {
395+ let name , value ;
411396 if ( typeof e === 'string' ) {
412397 // We are given a string name to call. Look it up in the global namespace.
413398 name = e ;
@@ -423,16 +408,78 @@ function hashCombine(seed, value) {
423408 continue ;
424409 }
425410
411+ // A task is a name + a function to call. For an export, the function is
412+ // simply a call of the export.
413+ tasks . push ( { name : name , func : /* async */ ( ) => callFunc ( value ) } ) ;
414+ }
415+
416+ // Reverse the array, so the first task is at the end, for efficient
417+ // popping in the common case.
418+ tasks . reverse ( ) ;
419+
420+ // Execute tasks while they remain.
421+ while ( tasks . length ) {
422+ let task ;
423+ if ( ordering === undefined ) {
424+ // Use the natural order.
425+ task = tasks . pop ( ) ;
426+ } else {
427+ // Pick a random task.
428+ ordering = hashCombine ( ordering , tasks . length ) ;
429+ let i = ordering % tasks . length ;
430+ task = tasks . splice ( i , 1 ) [ 0 ] ;
431+ }
432+
433+ // Execute the task.
434+ console . log ( '[fuzz-exec] calling ' + task . name ) ;
435+ let result ;
426436 try {
427- console . log ( '[fuzz-exec] calling ' + name ) ;
428- // TODO: Based on |ordering|, do not always await, leaving a promise
429- // for later, so we interleave stacks.
430- var result = /* await */ callFunc ( value ) ;
431- if ( typeof result !== 'undefined' ) {
432- console . log ( '[fuzz-exec] note result: ' + name + ' => ' + printed ( result ) ) ;
433- }
437+ result = task . func ( ) ;
434438 } catch ( e ) {
435439 console . log ( 'exception thrown: ' + e ) ;
440+ continue ;
441+ }
442+
443+ if ( JSPI ) {
444+ // When we are changing up the order, in JSPI we can also leave some
445+ // promises unresolved until later, which lets us interleave them. Note we
446+ // never defer a task more than once, and we only defer a promise (which
447+ // we check for using .then).
448+ // TODO: Deferring more than once may make sense, by chaining promises in
449+ // JS (that would not add wasm execution in the middle, but might
450+ // find JS issues in principle). We could also link promises by
451+ // depending on each other, ensuring certain orders of execution.
452+ if ( ordering !== undefined && ! task . deferred && result &&
453+ typeof result == 'object' && typeof result . then === 'function' ) {
454+ // Hash with -1 here, just to get something different than the hashing a
455+ // few lines above.
456+ ordering = hashCombine ( ordering , - 1 ) ;
457+ if ( ordering & 1 ) {
458+ // Defer it for later. Reuse the existing task for simplicity.
459+ console . log ( `(jspi: defer ${ task . name } )` ) ;
460+ task . func = /* async */ ( ) => {
461+ console . log ( `(jspi: finish ${ task . name } )` ) ;
462+ return /* await */ result ;
463+ } ;
464+ task . deferred = true ;
465+ tasks . push ( task ) ;
466+ continue ;
467+ }
468+ // Otherwise, continue down.
469+ }
470+
471+ // Await it right now.
472+ try {
473+ result = /* await */ result ;
474+ } catch ( e ) {
475+ console . log ( 'exception thrown: ' + e ) ;
476+ continue ;
477+ }
478+ }
479+
480+ // Log the result.
481+ if ( typeof result !== 'undefined' ) {
482+ console . log ( '[fuzz-exec] note result: ' + task . name + ' => ' + printed ( result ) ) ;
436483 }
437484 }
438485}
0 commit comments