| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 6a55802 commit a0f338e
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,9 +19,11 @@ const { | |||
| 19 | 19 | Set, | |
| 20 | 20 | StringPrototypeValueOf, | |
| 21 | 21 | SymbolPrototypeValueOf, | |
| 22 | + SymbolToStringTag, | ||
| 22 | 23 | } = primordials; | |
| 23 | 24 | ||
| 24 | 25 | const { compare } = internalBinding('buffer'); | |
| 26 | + const types = require('internal/util/types'); | ||
| 25 | 27 | const { | |
| 26 | 28 | isAnyArrayBuffer, | |
| 27 | 29 | isArrayBufferView, | |
@@ -37,8 +39,17 @@ const { | |||
| 37 | 39 | isBigIntObject, | |
| 38 | 40 | isSymbolObject, | |
| 39 | 41 | isFloat32Array, | |
| 40 | - isFloat64Array | ||
| 41 | - } = require('internal/util/types'); | ||
| 42 | + isFloat64Array, | ||
| 43 | + isUint8Array, | ||
| 44 | + isUint8ClampedArray, | ||
| 45 | + isUint16Array, | ||
| 46 | + isUint32Array, | ||
| 47 | + isInt8Array, | ||
| 48 | + isInt16Array, | ||
| 49 | + isInt32Array, | ||
| 50 | + isBigInt64Array, | ||
| 51 | + isBigUint64Array | ||
| 52 | + } = types; | ||
| 42 | 53 | const { | |
| 43 | 54 | getOwnNonIndexProperties, | |
| 44 | 55 | propertyFilter: { | |
@@ -110,6 +121,33 @@ function isEqualBoxedPrimitive(val1, val2) { | |||
| 110 | 121 | return false; | |
| 111 | 122 | } | |
| 112 | 123 | ||
| 124 | + function isIdenticalTypedArrayType(a, b) { | ||
| 125 | + // Fast path to reduce type checks in the common case. | ||
| 126 | + const check = types[`is${a[SymbolToStringTag]}`]; | ||
| 127 | + if (check !== undefined && check(a)) { | ||
| 128 | + return check(b); | ||
| 129 | + } | ||
| 130 | + // Manipulated Symbol.toStringTag. | ||
| 131 | + for (const check of [ | ||
| 132 | + isUint16Array, | ||
| 133 | + isUint32Array, | ||
| 134 | + isInt8Array, | ||
| 135 | + isInt16Array, | ||
| 136 | + isInt32Array, | ||
| 137 | + isFloat32Array, | ||
| 138 | + isFloat64Array, | ||
| 139 | + isBigInt64Array, | ||
| 140 | + isBigUint64Array, | ||
| 141 | + isUint8ClampedArray, | ||
| 142 | + isUint8Array | ||
| 143 | + ]) { | ||
| 144 | + if (check(a)) { | ||
| 145 | + return check(b); | ||
| 146 | + } | ||
| 147 | + } | ||
| 148 | + return false; | ||
| 149 | + } | ||
| 150 | + | ||
| 113 | 151 | // Notes: Type tags are historical [[Class]] properties that can be set by | |
| 114 | 152 | // FunctionTemplate::SetClassName() in C++ or Symbol.toStringTag in JS | |
| 115 | 153 | // and retrieved using Object.prototype.toString.call(obj) in JS | |
@@ -118,15 +156,8 @@ function isEqualBoxedPrimitive(val1, val2) { | |||
| 118 | 156 | // There are some unspecified tags in the wild too (e.g. typed array tags). | |
| 119 | 157 | // Since tags can be altered, they only serve fast failures | |
| 120 | 158 | // | |
| 121 | - // Typed arrays and buffers are checked by comparing the content in their | ||
| 122 | - // underlying ArrayBuffer. This optimization requires that it's | ||
| 123 | - // reasonable to interpret their underlying memory in the same way, | ||
| 124 | - // which is checked by comparing their type tags. | ||
| 125 | - // (e.g. a Uint8Array and a Uint16Array with the same memory content | ||
| 126 | - // could still be different because they will be interpreted differently). | ||
| 127 | - // | ||
| 128 | 159 | // For strict comparison, objects should have | |
| 129 | - // a) The same built-in type tags | ||
| 160 | + // a) The same built-in type tag. | ||
| 130 | 161 | // b) The same prototypes. | |
| 131 | 162 | ||
| 132 | 163 | function innerDeepEqual(val1, val2, strict, memos) { | |
@@ -167,9 +198,10 @@ function innerDeepEqual(val1, val2, strict, memos) { | |||
| 167 | 198 | if (val1Tag !== val2Tag) { | |
| 168 | 199 | return false; | |
| 169 | 200 | } | |
| 201 | + | ||
| 170 | 202 | if (ArrayIsArray(val1)) { | |
| 171 | 203 | // Check for sparse arrays and general fast path | |
| 172 | - if (val1.length !== val2.length) { | ||
| 204 | + if (!ArrayIsArray(val2) || val1.length !== val2.length) { | ||
| 173 | 205 | return false; | |
| 174 | 206 | } | |
| 175 | 207 | const filter = strict ? ONLY_ENUMERABLE : ONLY_ENUMERABLE | SKIP_SYMBOLS; | |
@@ -179,25 +211,28 @@ function innerDeepEqual(val1, val2, strict, memos) { | |||
| 179 | 211 | return false; | |
| 180 | 212 | } | |
| 181 | 213 | return keyCheck(val1, val2, strict, memos, kIsArray, keys1); | |
| 182 | - } | ||
| 183 | - if (val1Tag === '[object Object]') { | ||
| 214 | + } else if (val1Tag === '[object Object]') { | ||
| 184 | 215 | return keyCheck(val1, val2, strict, memos, kNoIterator); | |
| 185 | - } | ||
| 186 | - if (isDate(val1)) { | ||
| 187 | - if (DatePrototypeGetTime(val1) !== DatePrototypeGetTime(val2)) { | ||
| 216 | + } else if (isDate(val1)) { | ||
| 217 | + if (!isDate(val2) || | ||
| 218 | + DatePrototypeGetTime(val1) !== DatePrototypeGetTime(val2)) { | ||
| 188 | 219 | return false; | |
| 189 | 220 | } | |
| 190 | 221 | } else if (isRegExp(val1)) { | |
| 191 | - if (!areSimilarRegExps(val1, val2)) { | ||
| 222 | + if (!isRegExp(val2) || !areSimilarRegExps(val1, val2)) { | ||
| 192 | 223 | return false; | |
| 193 | 224 | } | |
| 194 | 225 | } else if (isNativeError(val1) || val1 instanceof Error) { | |
| 195 | 226 | // Do not compare the stack as it might differ even though the error itself | |
| 196 | 227 | // is otherwise identical. | |
| 197 | - if (val1.message !== val2.message || val1.name !== val2.name) { | ||
| 228 | + if ((!isNativeError(val2) && !(val2 instanceof Error)) || | ||
| 229 | + val1.message !== val2.message || | ||
| 230 | + val1.name !== val2.name) { | ||
| 198 | 231 | return false; | |
| 199 | 232 | } | |
| 200 | 233 | } else if (isArrayBufferView(val1)) { | |
| 234 | + if (!isIdenticalTypedArrayType(val1, val2)) | ||
| 235 | + return false; | ||
| 201 | 236 | if (!strict && (isFloat32Array(val1) || isFloat64Array(val1))) { | |
| 202 | 237 | if (!areSimilarFloatArrays(val1, val2)) { | |
| 203 | 238 | return false; | |
@@ -226,12 +261,23 @@ function innerDeepEqual(val1, val2, strict, memos) { | |||
| 226 | 261 | } | |
| 227 | 262 | return keyCheck(val1, val2, strict, memos, kIsMap); | |
| 228 | 263 | } else if (isAnyArrayBuffer(val1)) { | |
| 229 | - if (!areEqualArrayBuffers(val1, val2)) { | ||
| 264 | + if (!isAnyArrayBuffer(val2) || !areEqualArrayBuffers(val1, val2)) { | ||
| 230 | 265 | return false; | |
| 231 | 266 | } | |
| 232 | - } | ||
| 233 | - if ((isBoxedPrimitive(val1) || isBoxedPrimitive(val2)) && | ||
| 234 | - !isEqualBoxedPrimitive(val1, val2)) { | ||
| 267 | + } else if (isBoxedPrimitive(val1)) { | ||
| 268 | + if (!isEqualBoxedPrimitive(val1, val2)) { | ||
| 269 | + return false; | ||
| 270 | + } | ||
| 271 | + } else if (ArrayIsArray(val2) || | ||
| 272 | + isArrayBufferView(val2) || | ||
| 273 | + isSet(val2) || | ||
| 274 | + isMap(val2) || | ||
| 275 | + isDate(val2) || | ||
| 276 | + isRegExp(val2) || | ||
| 277 | + isAnyArrayBuffer(val2) || | ||
| 278 | + isBoxedPrimitive(val2) || | ||
| 279 | + isNativeError(val2) || | ||
| 280 | + val2 instanceof Error) { | ||
| 235 | 281 | return false; | |
| 236 | 282 | } | |
| 237 | 283 | return keyCheck(val1, val2, strict, memos, kNoIterator); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1138,3 +1138,69 @@ assert.throws( | |||
| 1138 | 1138 | // The descriptor is not compared. | |
| 1139 | 1139 | assertDeepAndStrictEqual(a, { a: 5 }); | |
| 1140 | 1140 | } | |
| 1141 | + | ||
| 1142 | + // Verify object types being identical on both sides. | ||
| 1143 | + { | ||
| 1144 | + let a = Buffer.from('test'); | ||
| 1145 | + let b = Object.create( | ||
| 1146 | + Object.getPrototypeOf(a), | ||
| 1147 | + Object.getOwnPropertyDescriptors(a) | ||
| 1148 | + ); | ||
| 1149 | + Object.defineProperty(b, Symbol.toStringTag, { | ||
| 1150 | + value: 'Uint8Array' | ||
| 1151 | + }); | ||
| 1152 | + assertNotDeepOrStrict(a, b); | ||
| 1153 | + | ||
| 1154 | + a = new Uint8Array(10); | ||
| 1155 | + b = new Int8Array(10); | ||
| 1156 | + Object.defineProperty(b, Symbol.toStringTag, { | ||
| 1157 | + value: 'Uint8Array' | ||
| 1158 | + }); | ||
| 1159 | + Object.setPrototypeOf(b, Uint8Array.prototype); | ||
| 1160 | + assertNotDeepOrStrict(a, b); | ||
| 1161 | + | ||
| 1162 | + a = [1, 2, 3]; | ||
| 1163 | + b = { 0: 1, 1: 2, 2: 3 }; | ||
| 1164 | + Object.setPrototypeOf(b, Array.prototype); | ||
| 1165 | + Object.defineProperty(b, 'length', { value: 3, enumerable: false }); | ||
| 1166 | + Object.defineProperty(b, Symbol.toStringTag, { | ||
| 1167 | + value: 'Array' | ||
| 1168 | + }); | ||
| 1169 | + assertNotDeepOrStrict(a, b); | ||
| 1170 | + | ||
| 1171 | + a = new Date(2000); | ||
| 1172 | + b = Object.create( | ||
| 1173 | + Object.getPrototypeOf(a), | ||
| 1174 | + Object.getOwnPropertyDescriptors(a) | ||
| 1175 | + ); | ||
| 1176 | + Object.defineProperty(b, Symbol.toStringTag, { | ||
| 1177 | + value: 'Date' | ||
| 1178 | + }); | ||
| 1179 | + assertNotDeepOrStrict(a, b); | ||
| 1180 | + | ||
| 1181 | + a = /abc/g; | ||
| 1182 | + b = Object.create( | ||
| 1183 | + Object.getPrototypeOf(a), | ||
| 1184 | + Object.getOwnPropertyDescriptors(a) | ||
| 1185 | + ); | ||
| 1186 | + Object.defineProperty(b, Symbol.toStringTag, { | ||
| 1187 | + value: 'RegExp' | ||
| 1188 | + }); | ||
| 1189 | + assertNotDeepOrStrict(a, b); | ||
| 1190 | + | ||
| 1191 | + a = []; | ||
| 1192 | + b = /abc/; | ||
| 1193 | + Object.setPrototypeOf(b, Array.prototype); | ||
| 1194 | + Object.defineProperty(b, Symbol.toStringTag, { | ||
| 1195 | + value: 'Array' | ||
| 1196 | + }); | ||
| 1197 | + assertNotDeepOrStrict(a, b); | ||
| 1198 | + | ||
| 1199 | + a = Object.create(null); | ||
| 1200 | + b = new RangeError('abc'); | ||
| 1201 | + Object.defineProperty(a, Symbol.toStringTag, { | ||
| 1202 | + value: 'Error' | ||
| 1203 | + }); | ||
| 1204 | + Object.setPrototypeOf(b, null); | ||
| 1205 | + assertNotDeepOrStrict(a, b, assert.AssertionError); | ||
| 1206 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments