Skip to content

Commit 76c9ab5

Browse files
TrottFishrock123
authored andcommitted
assert: allow circular references
assert.deepEqual() and assert.deepStrictEqual() will no longer throw a RangeError if passed objects with circular references. PR-URL: #6432 Fixes: #6416 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent b3f75ec commit 76c9ab5

File tree

2 files changed

+37
-16
lines changed

2 files changed

+37
-16
lines changed

lib/assert.js

+17-4
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
143143
}
144144
};
145145

146-
function _deepEqual(actual, expected, strict) {
146+
function _deepEqual(actual, expected, strict, memos) {
147147
// 7.1. All identical values are equivalent, as determined by ===.
148148
if (actual === expected) {
149149
return true;
@@ -191,15 +191,27 @@ function _deepEqual(actual, expected, strict) {
191191
// corresponding key, and an identical 'prototype' property. Note: this
192192
// accounts for both named and indexed properties on Arrays.
193193
} else {
194-
return objEquiv(actual, expected, strict);
194+
memos = memos || {actual: [], expected: []};
195+
196+
const actualIndex = memos.actual.indexOf(actual);
197+
if (actualIndex !== -1) {
198+
if (actualIndex === memos.expected.indexOf(expected)) {
199+
return true;
200+
}
201+
}
202+
203+
memos.actual.push(actual);
204+
memos.expected.push(expected);
205+
206+
return objEquiv(actual, expected, strict, memos);
195207
}
196208
}
197209

198210
function isArguments(object) {
199211
return Object.prototype.toString.call(object) == '[object Arguments]';
200212
}
201213

202-
function objEquiv(a, b, strict) {
214+
function objEquiv(a, b, strict, actualVisitedObjects) {
203215
if (a === null || a === undefined || b === null || b === undefined)
204216
return false;
205217
// if one is a primitive, the other must be same
@@ -235,7 +247,8 @@ function objEquiv(a, b, strict) {
235247
//~~~possibly expensive deep test
236248
for (i = ka.length - 1; i >= 0; i--) {
237249
key = ka[i];
238-
if (!_deepEqual(a[key], b[key], strict)) return false;
250+
if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects))
251+
return false;
239252
}
240253
return true;
241254
}

test/parallel/test-assert.js

+20-12
Original file line numberDiff line numberDiff line change
@@ -381,25 +381,33 @@ try {
381381

382382
assert.ok(threw);
383383

384-
// GH-207. Make sure deepEqual doesn't loop forever on circular refs
385-
var b = {};
386-
b.b = b;
384+
// https://github.com/nodejs/node/issues/6416
385+
// Make sure circular refs don't throw.
386+
{
387+
const b = {};
388+
b.b = b;
387389

388-
var c = {};
389-
c.b = c;
390+
const c = {};
391+
c.b = c;
390392

391-
var gotError = false;
392-
try {
393-
assert.deepEqual(b, c);
394-
} catch (e) {
395-
gotError = true;
396-
}
393+
a.doesNotThrow(makeBlock(a.deepEqual, b, c));
394+
a.doesNotThrow(makeBlock(a.deepStrictEqual, b, c));
397395

396+
const d = {};
397+
d.a = 1;
398+
d.b = d;
399+
400+
const e = {};
401+
e.a = 1;
402+
e.b = e.a;
403+
404+
a.throws(makeBlock(a.deepEqual, d, e), /AssertionError/);
405+
a.throws(makeBlock(a.deepStrictEqual, d, e), /AssertionError/);
406+
}
398407
// GH-7178. Ensure reflexivity of deepEqual with `arguments` objects.
399408
var args = (function() { return arguments; })();
400409
a.throws(makeBlock(a.deepEqual, [], args));
401410
a.throws(makeBlock(a.deepEqual, args, []));
402-
assert.ok(gotError);
403411

404412

405413
var circular = {y: 1};

0 commit comments

Comments
 (0)