@@ -19,6 +19,7 @@ const {
1919} = primordials ;
2020
2121const { compare } = internalBinding ( 'buffer' ) ;
22+ const types = require ( 'internal/util/types' ) ;
2223const {
2324 isAnyArrayBuffer,
2425 isArrayBufferView,
@@ -34,8 +35,17 @@ const {
3435 isBigIntObject,
3536 isSymbolObject,
3637 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 ;
3949const {
4050 getOwnNonIndexProperties,
4151 propertyFilter : {
@@ -107,6 +117,33 @@ function isEqualBoxedPrimitive(val1, val2) {
107117 return false ;
108118}
109119
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+
110147// Notes: Type tags are historical [[Class]] properties that can be set by
111148// FunctionTemplate::SetClassName() in C++ or Symbol.toStringTag in JS
112149// and retrieved using Object.prototype.toString.call(obj) in JS
@@ -115,15 +152,8 @@ function isEqualBoxedPrimitive(val1, val2) {
115152// There are some unspecified tags in the wild too (e.g. typed array tags).
116153// Since tags can be altered, they only serve fast failures
117154//
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- //
125155// For strict comparison, objects should have
126- // a) The same built-in type tags
156+ // a) The same built-in type tag.
127157// b) The same prototypes.
128158
129159function innerDeepEqual ( val1 , val2 , strict , memos ) {
@@ -164,9 +194,10 @@ function innerDeepEqual(val1, val2, strict, memos) {
164194 if ( val1Tag !== val2Tag ) {
165195 return false ;
166196 }
197+
167198 if ( ArrayIsArray ( val1 ) ) {
168199 // Check for sparse arrays and general fast path
169- if ( val1 . length !== val2 . length ) {
200+ if ( ! ArrayIsArray ( val2 ) || val1 . length !== val2 . length ) {
170201 return false ;
171202 }
172203 const filter = strict ? ONLY_ENUMERABLE : ONLY_ENUMERABLE | SKIP_SYMBOLS ;
@@ -176,25 +207,28 @@ function innerDeepEqual(val1, val2, strict, memos) {
176207 return false ;
177208 }
178209 return keyCheck ( val1 , val2 , strict , memos , kIsArray , keys1 ) ;
179- }
180- if ( val1Tag === '[object Object]' ) {
210+ } else if ( val1Tag === '[object Object]' ) {
181211 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 ) ) {
185215 return false ;
186216 }
187217 } else if ( isRegExp ( val1 ) ) {
188- if ( ! areSimilarRegExps ( val1 , val2 ) ) {
218+ if ( ! isRegExp ( val2 ) || ! areSimilarRegExps ( val1 , val2 ) ) {
189219 return false ;
190220 }
191221 } else if ( isNativeError ( val1 ) || val1 instanceof Error ) {
192222 // Do not compare the stack as it might differ even though the error itself
193223 // 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 ) {
195227 return false ;
196228 }
197229 } else if ( isArrayBufferView ( val1 ) ) {
230+ if ( ! isIdenticalTypedArrayType ( val1 , val2 ) )
231+ return false ;
198232 if ( ! strict && ( isFloat32Array ( val1 ) || isFloat64Array ( val1 ) ) ) {
199233 if ( ! areSimilarFloatArrays ( val1 , val2 ) ) {
200234 return false ;
@@ -223,12 +257,23 @@ function innerDeepEqual(val1, val2, strict, memos) {
223257 }
224258 return keyCheck ( val1 , val2 , strict , memos , kIsMap ) ;
225259 } else if ( isAnyArrayBuffer ( val1 ) ) {
226- if ( ! areEqualArrayBuffers ( val1 , val2 ) ) {
260+ if ( ! isAnyArrayBuffer ( val2 ) || ! areEqualArrayBuffers ( val1 , val2 ) ) {
227261 return false ;
228262 }
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 ) {
232277 return false ;
233278 }
234279 return keyCheck ( val1 , val2 , strict , memos , kNoIterator ) ;
0 commit comments