@@ -19,6 +19,7 @@ const {
19
19
} = primordials ;
20
20
21
21
const { compare } = internalBinding ( 'buffer' ) ;
22
+ const types = require ( 'internal/util/types' ) ;
22
23
const {
23
24
isAnyArrayBuffer,
24
25
isArrayBufferView,
@@ -34,8 +35,17 @@ const {
34
35
isBigIntObject,
35
36
isSymbolObject,
36
37
isFloat32Array,
37
- isFloat64Array
38
- } = require ( 'internal/util/types' ) ;
38
+ isFloat64Array,
39
+ isUint8Array,
40
+ isUint8ClampedArray,
41
+ isUint16Array,
42
+ isUint32Array,
43
+ isInt8Array,
44
+ isInt16Array,
45
+ isInt32Array,
46
+ isBigInt64Array,
47
+ isBigUint64Array
48
+ } = types ;
39
49
const {
40
50
getOwnNonIndexProperties,
41
51
propertyFilter : {
@@ -107,6 +117,33 @@ function isEqualBoxedPrimitive(val1, val2) {
107
117
return false ;
108
118
}
109
119
120
+ function isIdenticalTypedArrayType ( a , b ) {
121
+ // Fast path to reduce type checks in the common case.
122
+ const check = types [ `is${ a [ Symbol . toStringTag ] } ` ] ;
123
+ if ( check !== undefined && check ( a ) ) {
124
+ return check ( b ) ;
125
+ }
126
+ // Manipulated Symbol.toStringTag.
127
+ for ( const check of [
128
+ isUint16Array ,
129
+ isUint32Array ,
130
+ isInt8Array ,
131
+ isInt16Array ,
132
+ isInt32Array ,
133
+ isFloat32Array ,
134
+ isFloat64Array ,
135
+ isBigInt64Array ,
136
+ isBigUint64Array ,
137
+ isUint8ClampedArray ,
138
+ isUint8Array
139
+ ] ) {
140
+ if ( check ( a ) ) {
141
+ return check ( b ) ;
142
+ }
143
+ }
144
+ return false ;
145
+ }
146
+
110
147
// Notes: Type tags are historical [[Class]] properties that can be set by
111
148
// FunctionTemplate::SetClassName() in C++ or Symbol.toStringTag in JS
112
149
// and retrieved using Object.prototype.toString.call(obj) in JS
@@ -115,15 +152,8 @@ function isEqualBoxedPrimitive(val1, val2) {
115
152
// There are some unspecified tags in the wild too (e.g. typed array tags).
116
153
// Since tags can be altered, they only serve fast failures
117
154
//
118
- // Typed arrays and buffers are checked by comparing the content in their
119
- // underlying ArrayBuffer. This optimization requires that it's
120
- // reasonable to interpret their underlying memory in the same way,
121
- // which is checked by comparing their type tags.
122
- // (e.g. a Uint8Array and a Uint16Array with the same memory content
123
- // could still be different because they will be interpreted differently).
124
- //
125
155
// For strict comparison, objects should have
126
- // a) The same built-in type tags
156
+ // a) The same built-in type tag.
127
157
// b) The same prototypes.
128
158
129
159
function innerDeepEqual ( val1 , val2 , strict , memos ) {
@@ -164,9 +194,10 @@ function innerDeepEqual(val1, val2, strict, memos) {
164
194
if ( val1Tag !== val2Tag ) {
165
195
return false ;
166
196
}
197
+
167
198
if ( ArrayIsArray ( val1 ) ) {
168
199
// Check for sparse arrays and general fast path
169
- if ( val1 . length !== val2 . length ) {
200
+ if ( ! ArrayIsArray ( val2 ) || val1 . length !== val2 . length ) {
170
201
return false ;
171
202
}
172
203
const filter = strict ? ONLY_ENUMERABLE : ONLY_ENUMERABLE | SKIP_SYMBOLS ;
@@ -176,25 +207,28 @@ function innerDeepEqual(val1, val2, strict, memos) {
176
207
return false ;
177
208
}
178
209
return keyCheck ( val1 , val2 , strict , memos , kIsArray , keys1 ) ;
179
- }
180
- if ( val1Tag === '[object Object]' ) {
210
+ } else if ( val1Tag === '[object Object]' ) {
181
211
return keyCheck ( val1 , val2 , strict , memos , kNoIterator ) ;
182
- }
183
- if ( isDate ( val1 ) ) {
184
- if ( DatePrototypeGetTime ( val1 ) !== DatePrototypeGetTime ( val2 ) ) {
212
+ } else if ( isDate ( val1 ) ) {
213
+ if ( ! isDate ( val2 ) ||
214
+ DatePrototypeGetTime ( val1 ) !== DatePrototypeGetTime ( val2 ) ) {
185
215
return false ;
186
216
}
187
217
} else if ( isRegExp ( val1 ) ) {
188
- if ( ! areSimilarRegExps ( val1 , val2 ) ) {
218
+ if ( ! isRegExp ( val2 ) || ! areSimilarRegExps ( val1 , val2 ) ) {
189
219
return false ;
190
220
}
191
221
} else if ( isNativeError ( val1 ) || val1 instanceof Error ) {
192
222
// Do not compare the stack as it might differ even though the error itself
193
223
// is otherwise identical.
194
- if ( val1 . message !== val2 . message || val1 . name !== val2 . name ) {
224
+ if ( ( ! isNativeError ( val2 ) && ! ( val2 instanceof Error ) ) ||
225
+ val1 . message !== val2 . message ||
226
+ val1 . name !== val2 . name ) {
195
227
return false ;
196
228
}
197
229
} else if ( isArrayBufferView ( val1 ) ) {
230
+ if ( ! isIdenticalTypedArrayType ( val1 , val2 ) )
231
+ return false ;
198
232
if ( ! strict && ( isFloat32Array ( val1 ) || isFloat64Array ( val1 ) ) ) {
199
233
if ( ! areSimilarFloatArrays ( val1 , val2 ) ) {
200
234
return false ;
@@ -223,12 +257,23 @@ function innerDeepEqual(val1, val2, strict, memos) {
223
257
}
224
258
return keyCheck ( val1 , val2 , strict , memos , kIsMap ) ;
225
259
} else if ( isAnyArrayBuffer ( val1 ) ) {
226
- if ( ! areEqualArrayBuffers ( val1 , val2 ) ) {
260
+ if ( ! isAnyArrayBuffer ( val2 ) || ! areEqualArrayBuffers ( val1 , val2 ) ) {
227
261
return false ;
228
262
}
229
- }
230
- if ( ( isBoxedPrimitive ( val1 ) || isBoxedPrimitive ( val2 ) ) &&
231
- ! isEqualBoxedPrimitive ( val1 , val2 ) ) {
263
+ } else if ( isBoxedPrimitive ( val1 ) ) {
264
+ if ( ! isEqualBoxedPrimitive ( val1 , val2 ) ) {
265
+ return false ;
266
+ }
267
+ } else if ( ArrayIsArray ( val2 ) ||
268
+ isArrayBufferView ( val2 ) ||
269
+ isSet ( val2 ) ||
270
+ isMap ( val2 ) ||
271
+ isDate ( val2 ) ||
272
+ isRegExp ( val2 ) ||
273
+ isAnyArrayBuffer ( val2 ) ||
274
+ isBoxedPrimitive ( val2 ) ||
275
+ isNativeError ( val2 ) ||
276
+ val2 instanceof Error ) {
232
277
return false ;
233
278
}
234
279
return keyCheck ( val1 , val2 , strict , memos , kNoIterator ) ;
0 commit comments