diff --git a/lib/internal/util/comparisons.js b/lib/internal/util/comparisons.js index 3b800b48b7c5d5..bee24b8ef72dca 100644 --- a/lib/internal/util/comparisons.js +++ b/lib/internal/util/comparisons.js @@ -41,6 +41,7 @@ const { StringPrototypeValueOf, Symbol, SymbolPrototypeValueOf, + SymbolToStringTag, TypedArrayPrototypeGetByteLength: getByteLength, TypedArrayPrototypeGetSymbolToStringTag, Uint16Array, @@ -264,6 +265,17 @@ function innerDeepEqual(val1, val2, mode, memos) { return objectComparisonStart(val1, val2, mode, memos); } +function hasUnequalTag(val1, val2) { + return val1[SymbolToStringTag] !== val2[SymbolToStringTag]; +} + +function slowHasUnequalTag(val1Tag, val1, val2) { + if (val1[SymbolToStringTag] !== undefined && val2[SymbolToStringTag] !== undefined) { + return val1[SymbolToStringTag] !== val2[SymbolToStringTag]; + } + return val1Tag !== ObjectPrototypeToString(val2); +} + function objectComparisonStart(val1, val2, mode, memos) { if (mode === kStrict) { if (wellKnownConstructors.has(val1.constructor) || @@ -276,16 +288,10 @@ function objectComparisonStart(val1, val2, mode, memos) { } } - const val1Tag = ObjectPrototypeToString(val1); - const val2Tag = ObjectPrototypeToString(val2); - - if (val1Tag !== val2Tag) { - return false; - } - if (ArrayIsArray(val1)) { if (!ArrayIsArray(val2) || - (val1.length !== val2.length && (mode !== kPartial || val1.length < val2.length))) { + (val1.length !== val2.length && (mode !== kPartial || val1.length < val2.length)) || + hasUnequalTag(val1, val2)) { return false; } @@ -296,22 +302,29 @@ function objectComparisonStart(val1, val2, mode, memos) { return false; } return keyCheck(val1, val2, mode, memos, kIsArray, keys2); - } else if (val1Tag === '[object Object]') { - return keyCheck(val1, val2, mode, memos, kNoIterator); - } else if (isDate(val1)) { - if (!isDate(val2)) { + } + + let val1Tag; + if (val1[SymbolToStringTag] === undefined && + (val1Tag = ObjectPrototypeToString(val1)) === '[object Object]') { + if (slowHasUnequalTag(val1Tag, val1, val2)) { return false; } - const time1 = DatePrototypeGetTime(val1); - const time2 = DatePrototypeGetTime(val2); - // eslint-disable-next-line no-self-compare - if (time1 !== time2 && (time1 === time1 || time2 === time2)) { + return keyCheck(val1, val2, mode, memos, kNoIterator); + } else if (isSet(val1)) { + if (!isSet(val2) || + (val1.size !== val2.size && (mode !== kPartial || val1.size < val2.size)) || + hasUnequalTag(val1, val2)) { return false; } - } else if (isRegExp(val1)) { - if (!isRegExp(val2) || !areSimilarRegExps(val1, val2)) { + return keyCheck(val1, val2, mode, memos, kIsSet); + } else if (isMap(val1)) { + if (!isMap(val2) || + (val1.size !== val2.size && (mode !== kPartial || val1.size < val2.size)) || + hasUnequalTag(val1, val2)) { return false; } + return keyCheck(val1, val2, mode, memos, kIsMap); } else if (isArrayBufferView(val1)) { if (TypedArrayPrototypeGetSymbolToStringTag(val1) !== TypedArrayPrototypeGetSymbolToStringTag(val2)) { @@ -322,7 +335,7 @@ function objectComparisonStart(val1, val2, mode, memos) { return false; } } else if (mode === kLoose && - (isFloat32Array(val1) || isFloat64Array(val1) || isFloat16Array(val1))) { + (isFloat32Array(val1) || isFloat64Array(val1) || isFloat16Array(val1))) { if (!areSimilarFloatArrays(val1, val2)) { return false; } @@ -339,20 +352,22 @@ function objectComparisonStart(val1, val2, mode, memos) { return false; } return keyCheck(val1, val2, mode, memos, kNoIterator, keys2); - } else if (isSet(val1)) { - if (!isSet(val2) || - (val1.size !== val2.size && (mode !== kPartial || val1.size < val2.size))) { + } else if (isDate(val1)) { + if (!isDate(val2)) { return false; } - return keyCheck(val1, val2, mode, memos, kIsSet); - } else if (isMap(val1)) { - if (!isMap(val2) || - (val1.size !== val2.size && (mode !== kPartial || val1.size < val2.size))) { + const time1 = DatePrototypeGetTime(val1); + const time2 = DatePrototypeGetTime(val2); + // eslint-disable-next-line no-self-compare + if (time1 !== time2 && (time1 === time1 || time2 === time2)) { + return false; + } + } else if (isRegExp(val1)) { + if (!isRegExp(val2) || !areSimilarRegExps(val1, val2) || hasUnequalTag(val1, val2)) { return false; } - return keyCheck(val1, val2, mode, memos, kIsMap); } else if (isAnyArrayBuffer(val1)) { - if (!isAnyArrayBuffer(val2)) { + if (!isAnyArrayBuffer(val2) || hasUnequalTag(val1, val2)) { return false; } if (mode !== kPartial || val1.byteLength === val2.byteLength) { @@ -362,6 +377,15 @@ function objectComparisonStart(val1, val2, mode, memos) { } else if (!isPartialUint8Array(new Uint8Array(val1), new Uint8Array(val2))) { return false; } + } else if (slowHasUnequalTag(val1Tag ?? ObjectPrototypeToString(val1), val1, val2) || + ArrayIsArray(val2) || + isArrayBufferView(val2) || + isSet(val2) || + isMap(val2) || + isDate(val2) || + isRegExp(val2) || + isAnyArrayBuffer(val2)) { + return false; } else if (isError(val1)) { // Do not compare the stack as it might differ even though the error itself // is otherwise identical. @@ -380,17 +404,6 @@ function objectComparisonStart(val1, val2, mode, memos) { if (!isEqualBoxedPrimitive(val1, val2)) { return false; } - } else if (ArrayIsArray(val2) || - isArrayBufferView(val2) || - isSet(val2) || - isMap(val2) || - isDate(val2) || - isRegExp(val2) || - isAnyArrayBuffer(val2) || - isBoxedPrimitive(val2) || - isNativeError(val2) || - val2 instanceof Error) { - return false; } else if (isURL(val1)) { if (!isURL(val2) || val1.href !== val2.href) { return false; @@ -412,7 +425,12 @@ function objectComparisonStart(val1, val2, mode, memos) { ) { return false; } - } else if (isWeakMap(val1) || isWeakSet(val1) || isPromise(val1)) { + } else if (isBoxedPrimitive(val2) || + isNativeError(val2) || + val2 instanceof Error || + isWeakMap(val1) || + isWeakSet(val1) || + isPromise(val1)) { return false; } @@ -879,10 +897,8 @@ function partialSparseArrayEquiv(a, b, mode, memos, startA, startB) { let aPos = startA; const keysA = ObjectKeys(a); const keysB = ObjectKeys(b); - const keysBLength = keysB.length; - const keysALength = keysA.length; - const lenA = keysALength - startA; - const lenB = keysBLength - startB; + const lenA = keysA.length - startA; + const lenB = keysB.length - startB; if (lenA < lenB) { return false; } @@ -890,7 +906,7 @@ function partialSparseArrayEquiv(a, b, mode, memos, startA, startB) { const keyB = keysB[startB + i]; while (!innerDeepEqual(a[keysA[aPos]], b[keyB], mode, memos)) { aPos++; - if (aPos > keysALength - lenB + i) { + if (aPos > keysA.length - lenB + i) { return false; } } @@ -922,8 +938,6 @@ function partialArrayEquiv(a, b, mode, memos) { } function sparseArrayEquiv(a, b, mode, memos, i) { - // TODO(BridgeAR): Use internal method to only get index properties. The - // same applies to the partial implementation. const keysA = ObjectKeys(a); const keysB = ObjectKeys(b); if (keysA.length !== keysB.length) {