11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst { 41cb0ef41Sopenharmony_ci ArrayIsArray, 51cb0ef41Sopenharmony_ci ArrayPrototypeFilter, 61cb0ef41Sopenharmony_ci ArrayPrototypePush, 71cb0ef41Sopenharmony_ci BigIntPrototypeValueOf, 81cb0ef41Sopenharmony_ci BooleanPrototypeValueOf, 91cb0ef41Sopenharmony_ci DatePrototypeGetTime, 101cb0ef41Sopenharmony_ci Error, 111cb0ef41Sopenharmony_ci NumberIsNaN, 121cb0ef41Sopenharmony_ci NumberPrototypeValueOf, 131cb0ef41Sopenharmony_ci ObjectGetOwnPropertySymbols, 141cb0ef41Sopenharmony_ci ObjectGetPrototypeOf, 151cb0ef41Sopenharmony_ci ObjectIs, 161cb0ef41Sopenharmony_ci ObjectKeys, 171cb0ef41Sopenharmony_ci ObjectPrototypeHasOwnProperty, 181cb0ef41Sopenharmony_ci ObjectPrototypePropertyIsEnumerable, 191cb0ef41Sopenharmony_ci ObjectPrototypeToString, 201cb0ef41Sopenharmony_ci SafeMap, 211cb0ef41Sopenharmony_ci SafeSet, 221cb0ef41Sopenharmony_ci StringPrototypeValueOf, 231cb0ef41Sopenharmony_ci SymbolPrototypeValueOf, 241cb0ef41Sopenharmony_ci TypedArrayPrototypeGetSymbolToStringTag, 251cb0ef41Sopenharmony_ci Uint8Array, 261cb0ef41Sopenharmony_ci} = primordials; 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_ciconst { compare } = internalBinding('buffer'); 291cb0ef41Sopenharmony_ciconst assert = require('internal/assert'); 301cb0ef41Sopenharmony_ciconst types = require('internal/util/types'); 311cb0ef41Sopenharmony_ciconst { 321cb0ef41Sopenharmony_ci isAnyArrayBuffer, 331cb0ef41Sopenharmony_ci isArrayBufferView, 341cb0ef41Sopenharmony_ci isDate, 351cb0ef41Sopenharmony_ci isMap, 361cb0ef41Sopenharmony_ci isRegExp, 371cb0ef41Sopenharmony_ci isSet, 381cb0ef41Sopenharmony_ci isNativeError, 391cb0ef41Sopenharmony_ci isBoxedPrimitive, 401cb0ef41Sopenharmony_ci isNumberObject, 411cb0ef41Sopenharmony_ci isStringObject, 421cb0ef41Sopenharmony_ci isBooleanObject, 431cb0ef41Sopenharmony_ci isBigIntObject, 441cb0ef41Sopenharmony_ci isSymbolObject, 451cb0ef41Sopenharmony_ci isFloat32Array, 461cb0ef41Sopenharmony_ci isFloat64Array, 471cb0ef41Sopenharmony_ci} = types; 481cb0ef41Sopenharmony_ciconst { 491cb0ef41Sopenharmony_ci constants: { 501cb0ef41Sopenharmony_ci ONLY_ENUMERABLE, 511cb0ef41Sopenharmony_ci SKIP_SYMBOLS, 521cb0ef41Sopenharmony_ci }, 531cb0ef41Sopenharmony_ci getOwnNonIndexProperties, 541cb0ef41Sopenharmony_ci} = internalBinding('util'); 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_ciconst kStrict = true; 571cb0ef41Sopenharmony_ciconst kLoose = false; 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ciconst kNoIterator = 0; 601cb0ef41Sopenharmony_ciconst kIsArray = 1; 611cb0ef41Sopenharmony_ciconst kIsSet = 2; 621cb0ef41Sopenharmony_ciconst kIsMap = 3; 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci// Check if they have the same source and flags 651cb0ef41Sopenharmony_cifunction areSimilarRegExps(a, b) { 661cb0ef41Sopenharmony_ci return a.source === b.source && 671cb0ef41Sopenharmony_ci a.flags === b.flags && 681cb0ef41Sopenharmony_ci a.lastIndex === b.lastIndex; 691cb0ef41Sopenharmony_ci} 701cb0ef41Sopenharmony_ci 711cb0ef41Sopenharmony_cifunction areSimilarFloatArrays(a, b) { 721cb0ef41Sopenharmony_ci if (a.byteLength !== b.byteLength) { 731cb0ef41Sopenharmony_ci return false; 741cb0ef41Sopenharmony_ci } 751cb0ef41Sopenharmony_ci for (let offset = 0; offset < a.byteLength; offset++) { 761cb0ef41Sopenharmony_ci if (a[offset] !== b[offset]) { 771cb0ef41Sopenharmony_ci return false; 781cb0ef41Sopenharmony_ci } 791cb0ef41Sopenharmony_ci } 801cb0ef41Sopenharmony_ci return true; 811cb0ef41Sopenharmony_ci} 821cb0ef41Sopenharmony_ci 831cb0ef41Sopenharmony_cifunction areSimilarTypedArrays(a, b) { 841cb0ef41Sopenharmony_ci if (a.byteLength !== b.byteLength) { 851cb0ef41Sopenharmony_ci return false; 861cb0ef41Sopenharmony_ci } 871cb0ef41Sopenharmony_ci return compare(new Uint8Array(a.buffer, a.byteOffset, a.byteLength), 881cb0ef41Sopenharmony_ci new Uint8Array(b.buffer, b.byteOffset, b.byteLength)) === 0; 891cb0ef41Sopenharmony_ci} 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_cifunction areEqualArrayBuffers(buf1, buf2) { 921cb0ef41Sopenharmony_ci return buf1.byteLength === buf2.byteLength && 931cb0ef41Sopenharmony_ci compare(new Uint8Array(buf1), new Uint8Array(buf2)) === 0; 941cb0ef41Sopenharmony_ci} 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_cifunction isEqualBoxedPrimitive(val1, val2) { 971cb0ef41Sopenharmony_ci if (isNumberObject(val1)) { 981cb0ef41Sopenharmony_ci return isNumberObject(val2) && 991cb0ef41Sopenharmony_ci ObjectIs(NumberPrototypeValueOf(val1), 1001cb0ef41Sopenharmony_ci NumberPrototypeValueOf(val2)); 1011cb0ef41Sopenharmony_ci } 1021cb0ef41Sopenharmony_ci if (isStringObject(val1)) { 1031cb0ef41Sopenharmony_ci return isStringObject(val2) && 1041cb0ef41Sopenharmony_ci StringPrototypeValueOf(val1) === StringPrototypeValueOf(val2); 1051cb0ef41Sopenharmony_ci } 1061cb0ef41Sopenharmony_ci if (isBooleanObject(val1)) { 1071cb0ef41Sopenharmony_ci return isBooleanObject(val2) && 1081cb0ef41Sopenharmony_ci BooleanPrototypeValueOf(val1) === BooleanPrototypeValueOf(val2); 1091cb0ef41Sopenharmony_ci } 1101cb0ef41Sopenharmony_ci if (isBigIntObject(val1)) { 1111cb0ef41Sopenharmony_ci return isBigIntObject(val2) && 1121cb0ef41Sopenharmony_ci BigIntPrototypeValueOf(val1) === BigIntPrototypeValueOf(val2); 1131cb0ef41Sopenharmony_ci } 1141cb0ef41Sopenharmony_ci if (isSymbolObject(val1)) { 1151cb0ef41Sopenharmony_ci return isSymbolObject(val2) && 1161cb0ef41Sopenharmony_ci SymbolPrototypeValueOf(val1) === SymbolPrototypeValueOf(val2); 1171cb0ef41Sopenharmony_ci } 1181cb0ef41Sopenharmony_ci /* c8 ignore next */ 1191cb0ef41Sopenharmony_ci assert.fail(`Unknown boxed type ${val1}`); 1201cb0ef41Sopenharmony_ci} 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci// Notes: Type tags are historical [[Class]] properties that can be set by 1231cb0ef41Sopenharmony_ci// FunctionTemplate::SetClassName() in C++ or Symbol.toStringTag in JS 1241cb0ef41Sopenharmony_ci// and retrieved using Object.prototype.toString.call(obj) in JS 1251cb0ef41Sopenharmony_ci// See https://tc39.github.io/ecma262/#sec-object.prototype.tostring 1261cb0ef41Sopenharmony_ci// for a list of tags pre-defined in the spec. 1271cb0ef41Sopenharmony_ci// There are some unspecified tags in the wild too (e.g. typed array tags). 1281cb0ef41Sopenharmony_ci// Since tags can be altered, they only serve fast failures 1291cb0ef41Sopenharmony_ci// 1301cb0ef41Sopenharmony_ci// For strict comparison, objects should have 1311cb0ef41Sopenharmony_ci// a) The same built-in type tag. 1321cb0ef41Sopenharmony_ci// b) The same prototypes. 1331cb0ef41Sopenharmony_ci 1341cb0ef41Sopenharmony_cifunction innerDeepEqual(val1, val2, strict, memos) { 1351cb0ef41Sopenharmony_ci // All identical values are equivalent, as determined by ===. 1361cb0ef41Sopenharmony_ci if (val1 === val2) { 1371cb0ef41Sopenharmony_ci if (val1 !== 0) 1381cb0ef41Sopenharmony_ci return true; 1391cb0ef41Sopenharmony_ci return strict ? ObjectIs(val1, val2) : true; 1401cb0ef41Sopenharmony_ci } 1411cb0ef41Sopenharmony_ci 1421cb0ef41Sopenharmony_ci // Check more closely if val1 and val2 are equal. 1431cb0ef41Sopenharmony_ci if (strict) { 1441cb0ef41Sopenharmony_ci if (typeof val1 !== 'object') { 1451cb0ef41Sopenharmony_ci return typeof val1 === 'number' && NumberIsNaN(val1) && 1461cb0ef41Sopenharmony_ci NumberIsNaN(val2); 1471cb0ef41Sopenharmony_ci } 1481cb0ef41Sopenharmony_ci if (typeof val2 !== 'object' || val1 === null || val2 === null) { 1491cb0ef41Sopenharmony_ci return false; 1501cb0ef41Sopenharmony_ci } 1511cb0ef41Sopenharmony_ci if (ObjectGetPrototypeOf(val1) !== ObjectGetPrototypeOf(val2)) { 1521cb0ef41Sopenharmony_ci return false; 1531cb0ef41Sopenharmony_ci } 1541cb0ef41Sopenharmony_ci } else { 1551cb0ef41Sopenharmony_ci if (val1 === null || typeof val1 !== 'object') { 1561cb0ef41Sopenharmony_ci if (val2 === null || typeof val2 !== 'object') { 1571cb0ef41Sopenharmony_ci // eslint-disable-next-line eqeqeq 1581cb0ef41Sopenharmony_ci return val1 == val2 || (NumberIsNaN(val1) && NumberIsNaN(val2)); 1591cb0ef41Sopenharmony_ci } 1601cb0ef41Sopenharmony_ci return false; 1611cb0ef41Sopenharmony_ci } 1621cb0ef41Sopenharmony_ci if (val2 === null || typeof val2 !== 'object') { 1631cb0ef41Sopenharmony_ci return false; 1641cb0ef41Sopenharmony_ci } 1651cb0ef41Sopenharmony_ci } 1661cb0ef41Sopenharmony_ci const val1Tag = ObjectPrototypeToString(val1); 1671cb0ef41Sopenharmony_ci const val2Tag = ObjectPrototypeToString(val2); 1681cb0ef41Sopenharmony_ci 1691cb0ef41Sopenharmony_ci if (val1Tag !== val2Tag) { 1701cb0ef41Sopenharmony_ci return false; 1711cb0ef41Sopenharmony_ci } 1721cb0ef41Sopenharmony_ci 1731cb0ef41Sopenharmony_ci if (ArrayIsArray(val1)) { 1741cb0ef41Sopenharmony_ci // Check for sparse arrays and general fast path 1751cb0ef41Sopenharmony_ci if (!ArrayIsArray(val2) || val1.length !== val2.length) { 1761cb0ef41Sopenharmony_ci return false; 1771cb0ef41Sopenharmony_ci } 1781cb0ef41Sopenharmony_ci const filter = strict ? ONLY_ENUMERABLE : ONLY_ENUMERABLE | SKIP_SYMBOLS; 1791cb0ef41Sopenharmony_ci const keys1 = getOwnNonIndexProperties(val1, filter); 1801cb0ef41Sopenharmony_ci const keys2 = getOwnNonIndexProperties(val2, filter); 1811cb0ef41Sopenharmony_ci if (keys1.length !== keys2.length) { 1821cb0ef41Sopenharmony_ci return false; 1831cb0ef41Sopenharmony_ci } 1841cb0ef41Sopenharmony_ci return keyCheck(val1, val2, strict, memos, kIsArray, keys1); 1851cb0ef41Sopenharmony_ci } else if (val1Tag === '[object Object]') { 1861cb0ef41Sopenharmony_ci return keyCheck(val1, val2, strict, memos, kNoIterator); 1871cb0ef41Sopenharmony_ci } else if (isDate(val1)) { 1881cb0ef41Sopenharmony_ci if (!isDate(val2) || 1891cb0ef41Sopenharmony_ci DatePrototypeGetTime(val1) !== DatePrototypeGetTime(val2)) { 1901cb0ef41Sopenharmony_ci return false; 1911cb0ef41Sopenharmony_ci } 1921cb0ef41Sopenharmony_ci } else if (isRegExp(val1)) { 1931cb0ef41Sopenharmony_ci if (!isRegExp(val2) || !areSimilarRegExps(val1, val2)) { 1941cb0ef41Sopenharmony_ci return false; 1951cb0ef41Sopenharmony_ci } 1961cb0ef41Sopenharmony_ci } else if (isNativeError(val1) || val1 instanceof Error) { 1971cb0ef41Sopenharmony_ci // Do not compare the stack as it might differ even though the error itself 1981cb0ef41Sopenharmony_ci // is otherwise identical. 1991cb0ef41Sopenharmony_ci if ((!isNativeError(val2) && !(val2 instanceof Error)) || 2001cb0ef41Sopenharmony_ci val1.message !== val2.message || 2011cb0ef41Sopenharmony_ci val1.name !== val2.name) { 2021cb0ef41Sopenharmony_ci return false; 2031cb0ef41Sopenharmony_ci } 2041cb0ef41Sopenharmony_ci } else if (isArrayBufferView(val1)) { 2051cb0ef41Sopenharmony_ci if (TypedArrayPrototypeGetSymbolToStringTag(val1) !== 2061cb0ef41Sopenharmony_ci TypedArrayPrototypeGetSymbolToStringTag(val2)) { 2071cb0ef41Sopenharmony_ci return false; 2081cb0ef41Sopenharmony_ci } 2091cb0ef41Sopenharmony_ci if (!strict && (isFloat32Array(val1) || isFloat64Array(val1))) { 2101cb0ef41Sopenharmony_ci if (!areSimilarFloatArrays(val1, val2)) { 2111cb0ef41Sopenharmony_ci return false; 2121cb0ef41Sopenharmony_ci } 2131cb0ef41Sopenharmony_ci } else if (!areSimilarTypedArrays(val1, val2)) { 2141cb0ef41Sopenharmony_ci return false; 2151cb0ef41Sopenharmony_ci } 2161cb0ef41Sopenharmony_ci // Buffer.compare returns true, so val1.length === val2.length. If they both 2171cb0ef41Sopenharmony_ci // only contain numeric keys, we don't need to exam further than checking 2181cb0ef41Sopenharmony_ci // the symbols. 2191cb0ef41Sopenharmony_ci const filter = strict ? ONLY_ENUMERABLE : ONLY_ENUMERABLE | SKIP_SYMBOLS; 2201cb0ef41Sopenharmony_ci const keys1 = getOwnNonIndexProperties(val1, filter); 2211cb0ef41Sopenharmony_ci const keys2 = getOwnNonIndexProperties(val2, filter); 2221cb0ef41Sopenharmony_ci if (keys1.length !== keys2.length) { 2231cb0ef41Sopenharmony_ci return false; 2241cb0ef41Sopenharmony_ci } 2251cb0ef41Sopenharmony_ci return keyCheck(val1, val2, strict, memos, kNoIterator, keys1); 2261cb0ef41Sopenharmony_ci } else if (isSet(val1)) { 2271cb0ef41Sopenharmony_ci if (!isSet(val2) || val1.size !== val2.size) { 2281cb0ef41Sopenharmony_ci return false; 2291cb0ef41Sopenharmony_ci } 2301cb0ef41Sopenharmony_ci return keyCheck(val1, val2, strict, memos, kIsSet); 2311cb0ef41Sopenharmony_ci } else if (isMap(val1)) { 2321cb0ef41Sopenharmony_ci if (!isMap(val2) || val1.size !== val2.size) { 2331cb0ef41Sopenharmony_ci return false; 2341cb0ef41Sopenharmony_ci } 2351cb0ef41Sopenharmony_ci return keyCheck(val1, val2, strict, memos, kIsMap); 2361cb0ef41Sopenharmony_ci } else if (isAnyArrayBuffer(val1)) { 2371cb0ef41Sopenharmony_ci if (!isAnyArrayBuffer(val2) || !areEqualArrayBuffers(val1, val2)) { 2381cb0ef41Sopenharmony_ci return false; 2391cb0ef41Sopenharmony_ci } 2401cb0ef41Sopenharmony_ci } else if (isBoxedPrimitive(val1)) { 2411cb0ef41Sopenharmony_ci if (!isEqualBoxedPrimitive(val1, val2)) { 2421cb0ef41Sopenharmony_ci return false; 2431cb0ef41Sopenharmony_ci } 2441cb0ef41Sopenharmony_ci } else if (ArrayIsArray(val2) || 2451cb0ef41Sopenharmony_ci isArrayBufferView(val2) || 2461cb0ef41Sopenharmony_ci isSet(val2) || 2471cb0ef41Sopenharmony_ci isMap(val2) || 2481cb0ef41Sopenharmony_ci isDate(val2) || 2491cb0ef41Sopenharmony_ci isRegExp(val2) || 2501cb0ef41Sopenharmony_ci isAnyArrayBuffer(val2) || 2511cb0ef41Sopenharmony_ci isBoxedPrimitive(val2) || 2521cb0ef41Sopenharmony_ci isNativeError(val2) || 2531cb0ef41Sopenharmony_ci val2 instanceof Error) { 2541cb0ef41Sopenharmony_ci return false; 2551cb0ef41Sopenharmony_ci } 2561cb0ef41Sopenharmony_ci return keyCheck(val1, val2, strict, memos, kNoIterator); 2571cb0ef41Sopenharmony_ci} 2581cb0ef41Sopenharmony_ci 2591cb0ef41Sopenharmony_cifunction getEnumerables(val, keys) { 2601cb0ef41Sopenharmony_ci return ArrayPrototypeFilter( 2611cb0ef41Sopenharmony_ci keys, 2621cb0ef41Sopenharmony_ci (k) => ObjectPrototypePropertyIsEnumerable(val, k), 2631cb0ef41Sopenharmony_ci ); 2641cb0ef41Sopenharmony_ci} 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_cifunction keyCheck(val1, val2, strict, memos, iterationType, aKeys) { 2671cb0ef41Sopenharmony_ci // For all remaining Object pairs, including Array, objects and Maps, 2681cb0ef41Sopenharmony_ci // equivalence is determined by having: 2691cb0ef41Sopenharmony_ci // a) The same number of owned enumerable properties 2701cb0ef41Sopenharmony_ci // b) The same set of keys/indexes (although not necessarily the same order) 2711cb0ef41Sopenharmony_ci // c) Equivalent values for every corresponding key/index 2721cb0ef41Sopenharmony_ci // d) For Sets and Maps, equal contents 2731cb0ef41Sopenharmony_ci // Note: this accounts for both named and indexed properties on Arrays. 2741cb0ef41Sopenharmony_ci if (arguments.length === 5) { 2751cb0ef41Sopenharmony_ci aKeys = ObjectKeys(val1); 2761cb0ef41Sopenharmony_ci const bKeys = ObjectKeys(val2); 2771cb0ef41Sopenharmony_ci 2781cb0ef41Sopenharmony_ci // The pair must have the same number of owned properties. 2791cb0ef41Sopenharmony_ci if (aKeys.length !== bKeys.length) { 2801cb0ef41Sopenharmony_ci return false; 2811cb0ef41Sopenharmony_ci } 2821cb0ef41Sopenharmony_ci } 2831cb0ef41Sopenharmony_ci 2841cb0ef41Sopenharmony_ci // Cheap key test 2851cb0ef41Sopenharmony_ci let i = 0; 2861cb0ef41Sopenharmony_ci for (; i < aKeys.length; i++) { 2871cb0ef41Sopenharmony_ci if (!ObjectPrototypePropertyIsEnumerable(val2, aKeys[i])) { 2881cb0ef41Sopenharmony_ci return false; 2891cb0ef41Sopenharmony_ci } 2901cb0ef41Sopenharmony_ci } 2911cb0ef41Sopenharmony_ci 2921cb0ef41Sopenharmony_ci if (strict && arguments.length === 5) { 2931cb0ef41Sopenharmony_ci const symbolKeysA = ObjectGetOwnPropertySymbols(val1); 2941cb0ef41Sopenharmony_ci if (symbolKeysA.length !== 0) { 2951cb0ef41Sopenharmony_ci let count = 0; 2961cb0ef41Sopenharmony_ci for (i = 0; i < symbolKeysA.length; i++) { 2971cb0ef41Sopenharmony_ci const key = symbolKeysA[i]; 2981cb0ef41Sopenharmony_ci if (ObjectPrototypePropertyIsEnumerable(val1, key)) { 2991cb0ef41Sopenharmony_ci if (!ObjectPrototypePropertyIsEnumerable(val2, key)) { 3001cb0ef41Sopenharmony_ci return false; 3011cb0ef41Sopenharmony_ci } 3021cb0ef41Sopenharmony_ci ArrayPrototypePush(aKeys, key); 3031cb0ef41Sopenharmony_ci count++; 3041cb0ef41Sopenharmony_ci } else if (ObjectPrototypePropertyIsEnumerable(val2, key)) { 3051cb0ef41Sopenharmony_ci return false; 3061cb0ef41Sopenharmony_ci } 3071cb0ef41Sopenharmony_ci } 3081cb0ef41Sopenharmony_ci const symbolKeysB = ObjectGetOwnPropertySymbols(val2); 3091cb0ef41Sopenharmony_ci if (symbolKeysA.length !== symbolKeysB.length && 3101cb0ef41Sopenharmony_ci getEnumerables(val2, symbolKeysB).length !== count) { 3111cb0ef41Sopenharmony_ci return false; 3121cb0ef41Sopenharmony_ci } 3131cb0ef41Sopenharmony_ci } else { 3141cb0ef41Sopenharmony_ci const symbolKeysB = ObjectGetOwnPropertySymbols(val2); 3151cb0ef41Sopenharmony_ci if (symbolKeysB.length !== 0 && 3161cb0ef41Sopenharmony_ci getEnumerables(val2, symbolKeysB).length !== 0) { 3171cb0ef41Sopenharmony_ci return false; 3181cb0ef41Sopenharmony_ci } 3191cb0ef41Sopenharmony_ci } 3201cb0ef41Sopenharmony_ci } 3211cb0ef41Sopenharmony_ci 3221cb0ef41Sopenharmony_ci if (aKeys.length === 0 && 3231cb0ef41Sopenharmony_ci (iterationType === kNoIterator || 3241cb0ef41Sopenharmony_ci (iterationType === kIsArray && val1.length === 0) || 3251cb0ef41Sopenharmony_ci val1.size === 0)) { 3261cb0ef41Sopenharmony_ci return true; 3271cb0ef41Sopenharmony_ci } 3281cb0ef41Sopenharmony_ci 3291cb0ef41Sopenharmony_ci // Use memos to handle cycles. 3301cb0ef41Sopenharmony_ci if (memos === undefined) { 3311cb0ef41Sopenharmony_ci memos = { 3321cb0ef41Sopenharmony_ci val1: new SafeMap(), 3331cb0ef41Sopenharmony_ci val2: new SafeMap(), 3341cb0ef41Sopenharmony_ci position: 0, 3351cb0ef41Sopenharmony_ci }; 3361cb0ef41Sopenharmony_ci } else { 3371cb0ef41Sopenharmony_ci // We prevent up to two map.has(x) calls by directly retrieving the value 3381cb0ef41Sopenharmony_ci // and checking for undefined. The map can only contain numbers, so it is 3391cb0ef41Sopenharmony_ci // safe to check for undefined only. 3401cb0ef41Sopenharmony_ci const val2MemoA = memos.val1.get(val1); 3411cb0ef41Sopenharmony_ci if (val2MemoA !== undefined) { 3421cb0ef41Sopenharmony_ci const val2MemoB = memos.val2.get(val2); 3431cb0ef41Sopenharmony_ci if (val2MemoB !== undefined) { 3441cb0ef41Sopenharmony_ci return val2MemoA === val2MemoB; 3451cb0ef41Sopenharmony_ci } 3461cb0ef41Sopenharmony_ci } 3471cb0ef41Sopenharmony_ci memos.position++; 3481cb0ef41Sopenharmony_ci } 3491cb0ef41Sopenharmony_ci 3501cb0ef41Sopenharmony_ci memos.val1.set(val1, memos.position); 3511cb0ef41Sopenharmony_ci memos.val2.set(val2, memos.position); 3521cb0ef41Sopenharmony_ci 3531cb0ef41Sopenharmony_ci const areEq = objEquiv(val1, val2, strict, aKeys, memos, iterationType); 3541cb0ef41Sopenharmony_ci 3551cb0ef41Sopenharmony_ci memos.val1.delete(val1); 3561cb0ef41Sopenharmony_ci memos.val2.delete(val2); 3571cb0ef41Sopenharmony_ci 3581cb0ef41Sopenharmony_ci return areEq; 3591cb0ef41Sopenharmony_ci} 3601cb0ef41Sopenharmony_ci 3611cb0ef41Sopenharmony_cifunction setHasEqualElement(set, val1, strict, memo) { 3621cb0ef41Sopenharmony_ci // Go looking. 3631cb0ef41Sopenharmony_ci for (const val2 of set) { 3641cb0ef41Sopenharmony_ci if (innerDeepEqual(val1, val2, strict, memo)) { 3651cb0ef41Sopenharmony_ci // Remove the matching element to make sure we do not check that again. 3661cb0ef41Sopenharmony_ci set.delete(val2); 3671cb0ef41Sopenharmony_ci return true; 3681cb0ef41Sopenharmony_ci } 3691cb0ef41Sopenharmony_ci } 3701cb0ef41Sopenharmony_ci 3711cb0ef41Sopenharmony_ci return false; 3721cb0ef41Sopenharmony_ci} 3731cb0ef41Sopenharmony_ci 3741cb0ef41Sopenharmony_ci// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#Loose_equality_using 3751cb0ef41Sopenharmony_ci// Sadly it is not possible to detect corresponding values properly in case the 3761cb0ef41Sopenharmony_ci// type is a string, number, bigint or boolean. The reason is that those values 3771cb0ef41Sopenharmony_ci// can match lots of different string values (e.g., 1n == '+00001'). 3781cb0ef41Sopenharmony_cifunction findLooseMatchingPrimitives(prim) { 3791cb0ef41Sopenharmony_ci switch (typeof prim) { 3801cb0ef41Sopenharmony_ci case 'undefined': 3811cb0ef41Sopenharmony_ci return null; 3821cb0ef41Sopenharmony_ci case 'object': // Only pass in null as object! 3831cb0ef41Sopenharmony_ci return undefined; 3841cb0ef41Sopenharmony_ci case 'symbol': 3851cb0ef41Sopenharmony_ci return false; 3861cb0ef41Sopenharmony_ci case 'string': 3871cb0ef41Sopenharmony_ci prim = +prim; 3881cb0ef41Sopenharmony_ci // Loose equal entries exist only if the string is possible to convert to 3891cb0ef41Sopenharmony_ci // a regular number and not NaN. 3901cb0ef41Sopenharmony_ci // Fall through 3911cb0ef41Sopenharmony_ci case 'number': 3921cb0ef41Sopenharmony_ci if (NumberIsNaN(prim)) { 3931cb0ef41Sopenharmony_ci return false; 3941cb0ef41Sopenharmony_ci } 3951cb0ef41Sopenharmony_ci } 3961cb0ef41Sopenharmony_ci return true; 3971cb0ef41Sopenharmony_ci} 3981cb0ef41Sopenharmony_ci 3991cb0ef41Sopenharmony_cifunction setMightHaveLoosePrim(a, b, prim) { 4001cb0ef41Sopenharmony_ci const altValue = findLooseMatchingPrimitives(prim); 4011cb0ef41Sopenharmony_ci if (altValue != null) 4021cb0ef41Sopenharmony_ci return altValue; 4031cb0ef41Sopenharmony_ci 4041cb0ef41Sopenharmony_ci return b.has(altValue) && !a.has(altValue); 4051cb0ef41Sopenharmony_ci} 4061cb0ef41Sopenharmony_ci 4071cb0ef41Sopenharmony_cifunction mapMightHaveLoosePrim(a, b, prim, item, memo) { 4081cb0ef41Sopenharmony_ci const altValue = findLooseMatchingPrimitives(prim); 4091cb0ef41Sopenharmony_ci if (altValue != null) { 4101cb0ef41Sopenharmony_ci return altValue; 4111cb0ef41Sopenharmony_ci } 4121cb0ef41Sopenharmony_ci const curB = b.get(altValue); 4131cb0ef41Sopenharmony_ci if ((curB === undefined && !b.has(altValue)) || 4141cb0ef41Sopenharmony_ci !innerDeepEqual(item, curB, false, memo)) { 4151cb0ef41Sopenharmony_ci return false; 4161cb0ef41Sopenharmony_ci } 4171cb0ef41Sopenharmony_ci return !a.has(altValue) && innerDeepEqual(item, curB, false, memo); 4181cb0ef41Sopenharmony_ci} 4191cb0ef41Sopenharmony_ci 4201cb0ef41Sopenharmony_cifunction setEquiv(a, b, strict, memo) { 4211cb0ef41Sopenharmony_ci // This is a lazily initiated Set of entries which have to be compared 4221cb0ef41Sopenharmony_ci // pairwise. 4231cb0ef41Sopenharmony_ci let set = null; 4241cb0ef41Sopenharmony_ci for (const val of a) { 4251cb0ef41Sopenharmony_ci // Note: Checking for the objects first improves the performance for object 4261cb0ef41Sopenharmony_ci // heavy sets but it is a minor slow down for primitives. As they are fast 4271cb0ef41Sopenharmony_ci // to check this improves the worst case scenario instead. 4281cb0ef41Sopenharmony_ci if (typeof val === 'object' && val !== null) { 4291cb0ef41Sopenharmony_ci if (set === null) { 4301cb0ef41Sopenharmony_ci set = new SafeSet(); 4311cb0ef41Sopenharmony_ci } 4321cb0ef41Sopenharmony_ci // If the specified value doesn't exist in the second set it's a non-null 4331cb0ef41Sopenharmony_ci // object (or non strict only: a not matching primitive) we'll need to go 4341cb0ef41Sopenharmony_ci // hunting for something that's deep-(strict-)equal to it. To make this 4351cb0ef41Sopenharmony_ci // O(n log n) complexity we have to copy these values in a new set first. 4361cb0ef41Sopenharmony_ci set.add(val); 4371cb0ef41Sopenharmony_ci } else if (!b.has(val)) { 4381cb0ef41Sopenharmony_ci if (strict) 4391cb0ef41Sopenharmony_ci return false; 4401cb0ef41Sopenharmony_ci 4411cb0ef41Sopenharmony_ci // Fast path to detect missing string, symbol, undefined and null values. 4421cb0ef41Sopenharmony_ci if (!setMightHaveLoosePrim(a, b, val)) { 4431cb0ef41Sopenharmony_ci return false; 4441cb0ef41Sopenharmony_ci } 4451cb0ef41Sopenharmony_ci 4461cb0ef41Sopenharmony_ci if (set === null) { 4471cb0ef41Sopenharmony_ci set = new SafeSet(); 4481cb0ef41Sopenharmony_ci } 4491cb0ef41Sopenharmony_ci set.add(val); 4501cb0ef41Sopenharmony_ci } 4511cb0ef41Sopenharmony_ci } 4521cb0ef41Sopenharmony_ci 4531cb0ef41Sopenharmony_ci if (set !== null) { 4541cb0ef41Sopenharmony_ci for (const val of b) { 4551cb0ef41Sopenharmony_ci // We have to check if a primitive value is already 4561cb0ef41Sopenharmony_ci // matching and only if it's not, go hunting for it. 4571cb0ef41Sopenharmony_ci if (typeof val === 'object' && val !== null) { 4581cb0ef41Sopenharmony_ci if (!setHasEqualElement(set, val, strict, memo)) 4591cb0ef41Sopenharmony_ci return false; 4601cb0ef41Sopenharmony_ci } else if (!strict && 4611cb0ef41Sopenharmony_ci !a.has(val) && 4621cb0ef41Sopenharmony_ci !setHasEqualElement(set, val, strict, memo)) { 4631cb0ef41Sopenharmony_ci return false; 4641cb0ef41Sopenharmony_ci } 4651cb0ef41Sopenharmony_ci } 4661cb0ef41Sopenharmony_ci return set.size === 0; 4671cb0ef41Sopenharmony_ci } 4681cb0ef41Sopenharmony_ci 4691cb0ef41Sopenharmony_ci return true; 4701cb0ef41Sopenharmony_ci} 4711cb0ef41Sopenharmony_ci 4721cb0ef41Sopenharmony_cifunction mapHasEqualEntry(set, map, key1, item1, strict, memo) { 4731cb0ef41Sopenharmony_ci // To be able to handle cases like: 4741cb0ef41Sopenharmony_ci // Map([[{}, 'a'], [{}, 'b']]) vs Map([[{}, 'b'], [{}, 'a']]) 4751cb0ef41Sopenharmony_ci // ... we need to consider *all* matching keys, not just the first we find. 4761cb0ef41Sopenharmony_ci for (const key2 of set) { 4771cb0ef41Sopenharmony_ci if (innerDeepEqual(key1, key2, strict, memo) && 4781cb0ef41Sopenharmony_ci innerDeepEqual(item1, map.get(key2), strict, memo)) { 4791cb0ef41Sopenharmony_ci set.delete(key2); 4801cb0ef41Sopenharmony_ci return true; 4811cb0ef41Sopenharmony_ci } 4821cb0ef41Sopenharmony_ci } 4831cb0ef41Sopenharmony_ci 4841cb0ef41Sopenharmony_ci return false; 4851cb0ef41Sopenharmony_ci} 4861cb0ef41Sopenharmony_ci 4871cb0ef41Sopenharmony_cifunction mapEquiv(a, b, strict, memo) { 4881cb0ef41Sopenharmony_ci let set = null; 4891cb0ef41Sopenharmony_ci 4901cb0ef41Sopenharmony_ci for (const { 0: key, 1: item1 } of a) { 4911cb0ef41Sopenharmony_ci if (typeof key === 'object' && key !== null) { 4921cb0ef41Sopenharmony_ci if (set === null) { 4931cb0ef41Sopenharmony_ci set = new SafeSet(); 4941cb0ef41Sopenharmony_ci } 4951cb0ef41Sopenharmony_ci set.add(key); 4961cb0ef41Sopenharmony_ci } else { 4971cb0ef41Sopenharmony_ci // By directly retrieving the value we prevent another b.has(key) check in 4981cb0ef41Sopenharmony_ci // almost all possible cases. 4991cb0ef41Sopenharmony_ci const item2 = b.get(key); 5001cb0ef41Sopenharmony_ci if (((item2 === undefined && !b.has(key)) || 5011cb0ef41Sopenharmony_ci !innerDeepEqual(item1, item2, strict, memo))) { 5021cb0ef41Sopenharmony_ci if (strict) 5031cb0ef41Sopenharmony_ci return false; 5041cb0ef41Sopenharmony_ci // Fast path to detect missing string, symbol, undefined and null 5051cb0ef41Sopenharmony_ci // keys. 5061cb0ef41Sopenharmony_ci if (!mapMightHaveLoosePrim(a, b, key, item1, memo)) 5071cb0ef41Sopenharmony_ci return false; 5081cb0ef41Sopenharmony_ci if (set === null) { 5091cb0ef41Sopenharmony_ci set = new SafeSet(); 5101cb0ef41Sopenharmony_ci } 5111cb0ef41Sopenharmony_ci set.add(key); 5121cb0ef41Sopenharmony_ci } 5131cb0ef41Sopenharmony_ci } 5141cb0ef41Sopenharmony_ci } 5151cb0ef41Sopenharmony_ci 5161cb0ef41Sopenharmony_ci if (set !== null) { 5171cb0ef41Sopenharmony_ci for (const { 0: key, 1: item } of b) { 5181cb0ef41Sopenharmony_ci if (typeof key === 'object' && key !== null) { 5191cb0ef41Sopenharmony_ci if (!mapHasEqualEntry(set, a, key, item, strict, memo)) 5201cb0ef41Sopenharmony_ci return false; 5211cb0ef41Sopenharmony_ci } else if (!strict && 5221cb0ef41Sopenharmony_ci (!a.has(key) || 5231cb0ef41Sopenharmony_ci !innerDeepEqual(a.get(key), item, false, memo)) && 5241cb0ef41Sopenharmony_ci !mapHasEqualEntry(set, a, key, item, false, memo)) { 5251cb0ef41Sopenharmony_ci return false; 5261cb0ef41Sopenharmony_ci } 5271cb0ef41Sopenharmony_ci } 5281cb0ef41Sopenharmony_ci return set.size === 0; 5291cb0ef41Sopenharmony_ci } 5301cb0ef41Sopenharmony_ci 5311cb0ef41Sopenharmony_ci return true; 5321cb0ef41Sopenharmony_ci} 5331cb0ef41Sopenharmony_ci 5341cb0ef41Sopenharmony_cifunction objEquiv(a, b, strict, keys, memos, iterationType) { 5351cb0ef41Sopenharmony_ci // Sets and maps don't have their entries accessible via normal object 5361cb0ef41Sopenharmony_ci // properties. 5371cb0ef41Sopenharmony_ci let i = 0; 5381cb0ef41Sopenharmony_ci 5391cb0ef41Sopenharmony_ci if (iterationType === kIsSet) { 5401cb0ef41Sopenharmony_ci if (!setEquiv(a, b, strict, memos)) { 5411cb0ef41Sopenharmony_ci return false; 5421cb0ef41Sopenharmony_ci } 5431cb0ef41Sopenharmony_ci } else if (iterationType === kIsMap) { 5441cb0ef41Sopenharmony_ci if (!mapEquiv(a, b, strict, memos)) { 5451cb0ef41Sopenharmony_ci return false; 5461cb0ef41Sopenharmony_ci } 5471cb0ef41Sopenharmony_ci } else if (iterationType === kIsArray) { 5481cb0ef41Sopenharmony_ci for (; i < a.length; i++) { 5491cb0ef41Sopenharmony_ci if (ObjectPrototypeHasOwnProperty(a, i)) { 5501cb0ef41Sopenharmony_ci if (!ObjectPrototypeHasOwnProperty(b, i) || 5511cb0ef41Sopenharmony_ci !innerDeepEqual(a[i], b[i], strict, memos)) { 5521cb0ef41Sopenharmony_ci return false; 5531cb0ef41Sopenharmony_ci } 5541cb0ef41Sopenharmony_ci } else if (ObjectPrototypeHasOwnProperty(b, i)) { 5551cb0ef41Sopenharmony_ci return false; 5561cb0ef41Sopenharmony_ci } else { 5571cb0ef41Sopenharmony_ci // Array is sparse. 5581cb0ef41Sopenharmony_ci const keysA = ObjectKeys(a); 5591cb0ef41Sopenharmony_ci for (; i < keysA.length; i++) { 5601cb0ef41Sopenharmony_ci const key = keysA[i]; 5611cb0ef41Sopenharmony_ci if (!ObjectPrototypeHasOwnProperty(b, key) || 5621cb0ef41Sopenharmony_ci !innerDeepEqual(a[key], b[key], strict, memos)) { 5631cb0ef41Sopenharmony_ci return false; 5641cb0ef41Sopenharmony_ci } 5651cb0ef41Sopenharmony_ci } 5661cb0ef41Sopenharmony_ci if (keysA.length !== ObjectKeys(b).length) { 5671cb0ef41Sopenharmony_ci return false; 5681cb0ef41Sopenharmony_ci } 5691cb0ef41Sopenharmony_ci return true; 5701cb0ef41Sopenharmony_ci } 5711cb0ef41Sopenharmony_ci } 5721cb0ef41Sopenharmony_ci } 5731cb0ef41Sopenharmony_ci 5741cb0ef41Sopenharmony_ci // The pair must have equivalent values for every corresponding key. 5751cb0ef41Sopenharmony_ci // Possibly expensive deep test: 5761cb0ef41Sopenharmony_ci for (i = 0; i < keys.length; i++) { 5771cb0ef41Sopenharmony_ci const key = keys[i]; 5781cb0ef41Sopenharmony_ci if (!innerDeepEqual(a[key], b[key], strict, memos)) { 5791cb0ef41Sopenharmony_ci return false; 5801cb0ef41Sopenharmony_ci } 5811cb0ef41Sopenharmony_ci } 5821cb0ef41Sopenharmony_ci return true; 5831cb0ef41Sopenharmony_ci} 5841cb0ef41Sopenharmony_ci 5851cb0ef41Sopenharmony_cifunction isDeepEqual(val1, val2) { 5861cb0ef41Sopenharmony_ci return innerDeepEqual(val1, val2, kLoose); 5871cb0ef41Sopenharmony_ci} 5881cb0ef41Sopenharmony_ci 5891cb0ef41Sopenharmony_cifunction isDeepStrictEqual(val1, val2) { 5901cb0ef41Sopenharmony_ci return innerDeepEqual(val1, val2, kStrict); 5911cb0ef41Sopenharmony_ci} 5921cb0ef41Sopenharmony_ci 5931cb0ef41Sopenharmony_cimodule.exports = { 5941cb0ef41Sopenharmony_ci isDeepEqual, 5951cb0ef41Sopenharmony_ci isDeepStrictEqual, 5961cb0ef41Sopenharmony_ci}; 597