Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 61 additions & 47 deletions lib/internal/util/comparisons.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const {
StringPrototypeValueOf,
Symbol,
SymbolPrototypeValueOf,
SymbolToStringTag,
TypedArrayPrototypeGetByteLength: getByteLength,
TypedArrayPrototypeGetSymbolToStringTag,
Uint16Array,
Expand Down Expand Up @@ -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) ||
Expand All @@ -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;
}

Expand All @@ -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)) {
Expand All @@ -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))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated change (we should probably have the linter enforce one or the other)

Suggested change
(isFloat32Array(val1) || isFloat64Array(val1) || isFloat16Array(val1))) {
(isFloat32Array(val1) || isFloat64Array(val1) || isFloat16Array(val1))) {

if (!areSimilarFloatArrays(val1, val2)) {
return false;
}
Expand All @@ -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) {
Expand All @@ -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.
Expand All @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -879,18 +897,16 @@ 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;
}
for (let i = 0; i < lenB; i++) {
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;
}
}
Expand Down Expand Up @@ -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) {
Expand Down
Loading