21
21
'use strict' ;
22
22
23
23
const {
24
+ ArrayBufferIsView,
25
+ ArrayBufferPrototypeGetByteLength,
24
26
ArrayFrom,
25
27
ArrayIsArray,
26
28
ArrayPrototypeIndexOf,
27
29
ArrayPrototypeJoin,
28
30
ArrayPrototypePush,
29
31
ArrayPrototypeSlice,
32
+ DataViewPrototypeGetBuffer,
33
+ DataViewPrototypeGetByteLength,
34
+ DataViewPrototypeGetByteOffset,
30
35
Error,
31
36
FunctionPrototypeCall,
32
- MapPrototypeDelete,
33
37
MapPrototypeGet,
38
+ MapPrototypeGetSize,
34
39
MapPrototypeHas,
35
- MapPrototypeSet,
36
40
NumberIsNaN,
37
41
ObjectAssign,
38
42
ObjectIs,
39
43
ObjectKeys,
40
44
ObjectPrototypeIsPrototypeOf,
45
+ ObjectPrototypeToString,
41
46
ReflectApply,
42
47
ReflectHas,
43
48
ReflectOwnKeys,
44
49
RegExpPrototypeExec,
50
+ SafeArrayIterator,
45
51
SafeMap,
46
52
SafeSet,
47
53
SafeWeakSet,
54
+ SetPrototypeGetSize,
48
55
String,
49
56
StringPrototypeIndexOf,
50
57
StringPrototypeSlice,
51
58
StringPrototypeSplit,
52
59
SymbolIterator,
60
+ TypedArrayPrototypeGetLength,
61
+ Uint8Array,
53
62
} = primordials ;
54
63
55
64
const {
@@ -65,6 +74,8 @@ const AssertionError = require('internal/assert/assertion_error');
65
74
const { inspect } = require ( 'internal/util/inspect' ) ;
66
75
const { Buffer } = require ( 'buffer' ) ;
67
76
const {
77
+ isArrayBuffer,
78
+ isDataView,
68
79
isKeyObject,
69
80
isPromise,
70
81
isRegExp,
@@ -73,6 +84,8 @@ const {
73
84
isDate,
74
85
isWeakSet,
75
86
isWeakMap,
87
+ isSharedArrayBuffer,
88
+ isAnyArrayBuffer,
76
89
} = require ( 'internal/util/types' ) ;
77
90
const { isError, deprecate, emitExperimentalWarning } = require ( 'internal/util' ) ;
78
91
const { innerOk } = require ( 'internal/assert/utils' ) ;
@@ -369,9 +382,161 @@ function isSpecial(obj) {
369
382
}
370
383
371
384
const typesToCallDeepStrictEqualWith = [
372
- isKeyObject , isWeakSet , isWeakMap , Buffer . isBuffer ,
385
+ isKeyObject , isWeakSet , isWeakMap , Buffer . isBuffer , isSharedArrayBuffer ,
373
386
] ;
374
387
388
+ function compareMaps ( actual , expected , comparedObjects ) {
389
+ if ( MapPrototypeGetSize ( actual ) !== MapPrototypeGetSize ( expected ) ) {
390
+ return false ;
391
+ }
392
+ const safeIterator = FunctionPrototypeCall ( SafeMap . prototype [ SymbolIterator ] , actual ) ;
393
+
394
+ comparedObjects ??= new SafeWeakSet ( ) ;
395
+
396
+ for ( const { 0 : key , 1 : val } of safeIterator ) {
397
+ if ( ! MapPrototypeHas ( expected , key ) ) {
398
+ return false ;
399
+ }
400
+ if ( ! compareBranch ( val , MapPrototypeGet ( expected , key ) , comparedObjects ) ) {
401
+ return false ;
402
+ }
403
+ }
404
+ return true ;
405
+ }
406
+
407
+ function partiallyCompareArrayBuffersOrViews ( actual , expected ) {
408
+ let actualView , expectedView , expectedViewLength ;
409
+
410
+ if ( ! ArrayBufferIsView ( actual ) ) {
411
+ let actualViewLength ;
412
+
413
+ if ( isArrayBuffer ( actual ) && isArrayBuffer ( expected ) ) {
414
+ actualViewLength = ArrayBufferPrototypeGetByteLength ( actual ) ;
415
+ expectedViewLength = ArrayBufferPrototypeGetByteLength ( expected ) ;
416
+ } else if ( isSharedArrayBuffer ( actual ) && isSharedArrayBuffer ( expected ) ) {
417
+ actualViewLength = actual . byteLength ;
418
+ expectedViewLength = expected . byteLength ;
419
+ } else {
420
+ // Cannot compare ArrayBuffers with SharedArrayBuffers
421
+ return false ;
422
+ }
423
+
424
+ if ( expectedViewLength > actualViewLength ) {
425
+ return false ;
426
+ }
427
+ actualView = new Uint8Array ( actual ) ;
428
+ expectedView = new Uint8Array ( expected ) ;
429
+
430
+ } else if ( isDataView ( actual ) ) {
431
+ if ( ! isDataView ( expected ) ) {
432
+ return false ;
433
+ }
434
+ const actualByteLength = DataViewPrototypeGetByteLength ( actual ) ;
435
+ expectedViewLength = DataViewPrototypeGetByteLength ( expected ) ;
436
+ if ( expectedViewLength > actualByteLength ) {
437
+ return false ;
438
+ }
439
+
440
+ actualView = new Uint8Array (
441
+ DataViewPrototypeGetBuffer ( actual ) ,
442
+ DataViewPrototypeGetByteOffset ( actual ) ,
443
+ actualByteLength ,
444
+ ) ;
445
+ expectedView = new Uint8Array (
446
+ DataViewPrototypeGetBuffer ( expected ) ,
447
+ DataViewPrototypeGetByteOffset ( expected ) ,
448
+ expectedViewLength ,
449
+ ) ;
450
+ } else {
451
+ if ( ObjectPrototypeToString ( actual ) !== ObjectPrototypeToString ( expected ) ) {
452
+ return false ;
453
+ }
454
+ actualView = actual ;
455
+ expectedView = expected ;
456
+ expectedViewLength = TypedArrayPrototypeGetLength ( expected ) ;
457
+
458
+ if ( expectedViewLength > TypedArrayPrototypeGetLength ( actual ) ) {
459
+ return false ;
460
+ }
461
+ }
462
+
463
+ for ( let i = 0 ; i < expectedViewLength ; i ++ ) {
464
+ if ( actualView [ i ] !== expectedView [ i ] ) {
465
+ return false ;
466
+ }
467
+ }
468
+
469
+ return true ;
470
+ }
471
+
472
+ function partiallyCompareSets ( actual , expected , comparedObjects ) {
473
+ if ( SetPrototypeGetSize ( expected ) > SetPrototypeGetSize ( actual ) ) {
474
+ return false ; // `expected` can't be a subset if it has more elements
475
+ }
476
+
477
+ if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
478
+
479
+ const actualArray = ArrayFrom ( FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ) ;
480
+ const expectedIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , expected ) ;
481
+ const usedIndices = new SafeSet ( ) ;
482
+
483
+ expectedIteration: for ( const expectedItem of expectedIterator ) {
484
+ for ( let actualIdx = 0 ; actualIdx < actualArray . length ; actualIdx ++ ) {
485
+ if ( ! usedIndices . has ( actualIdx ) && isDeepStrictEqual ( actualArray [ actualIdx ] , expectedItem ) ) {
486
+ usedIndices . add ( actualIdx ) ;
487
+ continue expectedIteration;
488
+ }
489
+ }
490
+ return false ;
491
+ }
492
+
493
+ return true ;
494
+ }
495
+
496
+ function partiallyCompareArrays ( actual , expected , comparedObjects ) {
497
+ if ( expected . length > actual . length ) {
498
+ return false ;
499
+ }
500
+
501
+ if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
502
+
503
+ // Create a map to count occurrences of each element in the expected array
504
+ const expectedCounts = new SafeMap ( ) ;
505
+ for ( const expectedItem of expected ) {
506
+ let found = false ;
507
+ for ( const { 0 : key , 1 : count } of expectedCounts ) {
508
+ if ( isDeepStrictEqual ( key , expectedItem ) ) {
509
+ expectedCounts . set ( key , count + 1 ) ;
510
+ found = true ;
511
+ break ;
512
+ }
513
+ }
514
+ if ( ! found ) {
515
+ expectedCounts . set ( expectedItem , 1 ) ;
516
+ }
517
+ }
518
+
519
+ const safeActual = new SafeArrayIterator ( actual ) ;
520
+
521
+ // Create a map to count occurrences of relevant elements in the actual array
522
+ for ( const actualItem of safeActual ) {
523
+ for ( const { 0 : key , 1 : count } of expectedCounts ) {
524
+ if ( isDeepStrictEqual ( key , actualItem ) ) {
525
+ if ( count === 1 ) {
526
+ expectedCounts . delete ( key ) ;
527
+ } else {
528
+ expectedCounts . set ( key , count - 1 ) ;
529
+ }
530
+ break ;
531
+ }
532
+ }
533
+ }
534
+
535
+ const { size } = expectedCounts ;
536
+ expectedCounts . clear ( ) ;
537
+ return size === 0 ;
538
+ }
539
+
375
540
/**
376
541
* Compares two objects or values recursively to check if they are equal.
377
542
* @param {any } actual - The actual value to compare.
@@ -388,22 +553,16 @@ function compareBranch(
388
553
) {
389
554
// Check for Map object equality
390
555
if ( isMap ( actual ) && isMap ( expected ) ) {
391
- if ( actual . size !== expected . size ) {
392
- return false ;
393
- }
394
- const safeIterator = FunctionPrototypeCall ( SafeMap . prototype [ SymbolIterator ] , actual ) ;
395
-
396
- comparedObjects ??= new SafeWeakSet ( ) ;
556
+ return compareMaps ( actual , expected , comparedObjects ) ;
557
+ }
397
558
398
- for ( const { 0 : key , 1 : val } of safeIterator ) {
399
- if ( ! MapPrototypeHas ( expected , key ) ) {
400
- return false ;
401
- }
402
- if ( ! compareBranch ( val , MapPrototypeGet ( expected , key ) , comparedObjects ) ) {
403
- return false ;
404
- }
405
- }
406
- return true ;
559
+ if (
560
+ ArrayBufferIsView ( actual ) ||
561
+ isAnyArrayBuffer ( actual ) ||
562
+ ArrayBufferIsView ( expected ) ||
563
+ isAnyArrayBuffer ( expected )
564
+ ) {
565
+ return partiallyCompareArrayBuffersOrViews ( actual , expected ) ;
407
566
}
408
567
409
568
for ( const type of typesToCallDeepStrictEqualWith ) {
@@ -415,68 +574,12 @@ function compareBranch(
415
574
416
575
// Check for Set object equality
417
576
if ( isSet ( actual ) && isSet ( expected ) ) {
418
- if ( expected . size > actual . size ) {
419
- return false ; // `expected` can't be a subset if it has more elements
420
- }
421
-
422
- if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
423
-
424
- const actualArray = ArrayFrom ( FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ) ;
425
- const expectedIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , expected ) ;
426
- const usedIndices = new SafeSet ( ) ;
427
-
428
- expectedIteration: for ( const expectedItem of expectedIterator ) {
429
- for ( let actualIdx = 0 ; actualIdx < actualArray . length ; actualIdx ++ ) {
430
- if ( ! usedIndices . has ( actualIdx ) && isDeepStrictEqual ( actualArray [ actualIdx ] , expectedItem ) ) {
431
- usedIndices . add ( actualIdx ) ;
432
- continue expectedIteration;
433
- }
434
- }
435
- return false ;
436
- }
437
-
438
- return true ;
577
+ return partiallyCompareSets ( actual , expected , comparedObjects ) ;
439
578
}
440
579
441
580
// Check if expected array is a subset of actual array
442
581
if ( ArrayIsArray ( actual ) && ArrayIsArray ( expected ) ) {
443
- if ( expected . length > actual . length ) {
444
- return false ;
445
- }
446
-
447
- if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
448
-
449
- // Create a map to count occurrences of each element in the expected array
450
- const expectedCounts = new SafeMap ( ) ;
451
- for ( const expectedItem of expected ) {
452
- let found = false ;
453
- for ( const { 0 : key , 1 : count } of expectedCounts ) {
454
- if ( isDeepStrictEqual ( key , expectedItem ) ) {
455
- MapPrototypeSet ( expectedCounts , key , count + 1 ) ;
456
- found = true ;
457
- break ;
458
- }
459
- }
460
- if ( ! found ) {
461
- MapPrototypeSet ( expectedCounts , expectedItem , 1 ) ;
462
- }
463
- }
464
-
465
- // Create a map to count occurrences of relevant elements in the actual array
466
- for ( const actualItem of actual ) {
467
- for ( const { 0 : key , 1 : count } of expectedCounts ) {
468
- if ( isDeepStrictEqual ( key , actualItem ) ) {
469
- if ( count === 1 ) {
470
- MapPrototypeDelete ( expectedCounts , key ) ;
471
- } else {
472
- MapPrototypeSet ( expectedCounts , key , count - 1 ) ;
473
- }
474
- break ;
475
- }
476
- }
477
- }
478
-
479
- return ! expectedCounts . size ;
582
+ return partiallyCompareArrays ( actual , expected , comparedObjects ) ;
480
583
}
481
584
482
585
// Comparison done when at least one of the values is not an object
0 commit comments