Skip to content

Commit cfbdff7

Browse files
Giovanni Bucciaduh95
authored andcommitted
assert: make partialDeepStrictEqual work with ArrayBuffers
Fixes: #56097 PR-URL: #56098 Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 5620b2b commit cfbdff7

File tree

3 files changed

+353
-79
lines changed

3 files changed

+353
-79
lines changed

‎lib/assert.js‎

Lines changed: 179 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -21,35 +21,44 @@
2121
'use strict';
2222

2323
const{
24+
ArrayBufferIsView,
25+
ArrayBufferPrototypeGetByteLength,
2426
ArrayFrom,
2527
ArrayIsArray,
2628
ArrayPrototypeIndexOf,
2729
ArrayPrototypeJoin,
2830
ArrayPrototypePush,
2931
ArrayPrototypeSlice,
32+
DataViewPrototypeGetBuffer,
33+
DataViewPrototypeGetByteLength,
34+
DataViewPrototypeGetByteOffset,
3035
Error,
3136
FunctionPrototypeCall,
32-
MapPrototypeDelete,
3337
MapPrototypeGet,
38+
MapPrototypeGetSize,
3439
MapPrototypeHas,
35-
MapPrototypeSet,
3640
NumberIsNaN,
3741
ObjectAssign,
3842
ObjectIs,
3943
ObjectKeys,
4044
ObjectPrototypeIsPrototypeOf,
45+
ObjectPrototypeToString,
4146
ReflectApply,
4247
ReflectHas,
4348
ReflectOwnKeys,
4449
RegExpPrototypeExec,
50+
SafeArrayIterator,
4551
SafeMap,
4652
SafeSet,
4753
SafeWeakSet,
54+
SetPrototypeGetSize,
4855
String,
4956
StringPrototypeIndexOf,
5057
StringPrototypeSlice,
5158
StringPrototypeSplit,
5259
SymbolIterator,
60+
TypedArrayPrototypeGetLength,
61+
Uint8Array,
5362
}=primordials;
5463

