23
23
// UTILITY
24
24
const compare = process . binding ( 'buffer' ) . compare ;
25
25
const util = require ( 'util' ) ;
26
+ const objectToString = require ( 'internal/util' ) . objectToString ;
26
27
const Buffer = require ( 'buffer' ) . Buffer ;
27
- const pToString = ( obj ) => Object . prototype . toString . call ( obj ) ;
28
28
29
29
// The assert module provides functions that throw
30
30
// AssertionError's when particular conditions are not met. The
@@ -136,117 +136,177 @@ assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
136
136
}
137
137
} ;
138
138
139
+ function areSimilarRegExps ( a , b ) {
140
+ return a . source === b . source && a . flags === b . flags ;
141
+ }
142
+
143
+ function areSimilarTypedArrays ( a , b ) {
144
+ return compare ( Buffer . from ( a . buffer ,
145
+ a . byteOffset ,
146
+ a . byteLength ) ,
147
+ Buffer . from ( b . buffer ,
148
+ b . byteOffset ,
149
+ b . byteLength ) ) === 0 ;
150
+ }
151
+
152
+ function isNullOrNonObj ( object ) {
153
+ return object === null || typeof object !== 'object' ;
154
+ }
155
+
156
+ function isFloatTypedArrayTag ( tag ) {
157
+ return tag === '[object Float32Array]' || tag === '[object Float64Array]' ;
158
+ }
159
+
160
+ function isArguments ( tag ) {
161
+ return tag === '[object Arguments]' ;
162
+ }
163
+
139
164
function _deepEqual ( actual , expected , strict , memos ) {
140
165
// All identical values are equivalent, as determined by ===.
141
166
if ( actual === expected ) {
142
167
return true ;
168
+ }
143
169
144
- // If both values are instances of buffers, equivalence is
145
- // determined by comparing the values and ensuring the result
146
- // === 0.
147
- } else if ( actual instanceof Buffer && expected instanceof Buffer ) {
148
- return compare ( actual , expected ) === 0 ;
149
-
150
- // If the expected value is a Date object, the actual value is
151
- // equivalent if it is also a Date object that refers to the same time.
152
- } else if ( util . isDate ( actual ) && util . isDate ( expected ) ) {
153
- return actual . getTime ( ) === expected . getTime ( ) ;
154
-
155
- // If the expected value is a RegExp object, the actual value is
156
- // equivalent if it is also a RegExp object with the same source and
157
- // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
158
- } else if ( util . isRegExp ( actual ) && util . isRegExp ( expected ) ) {
159
- return actual . source === expected . source &&
160
- actual . global === expected . global &&
161
- actual . multiline === expected . multiline &&
162
- actual . lastIndex === expected . lastIndex &&
163
- actual . ignoreCase === expected . ignoreCase ;
164
-
165
- // If both values are primitives, equivalence is determined by
166
- // == or, if checking for strict equivalence, ===.
167
- } else if ( ( actual === null || typeof actual !== 'object' ) &&
168
- ( expected === null || typeof expected !== 'object' ) ) {
170
+ // For primitives / functions
171
+ // (determined by typeof value !== 'object'),
172
+ // or null, equivalence is determined by === or ==.
173
+ if ( isNullOrNonObj ( actual ) && isNullOrNonObj ( expected ) ) {
169
174
return strict ? actual === expected : actual == expected ;
175
+ }
170
176
171
- // If both values are instances of typed arrays, wrap their underlying
172
- // ArrayBuffers in a Buffer to increase performance.
173
- // This optimization requires the arrays to have the same type as checked by
174
- // Object.prototype.toString (pToString). Never perform binary
175
- // comparisons for Float*Arrays, though, since +0 === -0 is true despite the
176
- // two values' bit patterns not being identical.
177
- } else if ( ArrayBuffer . isView ( actual ) && ArrayBuffer . isView ( expected ) &&
178
- pToString ( actual ) === pToString ( expected ) &&
179
- ! ( actual instanceof Float32Array ||
180
- actual instanceof Float64Array ) ) {
181
- return compare ( Buffer . from ( actual . buffer ,
182
- actual . byteOffset ,
183
- actual . byteLength ) ,
184
- Buffer . from ( expected . buffer ,
185
- expected . byteOffset ,
186
- expected . byteLength ) ) === 0 ;
187
-
188
- // For all other Object pairs, including Array objects, equivalence is
189
- // determined by having the same number of owned properties (as verified
190
- // with Object.prototype.hasOwnProperty.call), the same set of keys
191
- // (although not necessarily the same order), equivalent values for every
192
- // corresponding key, and an identical 'prototype' property. Note: this
193
- // accounts for both named and indexed properties on Arrays.
194
- } else {
195
- memos = memos || { actual : [ ] , expected : [ ] } ;
177
+ // If they bypass the previous check, then at least
178
+ // one of them must be an non-null object.
179
+ // If the other one is null or undefined, they must not be equal.
180
+ if ( actual === null || actual === undefined ||
181
+ expected === null || expected === undefined )
182
+ return false ;
196
183
197
- const actualIndex = memos . actual . indexOf ( actual ) ;
198
- if ( actualIndex !== - 1 ) {
199
- if ( actualIndex === memos . expected . indexOf ( expected ) ) {
200
- return true ;
201
- }
184
+ // Notes: Type tags are historical [[Class]] properties that can be set by
185
+ // FunctionTemplate::SetClassName() in C++ or Symbol.toStringTag in JS
186
+ // and retrieved using Object.prototype.toString.call(obj) in JS
187
+ // See https://tc39.github.io/ecma262/#sec-object.prototype.tostring
188
+ // for a list of tags pre-defined in the spec.
189
+ // There are some unspecified tags in the wild too (e.g. typed array tags).
190
+ // Since tags can be altered, they only serve fast failures
191
+ const actualTag = objectToString ( actual ) ;
192
+ const expectedTag = objectToString ( expected ) ;
193
+
194
+ // Passing null or undefined to Object.getPrototypeOf() will throw
195
+ // so this must done after previous checks.
196
+ // For strict comparison, objects should have
197
+ // a) The same prototypes.
198
+ // b) The same built-in type tags
199
+ if ( strict ) {
200
+ if ( Object . getPrototypeOf ( actual ) !== Object . getPrototypeOf ( expected ) ) {
201
+ return false ;
202
202
}
203
+ }
203
204
204
- memos . actual . push ( actual ) ;
205
- memos . expected . push ( expected ) ;
205
+ // Do fast checks for builtin types.
206
+ // If they don't match, they must not be equal.
207
+ // If they match, return true for non-strict comparison.
208
+ // For strict comparison we need to exam further.
206
209
207
- return objEquiv ( actual , expected , strict , memos ) ;
210
+ // If both values are Date objects,
211
+ // check if the time underneath are equal first.
212
+ if ( util . isDate ( actual ) && util . isDate ( expected ) ) {
213
+ if ( actual . getTime ( ) !== expected . getTime ( ) ) {
214
+ return false ;
215
+ } else if ( ! strict ) {
216
+ return true ; // Skip further checks for non-strict comparison.
217
+ }
208
218
}
209
- }
210
219
211
- function isArguments ( object ) {
212
- return Object . prototype . toString . call ( object ) === '[object Arguments]' ;
213
- }
220
+ // If both values are RegExp, check if they have
221
+ // the same source and flags first
222
+ if ( util . isRegExp ( actual ) && util . isRegExp ( expected ) ) {
223
+ if ( ! areSimilarRegExps ( actual , expected ) ) {
224
+ return false ;
225
+ } else if ( ! strict ) {
226
+ return true ; // Skip further checks for non-strict comparison.
227
+ }
228
+ }
214
229
215
- function objEquiv ( a , b , strict , actualVisitedObjects ) {
216
- if ( a === null || a === undefined || b === null || b === undefined )
230
+ // Ensure reflexivity of deepEqual with `arguments` objects.
231
+ // See https://github.com/nodejs/node-v0.x-archive/pull/7178
232
+ if ( isArguments ( actualTag ) !== isArguments ( expectedTag ) ) {
217
233
return false ;
234
+ }
235
+
236
+ // Check typed arrays and buffers by comparing the content in their
237
+ // underlying ArrayBuffer. This optimization requires that it's
238
+ // reasonable to interpret their underlying memory in the same way,
239
+ // which is checked by comparing their type tags.
240
+ // (e.g. a Uint8Array and a Uint16Array with the same memory content
241
+ // could still be different because they will be interpreted differently)
242
+ // Never perform binary comparisons for Float*Arrays, though,
243
+ // since e.g. +0 === -0 is true despite the two values' bit patterns
244
+ // not being identical.
245
+ if ( ArrayBuffer . isView ( actual ) && ArrayBuffer . isView ( expected ) &&
246
+ actualTag === expectedTag && ! isFloatTypedArrayTag ( actualTag ) ) {
247
+ if ( ! areSimilarTypedArrays ( actual , expected ) ) {
248
+ return false ;
249
+ } else if ( ! strict ) {
250
+ return true ; // Skip further checks for non-strict comparison.
251
+ }
252
+
253
+ // Buffer.compare returns true, so actual.length === expected.length
254
+ // if they both only contain numeric keys, we don't need to exam further
255
+ if ( Object . keys ( actual ) . length === actual . length &&
256
+ Object . keys ( expected ) . length === expected . length ) {
257
+ return true ;
258
+ }
259
+ }
260
+
261
+ // For all other Object pairs, including Array objects,
262
+ // equivalence is determined by having:
263
+ // a) The same number of owned enumerable properties
264
+ // b) The same set of keys/indexes (although not necessarily the same order)
265
+ // c) Equivalent values for every corresponding key/index
266
+ // Note: this accounts for both named and indexed properties on Arrays.
267
+
268
+ // Use memos to handle cycles.
269
+ memos = memos || { actual : [ ] , expected : [ ] } ;
270
+ const actualIndex = memos . actual . indexOf ( actual ) ;
271
+ if ( actualIndex !== - 1 ) {
272
+ if ( actualIndex === memos . expected . indexOf ( expected ) ) {
273
+ return true ;
274
+ }
275
+ }
276
+ memos . actual . push ( actual ) ;
277
+ memos . expected . push ( expected ) ;
218
278
219
- // If one is a primitive, the other must be the same.
279
+ return objEquiv ( actual , expected , strict , memos ) ;
280
+ }
281
+
282
+ function objEquiv ( a , b , strict , actualVisitedObjects ) {
283
+ // If one of them is a primitive, the other must be the same.
220
284
if ( util . isPrimitive ( a ) || util . isPrimitive ( b ) )
221
285
return a === b ;
222
- if ( strict && Object . getPrototypeOf ( a ) !== Object . getPrototypeOf ( b ) )
223
- return false ;
224
- const aIsArgs = isArguments ( a ) ;
225
- const bIsArgs = isArguments ( b ) ;
226
- if ( ( aIsArgs && ! bIsArgs ) || ( ! aIsArgs && bIsArgs ) )
227
- return false ;
228
- const ka = Object . keys ( a ) ;
229
- const kb = Object . keys ( b ) ;
286
+
287
+ const aKeys = Object . keys ( a ) ;
288
+ const bKeys = Object . keys ( b ) ;
230
289
var key , i ;
231
290
232
- // The pair must have the same number of owned properties (keys
233
- // incorporates hasOwnProperty).
234
- if ( ka . length !== kb . length )
291
+ // The pair must have the same number of owned properties
292
+ // (keys incorporates hasOwnProperty).
293
+ if ( aKeys . length !== bKeys . length )
235
294
return false ;
236
295
237
296
// The pair must have the same set of keys (although not
238
297
// necessarily in the same order).
239
- ka . sort ( ) ;
240
- kb . sort ( ) ;
298
+ aKeys . sort ( ) ;
299
+ bKeys . sort ( ) ;
241
300
// Cheap key test:
242
- for ( i = ka . length - 1 ; i >= 0 ; i -- ) {
243
- if ( ka [ i ] !== kb [ i ] )
301
+ for ( i = aKeys . length - 1 ; i >= 0 ; i -- ) {
302
+ if ( aKeys [ i ] !== bKeys [ i ] )
244
303
return false ;
245
304
}
305
+
246
306
// The pair must have equivalent values for every corresponding key.
247
307
// Possibly expensive deep test:
248
- for ( i = ka . length - 1 ; i >= 0 ; i -- ) {
249
- key = ka [ i ] ;
308
+ for ( i = aKeys . length - 1 ; i >= 0 ; i -- ) {
309
+ key = aKeys [ i ] ;
250
310
if ( ! _deepEqual ( a [ key ] , b [ key ] , strict , actualVisitedObjects ) )
251
311
return false ;
252
312
}
@@ -269,7 +329,6 @@ function notDeepStrictEqual(actual, expected, message) {
269
329
}
270
330
}
271
331
272
-
273
332
// The strict equality assertion tests strict equality, as determined by ===.
274
333
// assert.strictEqual(actual, expected, message_opt);
275
334
@@ -295,7 +354,7 @@ function expectedException(actual, expected) {
295
354
return false ;
296
355
}
297
356
298
- if ( Object . prototype . toString . call ( expected ) === '[object RegExp]' ) {
357
+ if ( objectToString ( expected ) === '[object RegExp]' ) {
299
358
return expected . test ( actual ) ;
300
359
}
301
360
0 commit comments