From 037b143fcf4edc35dab568f99ea8fe706472dd98 Mon Sep 17 00:00:00 2001 From: liaokunhua Date: Sat, 4 Dec 2021 18:02:23 +0800 Subject: [PATCH] [Fix] `stringify`: actually fix cyclic references Fixes #403. Co-authored-by: liaokunhua Co-authored-by: Jordan Harband --- lib/stringify.js | 24 +++++++++++++++++++++--- test/stringify.js | 10 ++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/stringify.js b/lib/stringify.js index fa329304..c5990499 100644 --- a/lib/stringify.js +++ b/lib/stringify.js @@ -56,6 +56,8 @@ var isNonNullishPrimitive = function isNonNullishPrimitive(v) { || typeof v === 'bigint'; }; +var sentinel = {}; + var stringify = function stringify( object, prefix, @@ -75,8 +77,23 @@ var stringify = function stringify( ) { var obj = object; - if (sideChannel.has(object)) { - throw new RangeError('Cyclic object value'); + var tmpSc = sideChannel; + var step = 0; + var findFlag = false; + while ((tmpSc = tmpSc.get(sentinel)) !== undefined && !findFlag) { + // Where object last appeared in the ref tree + var pos = tmpSc.get(object); + step += 1; + if (typeof pos !== 'undefined') { + if (pos === step) { + throw new RangeError('Cyclic object value'); + } else { + findFlag = true; // Break while + } + } + if (typeof tmpSc.get(sentinel) === 'undefined') { + step = 0; + } } if (typeof filter === 'function') { @@ -145,8 +162,9 @@ var stringify = function stringify( ? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix : prefix + (allowDots ? '.' + key : '[' + key + ']'); - sideChannel.set(object, true); + sideChannel.set(object, step); var valueSideChannel = getSideChannel(); + valueSideChannel.set(sentinel, sideChannel); pushToArray(values, stringify( value, keyPrefix, diff --git a/test/stringify.js b/test/stringify.js index 9750594e..99d7c66b 100644 --- a/test/stringify.js +++ b/test/stringify.js @@ -454,7 +454,7 @@ test('stringify()', function (t) { st['throws']( function () { qs.stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); }, - RangeError, + /RangeError: Cyclic object value/, 'cyclic values throw' ); @@ -464,10 +464,16 @@ test('stringify()', function (t) { circular.a = circular; st['throws']( function () { qs.stringify(circular); }, - RangeError, + /RangeError: Cyclic object value/, 'cyclic values throw' ); + var a = ['a']; + st.doesNotThrow( + function () { qs.stringify({ x:a, y:a }); }, + 'non-cyclic values do not throw' + ); + st.end(); });