5564
const{
@@ -65,6 +74,8 @@ const AssertionError = require('internal/assert/assertion_error');
6574
const{ inspect }=require('internal/util/inspect');
6675
const{ Buffer }=require('buffer');
6776
const{
77+
isArrayBuffer,
78+
isDataView,
6879
isKeyObject,
6980
isPromise,
7081
isRegExp,
@@ -73,6 +84,8 @@ const{
7384
isDate,
7485
isWeakSet,
7586
isWeakMap,
87+
isSharedArrayBuffer,
88+
isAnyArrayBuffer,
7689
}=require('internal/util/types');
7790
const{ isError, deprecate, emitExperimentalWarning }=require('internal/util');
7891
const{ innerOk }=require('internal/assert/utils');
@@ -369,9 +382,161 @@ function isSpecial(obj){
369382
}
370383

371384
consttypesToCallDeepStrictEqualWith=[
372-
isKeyObject,isWeakSet,isWeakMap,Buffer.isBuffer,
385+
isKeyObject,isWeakSet,isWeakMap,Buffer.isBuffer,isSharedArrayBuffer,
373386
];
374387

388+
functioncompareMaps(actual,expected,comparedObjects){
389+
if(MapPrototypeGetSize(actual)!==MapPrototypeGetSize(expected)){
390+
returnfalse;
391+
}
392+
constsafeIterator=FunctionPrototypeCall(SafeMap.prototype[SymbolIterator],actual);
393+
394+
comparedObjects??=newSafeWeakSet();
395+
396+
for(const{0: key,1: val}ofsafeIterator){
397+
if(!MapPrototypeHas(expected,key)){
398+
returnfalse;
399+
}
400+
if(!compareBranch(val,MapPrototypeGet(expected,key),comparedObjects)){
401+
returnfalse;
402+
}
403+
}
404+
returntrue;
405+
}
406+
407+
functionpartiallyCompareArrayBuffersOrViews(actual,expected){
408+
letactualView,expectedView,expectedViewLength;
409+
410+
if(!ArrayBufferIsView(actual)){
411+
letactualViewLength;
412+
413+
if(isArrayBuffer(actual)&&isArrayBuffer(expected)){
414+
actualViewLength=ArrayBufferPrototypeGetByteLength(actual);
415+
expectedViewLength=ArrayBufferPrototypeGetByteLength(expected);
416+
}elseif(isSharedArrayBuffer(actual)&&isSharedArrayBuffer(expected)){
417+
actualViewLength=actual.byteLength;
418+
expectedViewLength=expected.byteLength;
419+
}else{
420+
// Cannot compare ArrayBuffers with SharedArrayBuffers
421+
returnfalse;
422+
}
423+
424+
if(expectedViewLength>actualViewLength){
425+
returnfalse;
426+
}
427+
actualView=newUint8Array(actual);
428+
expectedView=newUint8Array(expected);
429+
430+
}elseif(isDataView(actual)){
431+
if(!isDataView(expected)){
432+
returnfalse;
433+
}
434+
constactualByteLength=DataViewPrototypeGetByteLength(actual);
435+
expectedViewLength=DataViewPrototypeGetByteLength(expected);
436+
if(expectedViewLength>actualByteLength){
437+
returnfalse;
438+
}
439+
440+
actualView=newUint8Array(
441+
DataViewPrototypeGetBuffer(actual),
442+
DataViewPrototypeGetByteOffset(actual),
443+
actualByteLength,
444+
);
445+
expectedView=newUint8Array(
446+
DataViewPrototypeGetBuffer(expected),
447+
DataViewPrototypeGetByteOffset(expected),
448+
expectedViewLength,
449+
);
450+
}else{
451+
if(ObjectPrototypeToString(actual)!==ObjectPrototypeToString(expected)){
452+
returnfalse;
453+
}
454+
actualView=actual;
455+
expectedView=expected;
456+
expectedViewLength=TypedArrayPrototypeGetLength(expected);
457+
458+
if(expectedViewLength>TypedArrayPrototypeGetLength(actual)){
459+
returnfalse;
460+
}
461+
}
462+
463+
for(leti=0;i<expectedViewLength;i++){
464+
if(actualView[i]!==expectedView[i]){
465+
returnfalse;
466+
}
467+
}
468+
469+
returntrue;
470+
}
471+
472+
functionpartiallyCompareSets(actual,expected,comparedObjects){
473+
if(SetPrototypeGetSize(expected)>SetPrototypeGetSize(actual)){
474+
returnfalse;// `expected` can't be a subset if it has more elements
475+
}
476+
477+
if(isDeepEqual===undefined)lazyLoadComparison();
478+
479+
constactualArray=ArrayFrom(FunctionPrototypeCall(SafeSet.prototype[SymbolIterator],actual));
480+
constexpectedIterator=FunctionPrototypeCall(SafeSet.prototype[SymbolIterator],expected);
481+
constusedIndices=newSafeSet();
482+
483+
expectedIteration: for(constexpectedItemofexpectedIterator){
484+
for(letactualIdx=0;actualIdx<actualArray.length;actualIdx++){
485+
if(!usedIndices.has(actualIdx)&&isDeepStrictEqual(actualArray[actualIdx],expectedItem)){
486+
usedIndices.add(actualIdx);
487+
continue expectedIteration;
488+
}
489+
}
490+
returnfalse;
491+
}
492+
493+
returntrue;
494+
}
495+
496+
functionpartiallyCompareArrays(actual,expected,comparedObjects){
497+
if(expected.length>actual.length){
498+
returnfalse;
499+
}
500+
501+
if(isDeepEqual===undefined)lazyLoadComparison();
502+
503+
// Create a map to count occurrences of each element in the expected array
504+
constexpectedCounts=newSafeMap();
505+
for(constexpectedItemofexpected){
506+
letfound=false;
507+
for(const{0: key,1: count}ofexpectedCounts){
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+
constsafeActual=newSafeArrayIterator(actual);
520+
521+
// Create a map to count occurrences of relevant elements in the actual array
522+
for(constactualItemofsafeActual){
523+
for(const{0: key,1: count}ofexpectedCounts){
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+
returnsize===0;
538+
}
539+
375540
/**
376541
* Compares two objects or values recursively to check if they are equal.
377542
* @param{any} actual - The actual value to compare.
@@ -388,22 +553,16 @@ function compareBranch(
388553
){
389554
// Check for Map object equality
390555
if(isMap(actual)&&isMap(expected)){
391-
if(actual.size!==expected.size){
392-
returnfalse;
393-
}
394-
constsafeIterator=FunctionPrototypeCall(SafeMap.prototype[SymbolIterator],actual);
395-
396-
comparedObjects??=newSafeWeakSet();
556+
returncompareMaps(actual,expected,comparedObjects);
557+
}
397558

398-
for(const{0: key,1: val}ofsafeIterator){
399-
if(!MapPrototypeHas(expected,key)){
400-
returnfalse;
401-
}
402-
if(!compareBranch(val,MapPrototypeGet(expected,key),comparedObjects)){
403-
returnfalse;
404-
}
405-
}
406-
returntrue;
559+
if(
560+
ArrayBufferIsView(actual)||
561+
isAnyArrayBuffer(actual)||
562+
ArrayBufferIsView(expected)||
563+
isAnyArrayBuffer(expected)
564+
){
565+
returnpartiallyCompareArrayBuffersOrViews(actual,expected);
407566
}
408567

409568
for(consttypeoftypesToCallDeepStrictEqualWith){
@@ -415,68 +574,12 @@ function compareBranch(
415574

416575
// Check for Set object equality
417576
if(isSet(actual)&&isSet(expected)){
418-
if(expected.size>actual.size){
419-
returnfalse;// `expected` can't be a subset if it has more elements
420-
}
421-
422-
if(isDeepEqual===undefined)lazyLoadComparison();
423-
424-
constactualArray=ArrayFrom(FunctionPrototypeCall(SafeSet.prototype[SymbolIterator],actual));
425-
constexpectedIterator=FunctionPrototypeCall(SafeSet.prototype[SymbolIterator],expected);
426-
constusedIndices=newSafeSet();
427-
428-
expectedIteration: for(constexpectedItemofexpectedIterator){
429-
for(letactualIdx=0;actualIdx<actualArray.length;actualIdx++){
430-
if(!usedIndices.has(actualIdx)&&isDeepStrictEqual(actualArray[actualIdx],expectedItem)){
431-
usedIndices.add(actualIdx);
432-
continue expectedIteration;
433-
}
434-
}
435-
returnfalse;
436-
}
437-
438-
returntrue;
577+
returnpartiallyCompareSets(actual,expected,comparedObjects);
439578
}
440579

441580
// Check if expected array is a subset of actual array
442581
if(ArrayIsArray(actual)&&ArrayIsArray(expected)){
443-
if(expected.length>actual.length){
444-
returnfalse;
445-
}
446-
447-
if(isDeepEqual===undefined)lazyLoadComparison();
448-
449-
// Create a map to count occurrences of each element in the expected array
450-
constexpectedCounts=newSafeMap();
451-
for(constexpectedItemofexpected){
452-
letfound=false;
453-
for(const{0: key,1: count}ofexpectedCounts){
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(constactualItemofactual){
467-
for(const{0: key,1: count}ofexpectedCounts){
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+
returnpartiallyCompareArrays(actual,expected,comparedObjects);
480583
}
481584

482585
// Comparison done when at least one of the values is not an object

0 commit comments

Comments
(0)