@@ -224,6 +224,34 @@ function toAddressType(table, index) {
224224 return index ;
225225}
226226
227+ // Simple deterministic hashing, on an unsigned 32-bit seed. See e.g.
228+ // https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine
229+ var hashSeed ;
230+
231+ function hasHashSeed ( ) {
232+ return hashSeed !== undefined ;
233+ }
234+
235+ function hashCombine ( value ) {
236+ // hashSeed must be set before we do anything.
237+ assert ( hasHashSeed ( ) ) ;
238+
239+ hashSeed ^= value + 0x9e3779b9 + ( hashSeed << 6 ) + ( hashSeed >>> 2 ) ;
240+ return hashSeed >>> 0 ;
241+ }
242+
243+ // Get a random 32-bit number. This is like hashCombine but does not take a
244+ // parameter.
245+ function randomBits ( ) {
246+ return hashCombine ( - 1 ) ;
247+ }
248+
249+ // Return true with probability 1 in n. E.g. oneIn(3) returns false 2/3 of the
250+ // time, and true 1/3 of the time.
251+ function oneIn ( n ) {
252+ return ( randomBits ( ) % n ) == 0 ;
253+ }
254+
227255// Set up the imports.
228256var tempRet0 ;
229257var imports = {
@@ -274,7 +302,10 @@ var imports = {
274302
275303 // Sleep a given amount of ms (when JSPI) and return a given id after that.
276304 'sleep' : ( ms , id ) => {
277- if ( ! JSPI ) {
305+ // Also avoid sleeping even in JSPI mode, rarely, just to add variety
306+ // here. Only do this when we have a hash seed, that is, when we are
307+ // allowing randomness.
308+ if ( ! JSPI || ( hasHashSeed ( ) && oneIn ( 10 ) ) ) {
278309 return id ;
279310 }
280311 return new Promise ( ( resolve , reject ) => {
@@ -371,20 +402,15 @@ function build(binary) {
371402 }
372403}
373404
374- // Simple deterministic hashing, on an unsigned 32-bit seed. See e.g.
375- // https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine
376- function hashCombine ( seed , value ) {
377- seed ^= value + 0x9e3779b9 + ( seed << 6 ) + ( seed >>> 2 ) ;
378- return seed >>> 0 ;
379- }
380-
381405// Run the code by calling exports. The optional |ordering| parameter indicates
382406// howe we should order the calls to the exports: if it is not provided, we call
383407// them in the natural order, which allows our output to be compared to other
384408// executions of the wasm (e.g. from wasm-opt --fuzz-exec). If |ordering| is
385409// provided, it is a random seed we use to make deterministic choices on
386410// the order of calls.
387411/* async */ function callExports ( ordering ) {
412+ hashSeed = ordering ;
413+
388414 // Call the exports we were told, or if we were not given an explicit list,
389415 // call them all.
390416 let relevantExports = exportsToCall || exportList ;
@@ -425,13 +451,12 @@ function hashCombine(seed, value) {
425451 task = tasks . pop ( ) ;
426452 } else {
427453 // Pick a random task.
428- ordering = hashCombine ( ordering , tasks . length ) ;
429- let i = ordering % tasks . length ;
454+ let i = hashCombine ( tasks . length ) % tasks . length ;
430455 task = tasks . splice ( i , 1 ) [ 0 ] ;
431456 }
432457
433458 // Execute the task.
434- console . log ( ' [fuzz-exec] calling ' + task . name ) ;
459+ console . log ( ` [fuzz-exec] calling ${ task . name } ${ task . deferred ? ' (after defer)' : '' } ` ) ;
435460 let result ;
436461 try {
437462 result = task . func ( ) ;
@@ -451,10 +476,7 @@ function hashCombine(seed, value) {
451476 // depending on each other, ensuring certain orders of execution.
452477 if ( ordering !== undefined && ! task . deferred && result &&
453478 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 ) {
479+ if ( randomBits ( ) & 1 ) {
458480 // Defer it for later. Reuse the existing task for simplicity.
459481 console . log ( `(jspi: defer ${ task . name } )` ) ;
460482 task . func = /* async */ ( ) => {
0 commit comments