@@ -41,6 +41,7 @@ const {
4141 StringPrototypeValueOf,
4242 Symbol,
4343 SymbolPrototypeValueOf,
44+ SymbolToStringTag,
4445 TypedArrayPrototypeGetByteLength : getByteLength ,
4546 TypedArrayPrototypeGetSymbolToStringTag,
4647 Uint16Array,
@@ -264,6 +265,17 @@ function innerDeepEqual(val1, val2, mode, memos) {
264265 return objectComparisonStart ( val1 , val2 , mode , memos ) ;
265266}
266267
268+ function hasUnequalTag ( val1 , val2 ) {
269+ return val1 [ SymbolToStringTag ] !== val2 [ SymbolToStringTag ] ;
270+ }
271+
272+ function slowHasUnequalTag ( val1Tag , val1 , val2 ) {
273+ if ( val1 [ SymbolToStringTag ] ) {
274+ return val1 [ SymbolToStringTag ] !== val2 [ SymbolToStringTag ] ;
275+ }
276+ return val1Tag !== ObjectPrototypeToString ( val2 ) ;
277+ }
278+
267279function objectComparisonStart ( val1 , val2 , mode , memos ) {
268280 if ( mode === kStrict ) {
269281 if ( wellKnownConstructors . has ( val1 . constructor ) ||
@@ -276,16 +288,10 @@ function objectComparisonStart(val1, val2, mode, memos) {
276288 }
277289 }
278290
279- const val1Tag = ObjectPrototypeToString ( val1 ) ;
280- const val2Tag = ObjectPrototypeToString ( val2 ) ;
281-
282- if ( val1Tag !== val2Tag ) {
283- return false ;
284- }
285-
286291 if ( ArrayIsArray ( val1 ) ) {
287292 if ( ! ArrayIsArray ( val2 ) ||
288- ( val1 . length !== val2 . length && ( mode !== kPartial || val1 . length < val2 . length ) ) ) {
293+ ( val1 . length !== val2 . length && ( mode !== kPartial || val1 . length < val2 . length ) ) ||
294+ hasUnequalTag ( val1 , val2 ) ) {
289295 return false ;
290296 }
291297
@@ -296,124 +302,136 @@ function objectComparisonStart(val1, val2, mode, memos) {
296302 return false ;
297303 }
298304 return keyCheck ( val1 , val2 , mode , memos , kIsArray , keys2 ) ;
299- } else if ( val1Tag === '[object Object]' ) {
300- return keyCheck ( val1 , val2 , mode , memos , kNoIterator ) ;
301- } else if ( isDate ( val1 ) ) {
302- if ( ! isDate ( val2 ) ) {
303- return false ;
304- }
305- const time1 = DatePrototypeGetTime ( val1 ) ;
306- const time2 = DatePrototypeGetTime ( val2 ) ;
307- // eslint-disable-next-line no-self-compare
308- if ( time1 !== time2 && ( time1 === time1 || time2 === time2 ) ) {
309- return false ;
310- }
311- } else if ( isRegExp ( val1 ) ) {
312- if ( ! isRegExp ( val2 ) || ! areSimilarRegExps ( val1 , val2 ) ) {
313- return false ;
314- }
315- } else if ( isArrayBufferView ( val1 ) ) {
316- if ( TypedArrayPrototypeGetSymbolToStringTag ( val1 ) !==
317- TypedArrayPrototypeGetSymbolToStringTag ( val2 ) ) {
318- return false ;
319- }
320- if ( mode === kPartial && val1 . byteLength !== val2 . byteLength ) {
321- if ( ! isPartialArrayBufferView ( val1 , val2 ) ) {
305+ } else {
306+ let val1Tag ;
307+ if ( val1 [ SymbolToStringTag ] === undefined &&
308+ ( val1Tag = ObjectPrototypeToString ( val1 ) ) === '[object Object]' ) {
309+ if ( slowHasUnequalTag ( val1Tag , val1 , val2 ) ) {
322310 return false ;
323311 }
324- } else if ( mode === kLoose &&
325- ( isFloat32Array ( val1 ) || isFloat64Array ( val1 ) || isFloat16Array ( val1 ) ) ) {
326- if ( ! areSimilarFloatArrays ( val1 , val2 ) ) {
312+ return keyCheck ( val1 , val2 , mode , memos , kNoIterator ) ;
313+ } else if ( isSet ( val1 ) ) {
314+ if ( ! isSet ( val2 ) ||
315+ ( val1 . size !== val2 . size && ( mode !== kPartial || val1 . size < val2 . size ) ) ||
316+ hasUnequalTag ( val1 , val2 ) ) {
327317 return false ;
328318 }
329- } else if ( ! areSimilarTypedArrays ( val1 , val2 ) ) {
330- return false ;
331- }
332- // Buffer.compare returns true, so val1.length === val2.length. If they both
333- // only contain numeric keys, we don't need to exam further than checking
334- // the symbols.
335- const filter = mode !== kLoose ? ONLY_ENUMERABLE : ONLY_ENUMERABLE | SKIP_SYMBOLS ;
336- const keys2 = getOwnNonIndexProperties ( val2 , filter ) ;
337- if ( mode !== kPartial &&
338- keys2 . length !== getOwnNonIndexProperties ( val1 , filter ) . length ) {
339- return false ;
340- }
341- return keyCheck ( val1 , val2 , mode , memos , kNoIterator , keys2 ) ;
342- } else if ( isSet ( val1 ) ) {
343- if ( ! isSet ( val2 ) ||
344- ( val1 . size !== val2 . size && ( mode !== kPartial || val1 . size < val2 . size ) ) ) {
345- return false ;
346- }
347- return keyCheck ( val1 , val2 , mode , memos , kIsSet ) ;
348- } else if ( isMap ( val1 ) ) {
349- if ( ! isMap ( val2 ) ||
350- ( val1 . size !== val2 . size && ( mode !== kPartial || val1 . size < val2 . size ) ) ) {
351- return false ;
352- }
353- return keyCheck ( val1 , val2 , mode , memos , kIsMap ) ;
354- } else if ( isAnyArrayBuffer ( val1 ) ) {
355- if ( ! isAnyArrayBuffer ( val2 ) ) {
356- return false ;
357- }
358- if ( mode !== kPartial || val1 . byteLength === val2 . byteLength ) {
359- if ( ! areEqualArrayBuffers ( val1 , val2 ) ) {
319+ return keyCheck ( val1 , val2 , mode , memos , kIsSet ) ;
320+ } else if ( isMap ( val1 ) ) {
321+ if ( ! isMap ( val2 ) ||
322+ ( val1 . size !== val2 . size && ( mode !== kPartial || val1 . size < val2 . size ) ) ||
323+ hasUnequalTag ( val1 , val2 ) ) {
360324 return false ;
361325 }
362- } else if ( ! isPartialUint8Array ( new Uint8Array ( val1 ) , new Uint8Array ( val2 ) ) ) {
363- return false ;
364- }
365- } else if ( isError ( val1 ) ) {
366- // Do not compare the stack as it might differ even though the error itself
367- // is otherwise identical.
368- if ( ! isError ( val2 ) ||
369- ! isEnumerableOrIdentical ( val1 , val2 , 'message' , mode , memos ) ||
370- ! isEnumerableOrIdentical ( val1 , val2 , 'name' , mode , memos ) ||
371- ! isEnumerableOrIdentical ( val1 , val2 , 'cause' , mode , memos ) ||
372- ! isEnumerableOrIdentical ( val1 , val2 , 'errors' , mode , memos ) ) {
373- return false ;
374- }
375- const hasOwnVal2Cause = hasOwn ( val2 , 'cause' ) ;
376- if ( ( hasOwnVal2Cause !== hasOwn ( val1 , 'cause' ) && ( mode !== kPartial || hasOwnVal2Cause ) ) ) {
377- return false ;
378- }
379- } else if ( isBoxedPrimitive ( val1 ) ) {
380- if ( ! isEqualBoxedPrimitive ( val1 , val2 ) ) {
381- return false ;
382- }
383- } else if ( ArrayIsArray ( val2 ) ||
384- isArrayBufferView ( val2 ) ||
385- isSet ( val2 ) ||
386- isMap ( val2 ) ||
387- isDate ( val2 ) ||
388- isRegExp ( val2 ) ||
389- isAnyArrayBuffer ( val2 ) ||
390- isBoxedPrimitive ( val2 ) ||
391- isNativeError ( val2 ) ||
392- val2 instanceof Error ) {
393- return false ;
394- } else if ( isURL ( val1 ) ) {
395- if ( ! isURL ( val2 ) || val1 . href !== val2 . href ) {
396- return false ;
397- }
398- } else if ( isKeyObject ( val1 ) ) {
399- if ( ! isKeyObject ( val2 ) || ! val1 . equals ( val2 ) ) {
326+ return keyCheck ( val1 , val2 , mode , memos , kIsMap ) ;
327+ } else if ( isArrayBufferView ( val1 ) ) {
328+ if ( TypedArrayPrototypeGetSymbolToStringTag ( val1 ) !==
329+ TypedArrayPrototypeGetSymbolToStringTag ( val2 ) ) {
330+ return false ;
331+ }
332+ if ( mode === kPartial && val1 . byteLength !== val2 . byteLength ) {
333+ if ( ! isPartialArrayBufferView ( val1 , val2 ) ) {
334+ return false ;
335+ }
336+ } else if ( mode === kLoose &&
337+ ( isFloat32Array ( val1 ) || isFloat64Array ( val1 ) || isFloat16Array ( val1 ) ) ) {
338+ if ( ! areSimilarFloatArrays ( val1 , val2 ) ) {
339+ return false ;
340+ }
341+ } else if ( ! areSimilarTypedArrays ( val1 , val2 ) ) {
342+ return false ;
343+ }
344+ // Buffer.compare returns true, so val1.length === val2.length. If they both
345+ // only contain numeric keys, we don't need to exam further than checking
346+ // the symbols.
347+ const filter = mode !== kLoose ? ONLY_ENUMERABLE : ONLY_ENUMERABLE | SKIP_SYMBOLS ;
348+ const keys2 = getOwnNonIndexProperties ( val2 , filter ) ;
349+ if ( mode !== kPartial &&
350+ keys2 . length !== getOwnNonIndexProperties ( val1 , filter ) . length ) {
351+ return false ;
352+ }
353+ return keyCheck ( val1 , val2 , mode , memos , kNoIterator , keys2 ) ;
354+ } else if ( isDate ( val1 ) ) {
355+ if ( ! isDate ( val2 ) ) {
356+ return false ;
357+ }
358+ const time1 = DatePrototypeGetTime ( val1 ) ;
359+ const time2 = DatePrototypeGetTime ( val2 ) ;
360+ // eslint-disable-next-line no-self-compare
361+ if ( time1 !== time2 && ( time1 === time1 || time2 === time2 ) ) {
362+ return false ;
363+ }
364+ } else if ( isRegExp ( val1 ) ) {
365+ if ( ! isRegExp ( val2 ) || ! areSimilarRegExps ( val1 , val2 ) || hasUnequalTag ( val1 , val2 ) ) {
366+ return false ;
367+ }
368+ } else if ( isAnyArrayBuffer ( val1 ) ) {
369+ if ( ! isAnyArrayBuffer ( val2 ) || hasUnequalTag ( val1 , val2 ) ) {
370+ return false ;
371+ }
372+ if ( mode !== kPartial || val1 . byteLength === val2 . byteLength ) {
373+ if ( ! areEqualArrayBuffers ( val1 , val2 ) ) {
374+ return false ;
375+ }
376+ } else if ( ! isPartialUint8Array ( new Uint8Array ( val1 ) , new Uint8Array ( val2 ) ) ) {
377+ return false ;
378+ }
379+ } else if ( slowHasUnequalTag ( val1Tag ?? ObjectPrototypeToString ( val1 ) , val1 , val2 ) ||
380+ ArrayIsArray ( val2 ) ||
381+ isArrayBufferView ( val2 ) ||
382+ isSet ( val2 ) ||
383+ isMap ( val2 ) ||
384+ isDate ( val2 ) ||
385+ isRegExp ( val2 ) ||
386+ isAnyArrayBuffer ( val2 ) ) {
400387 return false ;
401- }
402- } else if ( isCryptoKey ( val1 ) ) {
403- if ( kKeyObject === undefined ) {
404- kKeyObject = require ( 'internal/crypto/util' ) . kKeyObject ;
405- ( { kExtractable, kAlgorithm, kKeyUsages } = require ( 'internal/crypto/keys' ) ) ;
406- }
407- if ( ! isCryptoKey ( val2 ) ||
408- val1 [ kExtractable ] !== val2 [ kExtractable ] ||
409- ! innerDeepEqual ( val1 [ kAlgorithm ] , val2 [ kAlgorithm ] , mode , memos ) ||
410- ! innerDeepEqual ( val1 [ kKeyUsages ] , val2 [ kKeyUsages ] , mode , memos ) ||
411- ! innerDeepEqual ( val1 [ kKeyObject ] , val2 [ kKeyObject ] , mode , memos )
412- ) {
388+ } else if ( isError ( val1 ) ) {
389+ // Do not compare the stack as it might differ even though the error itself
390+ // is otherwise identical.
391+ if ( ! isError ( val2 ) ||
392+ ! isEnumerableOrIdentical ( val1 , val2 , 'message' , mode , memos ) ||
393+ ! isEnumerableOrIdentical ( val1 , val2 , 'name' , mode , memos ) ||
394+ ! isEnumerableOrIdentical ( val1 , val2 , 'cause' , mode , memos ) ||
395+ ! isEnumerableOrIdentical ( val1 , val2 , 'errors' , mode , memos ) ) {
396+ return false ;
397+ }
398+ const hasOwnVal2Cause = hasOwn ( val2 , 'cause' ) ;
399+ if ( ( hasOwnVal2Cause !== hasOwn ( val1 , 'cause' ) && ( mode !== kPartial || hasOwnVal2Cause ) ) ) {
400+ return false ;
401+ }
402+ } else if ( isBoxedPrimitive ( val1 ) ) {
403+ if ( ! isEqualBoxedPrimitive ( val1 , val2 ) ) {
404+ return false ;
405+ }
406+ } else if ( isURL ( val1 ) ) {
407+ if ( ! isURL ( val2 ) || val1 . href !== val2 . href ) {
408+ return false ;
409+ }
410+ } else if ( isKeyObject ( val1 ) ) {
411+ if ( ! isKeyObject ( val2 ) || ! val1 . equals ( val2 ) ) {
412+ return false ;
413+ }
414+ } else if ( isCryptoKey ( val1 ) ) {
415+ if ( kKeyObject === undefined ) {
416+ kKeyObject = require ( 'internal/crypto/util' ) . kKeyObject ;
417+ ( { kExtractable, kAlgorithm, kKeyUsages } = require ( 'internal/crypto/keys' ) ) ;
418+ }
419+ if ( ! isCryptoKey ( val2 ) ||
420+ val1 [ kExtractable ] !== val2 [ kExtractable ] ||
421+ ! innerDeepEqual ( val1 [ kAlgorithm ] , val2 [ kAlgorithm ] , mode , memos ) ||
422+ ! innerDeepEqual ( val1 [ kKeyUsages ] , val2 [ kKeyUsages ] , mode , memos ) ||
423+ ! innerDeepEqual ( val1 [ kKeyObject ] , val2 [ kKeyObject ] , mode , memos )
424+ ) {
425+ return false ;
426+ }
427+ } else if ( isBoxedPrimitive ( val2 ) ||
428+ isNativeError ( val2 ) ||
429+ val2 instanceof Error ||
430+ isWeakMap ( val1 ) ||
431+ isWeakSet ( val1 ) ||
432+ isPromise ( val1 ) ) {
413433 return false ;
414434 }
415- } else if ( isWeakMap ( val1 ) || isWeakSet ( val1 ) || isPromise ( val1 ) ) {
416- return false ;
417435 }
418436
419437 return keyCheck ( val1 , val2 , mode , memos , kNoIterator ) ;
@@ -879,18 +897,16 @@ function partialSparseArrayEquiv(a, b, mode, memos, startA, startB) {
879897 let aPos = startA ;
880898 const keysA = ObjectKeys ( a ) ;
881899 const keysB = ObjectKeys ( b ) ;
882- const keysBLength = keysB . length ;
883- const keysALength = keysA . length ;
884- const lenA = keysALength - startA ;
885- const lenB = keysBLength - startB ;
900+ const lenA = keysA . length - startA ;
901+ const lenB = keysB . length - startB ;
886902 if ( lenA < lenB ) {
887903 return false ;
888904 }
889905 for ( let i = 0 ; i < lenB ; i ++ ) {
890906 const keyB = keysB [ startB + i ] ;
891907 while ( ! innerDeepEqual ( a [ keysA [ aPos ] ] , b [ keyB ] , mode , memos ) ) {
892908 aPos ++ ;
893- if ( aPos > keysALength - lenB + i ) {
909+ if ( aPos > keysA . length - lenB + i ) {
894910 return false ;
895911 }
896912 }
@@ -922,8 +938,6 @@ function partialArrayEquiv(a, b, mode, memos) {
922938}
923939
924940function sparseArrayEquiv ( a , b , mode , memos , i ) {
925- // TODO(BridgeAR): Use internal method to only get index properties. The
926- // same applies to the partial implementation.
927941 const keysA = ObjectKeys ( a ) ;
928942 const keysB = ObjectKeys ( b ) ;
929943 if ( keysA . length !== keysB . length ) {
0 commit comments