@@ -19,9 +19,11 @@ const {
1919 Set,
2020 StringPrototypeValueOf,
2121 SymbolPrototypeValueOf,
22+ SymbolToStringTag,
2223} = primordials ;
2324
2425const { compare } = internalBinding ( 'buffer' ) ;
26+ const types = require ( 'internal/util/types' ) ;
2527const {
2628 isAnyArrayBuffer,
2729 isArrayBufferView,
@@ -37,8 +39,17 @@ const {
3739 isBigIntObject,
3840 isSymbolObject,
3941 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 ;
4253const {
4354 getOwnNonIndexProperties,
4455 propertyFilter : {
@@ -110,6 +121,33 @@ function isEqualBoxedPrimitive(val1, val2) {
110121 return false ;
111122}
112123
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+
113151// Notes: Type tags are historical [[Class]] properties that can be set by
114152// FunctionTemplate::SetClassName() in C++ or Symbol.toStringTag in JS
115153// and retrieved using Object.prototype.toString.call(obj) in JS
@@ -118,15 +156,8 @@ function isEqualBoxedPrimitive(val1, val2) {
118156// There are some unspecified tags in the wild too (e.g. typed array tags).
119157// Since tags can be altered, they only serve fast failures
120158//
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- //
128159// For strict comparison, objects should have
129- // a) The same built-in type tags
160+ // a) The same built-in type tag.
130161// b) The same prototypes.
131162
132163function innerDeepEqual ( val1 , val2 , strict , memos ) {
@@ -167,9 +198,10 @@ function innerDeepEqual(val1, val2, strict, memos) {
167198 if ( val1Tag !== val2Tag ) {
168199 return false ;
169200 }
201+
170202 if ( ArrayIsArray ( val1 ) ) {
171203 // Check for sparse arrays and general fast path
172- if ( val1 . length !== val2 . length ) {
204+ if ( ! ArrayIsArray ( val2 ) || val1 . length !== val2 . length ) {
173205 return false ;
174206 }
175207 const filter = strict ? ONLY_ENUMERABLE : ONLY_ENUMERABLE | SKIP_SYMBOLS ;
@@ -179,25 +211,28 @@ function innerDeepEqual(val1, val2, strict, memos) {
179211 return false ;
180212 }
181213 return keyCheck ( val1 , val2 , strict , memos , kIsArray , keys1 ) ;
182- }
183- if ( val1Tag === '[object Object]' ) {
214+ } else if ( val1Tag === '[object Object]' ) {
184215 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 ) ) {
188219 return false ;
189220 }
190221 } else if ( isRegExp ( val1 ) ) {
191- if ( ! areSimilarRegExps ( val1 , val2 ) ) {
222+ if ( ! isRegExp ( val2 ) || ! areSimilarRegExps ( val1 , val2 ) ) {
192223 return false ;
193224 }
194225 } else if ( isNativeError ( val1 ) || val1 instanceof Error ) {
195226 // Do not compare the stack as it might differ even though the error itself
196227 // 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 ) {
198231 return false ;
199232 }
200233 } else if ( isArrayBufferView ( val1 ) ) {
234+ if ( ! isIdenticalTypedArrayType ( val1 , val2 ) )
235+ return false ;
201236 if ( ! strict && ( isFloat32Array ( val1 ) || isFloat64Array ( val1 ) ) ) {
202237 if ( ! areSimilarFloatArrays ( val1 , val2 ) ) {
203238 return false ;
@@ -226,12 +261,23 @@ function innerDeepEqual(val1, val2, strict, memos) {
226261 }
227262 return keyCheck ( val1 , val2 , strict , memos , kIsMap ) ;
228263 } else if ( isAnyArrayBuffer ( val1 ) ) {
229- if ( ! areEqualArrayBuffers ( val1 , val2 ) ) {
264+ if ( ! isAnyArrayBuffer ( val2 ) || ! areEqualArrayBuffers ( val1 , val2 ) ) {
230265 return false ;
231266 }
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 ) {
235281 return false ;
236282 }
237283 return keyCheck ( val1 , val2 , strict , memos , kNoIterator ) ;
0 commit comments