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