diff --git a/lib/utils.js b/lib/utils.js index 8274601c..31760fb6 100755 --- a/lib/utils.js +++ b/lib/utils.js @@ -20,7 +20,7 @@ exports.arrayToObject = function (source) { }; -exports.clone = function (source) { +exports.clone = function (source, refs) { if (typeof source !== 'object' || source === null) { @@ -32,14 +32,24 @@ exports.clone = function (source) { return source.toString(); } - var obj = Array.isArray(source) ? [] : {}; + refs = refs || []; + + var lookup = refs.indexOf(source); + if (lookup !== -1) { + return refs[lookup]; + } + + var copy = Array.isArray(source) ? [] : source; + + refs.push(source); + for (var i in source) { if (source.hasOwnProperty(i)) { - obj[i] = exports.clone(source[i]); + copy[i] = exports.clone(source[i], refs); } } - return obj; + return copy; }; @@ -108,32 +118,41 @@ exports.decode = function (str) { }; -exports.compact = function (obj) { +exports.compact = function (obj, refs) { + + if (typeof obj !== 'object' || + obj === null) { - if (typeof obj !== 'object' || obj === null) { return obj; } - var compacted = {}; + refs = refs || []; + var lookup = refs.indexOf(obj); + if (lookup !== -1) { + return refs[lookup]; + } - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - if (Array.isArray(obj[key])) { - compacted[key] = []; + refs.push(obj); - for (var i = 0, l = obj[key].length; i < l; i++) { - if (typeof obj[key][i] !== 'undefined') { - compacted[key].push(obj[key][i]); - } - } - } - else { - compacted[key] = exports.compact(obj[key]); + if (Array.isArray(obj)) { + var compacted = []; + + for (var i = 0, l = obj.length; i < l; ++i) { + if (typeof obj[i] !== 'undefined') { + compacted.push(obj[i]); } } + + return compacted; + } + + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + obj[key] = exports.compact(obj[key], refs); + } } - return compacted; + return obj; }; diff --git a/test/parse.js b/test/parse.js index 04f11cdd..e8e1c184 100755 --- a/test/parse.js +++ b/test/parse.js @@ -337,4 +337,15 @@ describe('#parse', function () { expect(Qs.parse('roomInfoList[0].childrenAges[0]=15&roomInfoList[0].numberOfAdults=2')).to.deep.equal({ roomInfoList: [['15', '2']] }); done(); }); + + it('does not crash when parsing circular references', function (done) { + + var a = {}; + a.b = a; + expect(function () { + + Qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a }); + }).to.not.throw(Error); + done(); + }); });