@@ -24,6 +24,7 @@ const { compare } = process.binding('buffer');
24
24
const { isSet, isMap, isDate, isRegExp } = process . binding ( 'util' ) ;
25
25
const { objectToString } = require ( 'internal/util' ) ;
26
26
const errors = require ( 'internal/errors' ) ;
27
+ const { propertyIsEnumerable } = Object . prototype ;
27
28
28
29
// The assert module provides functions that throw
29
30
// AssertionError's when particular conditions are not met. The
@@ -165,7 +166,7 @@ function isObjectOrArrayTag(tag) {
165
166
// For strict comparison, objects should have
166
167
// a) The same built-in type tags
167
168
// b) The same prototypes.
168
- function strictDeepEqual ( actual , expected ) {
169
+ function strictDeepEqual ( actual , expected , memos ) {
169
170
if ( typeof actual !== 'object' ) {
170
171
return typeof actual === 'number' && Number . isNaN ( actual ) &&
171
172
Number . isNaN ( expected ) ;
@@ -186,12 +187,12 @@ function strictDeepEqual(actual, expected) {
186
187
// Check for sparse arrays and general fast path
187
188
if ( actual . length !== expected . length )
188
189
return false ;
189
- // Skip testing the part below and continue in the callee function .
190
- return ;
190
+ // Skip testing the part below and continue with the keyCheck .
191
+ return keyCheck ( actual , expected , true , memos ) ;
191
192
}
192
193
if ( actualTag === '[object Object]' ) {
193
- // Skip testing the part below and continue in the callee function .
194
- return ;
194
+ // Skip testing the part below and continue with the keyCheck .
195
+ return keyCheck ( actual , expected , true , memos ) ;
195
196
}
196
197
if ( isDate ( actual ) ) {
197
198
if ( actual . getTime ( ) !== expected . getTime ( ) ) {
@@ -215,10 +216,8 @@ function strictDeepEqual(actual, expected) {
215
216
}
216
217
// Buffer.compare returns true, so actual.length === expected.length
217
218
// if they both only contain numeric keys, we don't need to exam further
218
- if ( Object . keys ( actual ) . length === actual . length &&
219
- Object . keys ( expected ) . length === expected . length ) {
220
- return true ;
221
- }
219
+ return keyCheck ( actual , expected , true , memos , actual . length ,
220
+ expected . length ) ;
222
221
} else if ( typeof actual . valueOf === 'function' ) {
223
222
const actualValue = actual . valueOf ( ) ;
224
223
// Note: Boxed string keys are going to be compared again by Object.keys
@@ -232,15 +231,14 @@ function strictDeepEqual(actual, expected) {
232
231
lengthActual = actual . length ;
233
232
lengthExpected = expected . length ;
234
233
}
235
- if ( Object . keys ( actual ) . length === lengthActual &&
236
- Object . keys ( expected ) . length === lengthExpected ) {
237
- return true ;
238
- }
234
+ return keyCheck ( actual , expected , true , memos , lengthActual ,
235
+ lengthExpected ) ;
239
236
}
240
237
}
238
+ return keyCheck ( actual , expected , true , memos ) ;
241
239
}
242
240
243
- function looseDeepEqual ( actual , expected ) {
241
+ function looseDeepEqual ( actual , expected , memos ) {
244
242
if ( actual === null || typeof actual !== 'object' ) {
245
243
if ( expected === null || typeof expected !== 'object' ) {
246
244
// eslint-disable-next-line eqeqeq
@@ -274,33 +272,62 @@ function looseDeepEqual(actual, expected) {
274
272
} else if ( isArguments ( actualTag ) || isArguments ( expectedTag ) ) {
275
273
return false ;
276
274
}
275
+ return keyCheck ( actual , expected , false , memos ) ;
277
276
}
278
277
279
- function innerDeepEqual ( actual , expected , strict , memos ) {
280
- // All identical values are equivalent, as determined by ===.
281
- if ( actual === expected ) {
282
- if ( actual !== 0 )
283
- return true ;
284
- return strict ? Object . is ( actual , expected ) : true ;
285
- }
286
-
287
- // Returns a boolean if (not) equal and undefined in case we have to check
288
- // further.
289
- const partialCheck = strict ?
290
- strictDeepEqual ( actual , expected ) :
291
- looseDeepEqual ( actual , expected ) ;
292
-
293
- if ( partialCheck !== undefined ) {
294
- return partialCheck ;
295
- }
296
-
278
+ function keyCheck ( actual , expected , strict , memos , lengthA , lengthB ) {
297
279
// For all remaining Object pairs, including Array, objects and Maps,
298
280
// equivalence is determined by having:
299
281
// a) The same number of owned enumerable properties
300
282
// b) The same set of keys/indexes (although not necessarily the same order)
301
283
// c) Equivalent values for every corresponding key/index
302
284
// d) For Sets and Maps, equal contents
303
285
// Note: this accounts for both named and indexed properties on Arrays.
286
+ var aKeys = Object . keys ( actual ) ;
287
+ var bKeys = Object . keys ( expected ) ;
288
+ var i ;
289
+
290
+ // The pair must have the same number of owned properties.
291
+ if ( aKeys . length !== bKeys . length )
292
+ return false ;
293
+
294
+ if ( strict ) {
295
+ var symbolKeysA = Object . getOwnPropertySymbols ( actual ) ;
296
+ var symbolKeysB = Object . getOwnPropertySymbols ( expected ) ;
297
+ if ( symbolKeysA . length !== 0 ) {
298
+ symbolKeysA = symbolKeysA . filter ( ( k ) =>
299
+ propertyIsEnumerable . call ( actual , k ) ) ;
300
+ symbolKeysB = symbolKeysB . filter ( ( k ) =>
301
+ propertyIsEnumerable . call ( expected , k ) ) ;
302
+ if ( symbolKeysA . length !== symbolKeysB . length )
303
+ return false ;
304
+ } else if ( symbolKeysB . length !== 0 && symbolKeysB . filter ( ( k ) =>
305
+ propertyIsEnumerable . call ( expected , k ) ) . length !== 0 ) {
306
+ return false ;
307
+ }
308
+ if ( lengthA !== undefined ) {
309
+ if ( aKeys . length !== lengthA || bKeys . length !== lengthB )
310
+ return false ;
311
+ if ( symbolKeysA . length === 0 )
312
+ return true ;
313
+ aKeys = [ ] ;
314
+ bKeys = [ ] ;
315
+ }
316
+ if ( symbolKeysA . length !== 0 ) {
317
+ aKeys . push ( ...symbolKeysA ) ;
318
+ bKeys . push ( ...symbolKeysB ) ;
319
+ }
320
+ }
321
+
322
+ // Cheap key test:
323
+ const keys = { } ;
324
+ for ( i = 0 ; i < aKeys . length ; i ++ ) {
325
+ keys [ aKeys [ i ] ] = true ;
326
+ }
327
+ for ( i = 0 ; i < aKeys . length ; i ++ ) {
328
+ if ( keys [ bKeys [ i ] ] === undefined )
329
+ return false ;
330
+ }
304
331
305
332
// Use memos to handle cycles.
306
333
if ( memos === undefined ) {
@@ -323,25 +350,6 @@ function innerDeepEqual(actual, expected, strict, memos) {
323
350
memos . position ++ ;
324
351
}
325
352
326
- const aKeys = Object . keys ( actual ) ;
327
- const bKeys = Object . keys ( expected ) ;
328
- var i ;
329
-
330
- // The pair must have the same number of owned properties
331
- // (keys incorporates hasOwnProperty).
332
- if ( aKeys . length !== bKeys . length )
333
- return false ;
334
-
335
- // Cheap key test:
336
- const keys = { } ;
337
- for ( i = 0 ; i < aKeys . length ; i ++ ) {
338
- keys [ aKeys [ i ] ] = true ;
339
- }
340
- for ( i = 0 ; i < aKeys . length ; i ++ ) {
341
- if ( keys [ bKeys [ i ] ] === undefined )
342
- return false ;
343
- }
344
-
345
353
memos . actual . set ( actual , memos . position ) ;
346
354
memos . expected . set ( expected , memos . position ) ;
347
355
@@ -353,6 +361,21 @@ function innerDeepEqual(actual, expected, strict, memos) {
353
361
return areEq ;
354
362
}
355
363
364
+ function innerDeepEqual ( actual , expected , strict , memos ) {
365
+ // All identical values are equivalent, as determined by ===.
366
+ if ( actual === expected ) {
367
+ if ( actual !== 0 )
368
+ return true ;
369
+ return strict ? Object . is ( actual , expected ) : true ;
370
+ }
371
+
372
+ // Check more closely if actual and expected are equal.
373
+ if ( strict === true )
374
+ return strictDeepEqual ( actual , expected , memos ) ;
375
+
376
+ return looseDeepEqual ( actual , expected , memos ) ;
377
+ }
378
+
356
379
function setHasEqualElement ( set , val1 , strict , memo ) {
357
380
// Go looking.
358
381
for ( const val2 of set ) {
0 commit comments