Skip to content

Commit 9aee773

Browse files
liaokunhualjharb
andcommitted
[Fix] stringify: actually fix cyclic references
Fixes #403. Co-authored-by: liaokunhua <[email protected]> Co-authored-by: Jordan Harband <[email protected]>
1 parent 24c19cc commit 9aee773

File tree

2 files changed

+29
-5
lines changed

2 files changed

+29
-5
lines changed

lib/stringify.js

+21-3
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ var isNonNullishPrimitive = function isNonNullishPrimitive(v) {
5656
|| typeof v === 'bigint';
5757
};
5858

59+
var sentinel = {};
60+
5961
var stringify = function stringify(
6062
object,
6163
prefix,
@@ -75,8 +77,23 @@ var stringify = function stringify(
7577
) {
7678
var obj = object;
7779

78-
if (sideChannel.has(object)) {
79-
throw new RangeError('Cyclic object value');
80+
var tmpSc = sideChannel;
81+
var step = 0;
82+
var findFlag = false;
83+
while ((tmpSc = tmpSc.get(sentinel)) !== undefined && !findFlag) {
84+
// Where object last appeared in the ref tree
85+
var pos = tmpSc.get(object);
86+
step += 1;
87+
if (typeof pos !== 'undefined') {
88+
if (pos === step) {
89+
throw new RangeError('Cyclic object value');
90+
} else {
91+
findFlag = true; // Break while
92+
}
93+
}
94+
if (typeof tmpSc.get(sentinel) === 'undefined') {
95+
step = 0;
96+
}
8097
}
8198

8299
if (typeof filter === 'function') {
@@ -145,8 +162,9 @@ var stringify = function stringify(
145162
? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix
146163
: prefix + (allowDots ? '.' + key : '[' + key + ']');
147164

148-
sideChannel.set(object, true);
165+
sideChannel.set(object, step);
149166
var valueSideChannel = getSideChannel();
167+
valueSideChannel.set(sentinel, sideChannel);
150168
pushToArray(values, stringify(
151169
value,
152170
keyPrefix,

test/stringify.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ test('stringify()', function (t) {
454454

455455
st['throws'](
456456
function () { qs.stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); },
457-
RangeError,
457+
/RangeError: Cyclic object value/,
458458
'cyclic values throw'
459459
);
460460

@@ -464,10 +464,16 @@ test('stringify()', function (t) {
464464
circular.a = circular;
465465
st['throws'](
466466
function () { qs.stringify(circular); },
467-
RangeError,
467+
/RangeError: Cyclic object value/,
468468
'cyclic values throw'
469469
);
470470

471+
var arr = ['a'];
472+
st.doesNotThrow(
473+
function () { qs.stringify({ x: arr, y: arr }); },
474+
'non-cyclic values do not throw'
475+
);
476+
471477
st.end();
472478
});
473479

0 commit comments

Comments
 (0)