2323const {
2424 ArrayBufferIsView,
2525 ArrayBufferPrototypeGetByteLength,
26- ArrayFrom,
2726 ArrayIsArray,
2827 ArrayPrototypeIndexOf,
2928 ArrayPrototypeJoin,
@@ -395,12 +394,11 @@ function partiallyCompareMaps(actual, expected, comparedObjects) {
395394 const expectedIterator = FunctionPrototypeCall ( SafeMap . prototype [ SymbolIterator ] , expected ) ;
396395
397396 for ( const { 0 : key , 1 : expectedValue } of expectedIterator ) {
398- if ( ! MapPrototypeHas ( actual , key ) ) {
397+ const actualValue = MapPrototypeGet ( actual , key ) ;
398+ if ( actualValue === undefined && ! MapPrototypeHas ( actual , key ) ) {
399399 return false ;
400400 }
401401
402- const actualValue = MapPrototypeGet ( actual , key ) ;
403-
404402 if ( ! compareBranch ( actualValue , expectedValue , comparedObjects ) ) {
405403 return false ;
406404 }
@@ -474,28 +472,74 @@ function partiallyCompareArrayBuffersOrViews(actual, expected) {
474472 return true ;
475473}
476474
475+ // Adapted version of the "setEquiv" function in lib/internal/util/comparisons.js
477476function partiallyCompareSets ( actual , expected , comparedObjects ) {
478477 if ( SetPrototypeGetSize ( expected ) > SetPrototypeGetSize ( actual ) ) {
479- return false ; // `expected` can't be a subset if it has more elements
478+ return false ;
480479 }
481480
482481 if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
482+ let set = null ;
483483
484- const actualArray = ArrayFrom ( FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ) ;
484+ // First, check if elements from expected exist in actual
485485 const expectedIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , expected ) ;
486- const usedIndices = new SafeSet ( ) ;
486+ for ( const val of expectedIterator ) {
487+ // Fast path: direct inclusion check for both primitives and reference equality
488+ if ( actual . has ( val ) ) {
489+ continue ;
490+ }
491+
492+ // For primitives, if not found directly, return false immediately
493+ if ( isPrimitive ( val ) ) {
494+ return false ;
495+ }
487496
488- expectedIteration: for ( const expectedItem of expectedIterator ) {
489- for ( let actualIdx = 0 ; actualIdx < actualArray . length ; actualIdx ++ ) {
490- if ( ! usedIndices . has ( actualIdx ) && isDeepStrictEqual ( actualArray [ actualIdx ] , expectedItem ) ) {
491- usedIndices . add ( actualIdx ) ;
492- continue expectedIteration;
497+ if ( set === null ) {
498+ // Special case to avoid set creation for single-element comparison
499+ if ( SetPrototypeGetSize ( expected ) === 1 ) {
500+ // Try to find any deep-equal object in actual
501+ const actualIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ;
502+ for ( const actualItem of actualIterator ) {
503+ if ( ! isPrimitive ( actualItem ) && isDeepStrictEqual ( actualItem , val ) ) {
504+ return true ;
505+ }
506+ }
507+ return false ;
493508 }
509+ set = new SafeSet ( ) ;
494510 }
495- return false ;
511+
512+ // Add this object for later deep comparison
513+ set . add ( val ) ;
496514 }
497515
498- return true ;
516+ // If all items were found directly, we're done
517+ if ( set === null ) {
518+ return true ;
519+ }
520+
521+ // For remaining objects that need deep comparison
522+ const actualIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ;
523+ for ( const actualItem of actualIterator ) {
524+ // Only consider non-primitive values for deep comparison
525+ if ( ! isPrimitive ( actualItem ) ) {
526+ // Check if this actual item deep-equals any remaining expected item
527+ for ( const expectedItem of set ) {
528+ if ( isDeepStrictEqual ( actualItem , expectedItem ) ) {
529+ // Remove the matched item so we don't match it again
530+ set . delete ( expectedItem ) ;
531+ // If all items are matched, we can return early
532+ if ( set . size === 0 ) {
533+ return true ;
534+ }
535+ break ;
536+ }
537+ }
538+ }
539+ }
540+
541+ // If all objects in expected found matches, set will be empty
542+ return set . size === 0 ;
499543}
500544
501545const minusZeroSymbol = Symbol ( '-0' ) ;
@@ -510,21 +554,26 @@ function getZeroKey(item) {
510554}
511555
512556function partiallyCompareArrays ( actual , expected , comparedObjects ) {
557+ if ( actual === expected ) return true ;
558+
513559 if ( expected . length > actual . length ) {
514560 return false ;
515561 }
516562
563+ if ( expected . length === 0 ) {
564+ return true ;
565+ }
566+
517567 if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
518568
519569 // Create a map to count occurrences of each element in the expected array
520570 const expectedCounts = new SafeMap ( ) ;
521- const safeExpected = new SafeArrayIterator ( expected ) ;
522571
523- for ( const expectedItem of safeExpected ) {
524- // Check if the item is a zero or a -0, as these need to be handled separately
572+ const expectedIterator = new SafeArrayIterator ( expected ) ;
573+ for ( const expectedItem of expectedIterator ) {
525574 if ( expectedItem === 0 ) {
526575 const zeroKey = getZeroKey ( expectedItem ) ;
527- expectedCounts . set ( zeroKey , ( expectedCounts . get ( zeroKey ) ?. count || 0 ) + 1 ) ;
576+ expectedCounts . set ( zeroKey , ( expectedCounts . get ( zeroKey ) ?? 0 ) + 1 ) ;
528577 } else {
529578 let found = false ;
530579 for ( const { 0 : key , 1 : count } of expectedCounts ) {
@@ -540,10 +589,8 @@ function partiallyCompareArrays(actual, expected, comparedObjects) {
540589 }
541590 }
542591
543- const safeActual = new SafeArrayIterator ( actual ) ;
544-
545- for ( const actualItem of safeActual ) {
546- // Check if the item is a zero or a -0, as these need to be handled separately
592+ const actualIterator = new SafeArrayIterator ( actual ) ;
593+ for ( const actualItem of actualIterator ) {
547594 if ( actualItem === 0 ) {
548595 const zeroKey = getZeroKey ( actualItem ) ;
549596
@@ -567,6 +614,10 @@ function partiallyCompareArrays(actual, expected, comparedObjects) {
567614 }
568615 }
569616 }
617+
618+ if ( expectedCounts . size === 0 ) {
619+ return true ;
620+ }
570621 }
571622
572623 return expectedCounts . size === 0 ;
@@ -723,6 +774,10 @@ function compareExceptionKey(actual, expected, key, message, keys, fn) {
723774 }
724775}
725776
777+ function isPrimitive ( value ) {
778+ return typeof value !== 'object' || value === null ;
779+ }
780+
726781function expectedException ( actual , expected , message , fn ) {
727782 let generatedMessage = false ;
728783 let throwError = false ;
@@ -741,7 +796,7 @@ function expectedException(actual, expected, message, fn) {
741796 }
742797 throwError = true ;
743798 // Handle primitives properly.
744- } else if ( typeof actual !== 'object' || actual === null ) {
799+ } else if ( isPrimitive ( actual ) ) {
745800 const err = new AssertionError ( {
746801 actual,
747802 expected,
0 commit comments