Skip to content

Commit 39e2474

Browse files
TrottMyles Borins
authored and
Myles Borins
committed
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 5380743 commit 39e2474

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
@@ -380,25 +380,33 @@ try {
380380

381381
assert.ok(threw);
382382

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

387-
var c = {};
388-
c.b = c;
389+
const c = {};
390+
c.b = c;
389391

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

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

403411

404412
var circular = {y: 1};

0 commit comments

Comments
 (0)