@@ -41,6 +41,7 @@ const {
4141 StringPrototypeValueOf,
4242 Symbol,
4343 SymbolPrototypeValueOf,
44+ SymbolToStringTag,
4445 TypedArrayPrototypeGetByteLength : getByteLength ,
4546 TypedArrayPrototypeGetSymbolToStringTag,
4647 Uint16Array,
@@ -114,6 +115,7 @@ const {
114115 isFloat64Array,
115116 isKeyObject,
116117 isCryptoKey,
118+ isPromise,
117119 isWeakMap,
118120 isWeakSet,
119121} = types ;
@@ -263,6 +265,17 @@ function innerDeepEqual(val1, val2, mode, memos) {
263265 return objectComparisonStart ( val1 , val2 , mode , memos ) ;
264266}
265267
268+ function hasUnequalTag ( val1 , val2 ) {
269+ return val1 [ SymbolToStringTag ] !== val2 [ SymbolToStringTag ] ;
270+ }
271+
272+ function slowHasUnequalTag ( val1Tag , val1 , val2 ) {
273+ if ( val1 [ SymbolToStringTag ] !== undefined && val2 [ SymbolToStringTag ] !== undefined ) {
274+ return val1 [ SymbolToStringTag ] !== val2 [ SymbolToStringTag ] ;
275+ }
276+ return val1Tag !== ObjectPrototypeToString ( val2 ) ;
277+ }
278+
266279function objectComparisonStart ( val1 , val2 , mode , memos ) {
267280 if ( mode === kStrict ) {
268281 if ( wellKnownConstructors . has ( val1 . constructor ) ||
@@ -275,16 +288,10 @@ function objectComparisonStart(val1, val2, mode, memos) {
275288 }
276289 }
277290
278- const val1Tag = ObjectPrototypeToString ( val1 ) ;
279- const val2Tag = ObjectPrototypeToString ( val2 ) ;
280-
281- if ( val1Tag !== val2Tag ) {
282- return false ;
283- }
284-
285291 if ( ArrayIsArray ( val1 ) ) {
286292 if ( ! ArrayIsArray ( val2 ) ||
287- ( 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 ) ) {
288295 return false ;
289296 }
290297
@@ -295,23 +302,29 @@ function objectComparisonStart(val1, val2, mode, memos) {
295302 return false ;
296303 }
297304 return keyCheck ( val1 , val2 , mode , memos , kIsArray , keys2 ) ;
298- } else if ( val1Tag === '[object Object]' ) {
299- return keyCheck ( val1 , val2 , mode , memos , kNoIterator ) ;
300- } else if ( isDate ( val1 ) ) {
301- if ( ! isDate ( val2 ) ||
302- DatePrototypeGetTime ( val1 ) !== DatePrototypeGetTime ( val2 ) ) {
305+ }
306+
307+ let val1Tag ;
308+ if ( val1 [ SymbolToStringTag ] === undefined &&
309+ ( val1Tag = ObjectPrototypeToString ( val1 ) ) === '[object Object]' ) {
310+ if ( slowHasUnequalTag ( val1Tag , val1 , val2 ) ) {
303311 return false ;
304312 }
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 ) ) {
313+ return keyCheck ( val1 , val2 , mode , memos , kNoIterator ) ;
314+ } else if ( isSet ( val1 ) ) {
315+ if ( ! isSet ( val2 ) ||
316+ ( val1 . size !== val2 . size && ( mode !== kPartial || val1 . size < val2 . size ) ) ||
317+ hasUnequalTag ( val1 , val2 ) ) {
309318 return false ;
310319 }
311- } else if ( isRegExp ( val1 ) ) {
312- if ( ! isRegExp ( val2 ) || ! areSimilarRegExps ( val1 , val2 ) ) {
320+ return keyCheck ( val1 , val2 , mode , memos , kIsSet ) ;
321+ } else if ( isMap ( val1 ) ) {
322+ if ( ! isMap ( val2 ) ||
323+ ( val1 . size !== val2 . size && ( mode !== kPartial || val1 . size < val2 . size ) ) ||
324+ hasUnequalTag ( val1 , val2 ) ) {
313325 return false ;
314326 }
327+ return keyCheck ( val1 , val2 , mode , memos , kIsMap ) ;
315328 } else if ( isArrayBufferView ( val1 ) ) {
316329 if ( TypedArrayPrototypeGetSymbolToStringTag ( val1 ) !==
317330 TypedArrayPrototypeGetSymbolToStringTag ( val2 ) ) {
@@ -339,20 +352,22 @@ function objectComparisonStart(val1, val2, mode, memos) {
339352 return false ;
340353 }
341354 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 ) ) ) {
355+ } else if ( isDate ( val1 ) ) {
356+ if ( ! isDate ( val2 ) || hasUnequalTag ( val1 , val2 ) ) {
345357 return false ;
346358 }
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 ) ) ) {
359+ const time1 = DatePrototypeGetTime ( val1 ) ;
360+ const time2 = DatePrototypeGetTime ( val2 ) ;
361+ // eslint-disable-next-line no-self-compare
362+ if ( time1 !== time2 && ( time1 === time1 || time2 === time2 ) ) {
363+ return false ;
364+ }
365+ } else if ( isRegExp ( val1 ) ) {
366+ if ( ! isRegExp ( val2 ) || ! areSimilarRegExps ( val1 , val2 ) || hasUnequalTag ( val1 , val2 ) ) {
351367 return false ;
352368 }
353- return keyCheck ( val1 , val2 , mode , memos , kIsMap ) ;
354369 } else if ( isAnyArrayBuffer ( val1 ) ) {
355- if ( ! isAnyArrayBuffer ( val2 ) ) {
370+ if ( ! isAnyArrayBuffer ( val2 ) || hasUnequalTag ( val1 , val2 ) ) {
356371 return false ;
357372 }
358373 if ( mode !== kPartial || val1 . byteLength === val2 . byteLength ) {
@@ -362,6 +377,15 @@ function objectComparisonStart(val1, val2, mode, memos) {
362377 } else if ( ! isPartialUint8Array ( new Uint8Array ( val1 ) , new Uint8Array ( val2 ) ) ) {
363378 return false ;
364379 }
380+ } else if ( slowHasUnequalTag ( val1Tag ?? ObjectPrototypeToString ( val1 ) , val1 , val2 ) ||
381+ ArrayIsArray ( val2 ) ||
382+ isArrayBufferView ( val2 ) ||
383+ isSet ( val2 ) ||
384+ isMap ( val2 ) ||
385+ isDate ( val2 ) ||
386+ isRegExp ( val2 ) ||
387+ isAnyArrayBuffer ( val2 ) ) {
388+ return false ;
365389 } else if ( isError ( val1 ) ) {
366390 // Do not compare the stack as it might differ even though the error itself
367391 // is otherwise identical.
@@ -380,17 +404,6 @@ function objectComparisonStart(val1, val2, mode, memos) {
380404 if ( ! isEqualBoxedPrimitive ( val1 , val2 ) ) {
381405 return false ;
382406 }
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 ;
394407 } else if ( isURL ( val1 ) ) {
395408 if ( ! isURL ( val2 ) || val1 . href !== val2 . href ) {
396409 return false ;
@@ -412,7 +425,12 @@ function objectComparisonStart(val1, val2, mode, memos) {
412425 ) {
413426 return false ;
414427 }
415- } else if ( isWeakMap ( val1 ) || isWeakSet ( val1 ) ) {
428+ } else if ( isBoxedPrimitive ( val2 ) ||
429+ isNativeError ( val2 ) ||
430+ val2 instanceof Error ||
431+ isWeakMap ( val1 ) ||
432+ isWeakSet ( val1 ) ||
433+ isPromise ( val1 ) ) {
416434 return false ;
417435 }
418436
@@ -884,18 +902,16 @@ function partialSparseArrayEquiv(a, b, mode, memos, startA, startB) {
884902 let aPos = startA ;
885903 const keysA = ObjectKeys ( a ) ;
886904 const keysB = ObjectKeys ( b ) ;
887- const keysBLength = keysB . length ;
888- const keysALength = keysA . length ;
889- const lenA = keysALength - startA ;
890- const lenB = keysBLength - startB ;
905+ const lenA = keysA . length - startA ;
906+ const lenB = keysB . length - startB ;
891907 if ( lenA < lenB ) {
892908 return false ;
893909 }
894910 for ( let i = 0 ; i < lenB ; i ++ ) {
895911 const keyB = keysB [ startB + i ] ;
896912 while ( ! innerDeepEqual ( a [ keysA [ aPos ] ] , b [ keyB ] , mode , memos ) ) {
897913 aPos ++ ;
898- if ( aPos > keysALength - lenB + i ) {
914+ if ( aPos > keysA . length - lenB + i ) {
899915 return false ;
900916 }
901917 }
@@ -927,8 +943,6 @@ function partialArrayEquiv(a, b, mode, memos) {
927943}
928944
929945function sparseArrayEquiv ( a , b , mode , memos , i ) {
930- // TODO(BridgeAR): Use internal method to only get index properties. The
931- // same applies to the partial implementation.
932946 const keysA = ObjectKeys ( a ) ;
933947 const keysB = ObjectKeys ( b ) ;
934948 if ( keysA . length !== keysB . length ) {
0 commit comments