@@ -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 ) ;
0 commit comments