Skip to content

Commit

Permalink
assert: allow circular references
Browse files Browse the repository at this point in the history
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 <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
  • Loading branch information
Trott authored and Myles Borins committed Jul 11, 2016
1 parent 0eef098 commit 431b615
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 16 deletions.
21 changes: 17 additions & 4 deletions lib/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
}
};

function _deepEqual(actual, expected, strict) {
function _deepEqual(actual, expected, strict, memos) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
Expand Down Expand Up @@ -191,15 +191,27 @@ function _deepEqual(actual, expected, strict) {
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return objEquiv(actual, expected, strict);
memos = memos || {actual: [], expected: []};

const actualIndex = memos.actual.indexOf(actual);
if (actualIndex !== -1) {
if (actualIndex === memos.expected.indexOf(expected)) {
return true;
}
}

memos.actual.push(actual);
memos.expected.push(expected);

return objEquiv(actual, expected, strict, memos);
}
}

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

function objEquiv(a, b, strict) {
function objEquiv(a, b, strict, actualVisitedObjects) {
if (a === null || a === undefined || b === null || b === undefined)
return false;
// if one is a primitive, the other must be same
Expand Down Expand Up @@ -235,7 +247,8 @@ function objEquiv(a, b, strict) {
//~~~possibly expensive deep test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!_deepEqual(a[key], b[key], strict)) return false;
if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects))
return false;
}
return true;
}
Expand Down
32 changes: 20 additions & 12 deletions test/parallel/test-assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,25 +380,33 @@ try {

assert.ok(threw);

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

var c = {};
c.b = c;
const c = {};
c.b = c;

var gotError = false;
try {
assert.deepEqual(b, c);
} catch (e) {
gotError = true;
}
a.doesNotThrow(makeBlock(a.deepEqual, b, c));
a.doesNotThrow(makeBlock(a.deepStrictEqual, b, c));

const d = {};
d.a = 1;
d.b = d;

const e = {};
e.a = 1;
e.b = e.a;

a.throws(makeBlock(a.deepEqual, d, e), /AssertionError/);
a.throws(makeBlock(a.deepStrictEqual, d, e), /AssertionError/);
}
// GH-7178. Ensure reflexivity of deepEqual with `arguments` objects.
var args = (function() { return arguments; })();
a.throws(makeBlock(a.deepEqual, [], args));
a.throws(makeBlock(a.deepEqual, args, []));
assert.ok(gotError);


var circular = {y: 1};
Expand Down

0 comments on commit 431b615

Please sign in to comment.