Skip to content

Commit 130bdc2

Browse files
committed
account for circular references properly, closes #31
1 parent 9bc7567 commit 130bdc2

File tree

2 files changed

+50
-20
lines changed

2 files changed

+50
-20
lines changed

lib/utils.js

+39-20
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ exports.arrayToObject = function (source) {
2020
};
2121

2222

23-
exports.clone = function (source) {
23+
exports.clone = function (source, refs) {
2424

2525
if (typeof source !== 'object' ||
2626
source === null) {
@@ -32,14 +32,24 @@ exports.clone = function (source) {
3232
return source.toString();
3333
}
3434

35-
var obj = Array.isArray(source) ? [] : {};
35+
refs = refs || [];
36+
37+
var lookup = refs.indexOf(source);
38+
if (lookup !== -1) {
39+
return refs[lookup];
40+
}
41+
42+
var copy = Array.isArray(source) ? [] : source;
43+
44+
refs.push(source);
45+
3646
for (var i in source) {
3747
if (source.hasOwnProperty(i)) {
38-
obj[i] = exports.clone(source[i]);
48+
copy[i] = exports.clone(source[i], refs);
3949
}
4050
}
4151

42-
return obj;
52+
return copy;
4353
};
4454

4555

@@ -108,32 +118,41 @@ exports.decode = function (str) {
108118
};
109119

110120

111-
exports.compact = function (obj) {
121+
exports.compact = function (obj, refs) {
122+
123+
if (typeof obj !== 'object' ||
124+
obj === null) {
112125

113-
if (typeof obj !== 'object' || obj === null) {
114126
return obj;
115127
}
116128

117-
var compacted = {};
129+
refs = refs || [];
130+
var lookup = refs.indexOf(obj);
131+
if (lookup !== -1) {
132+
return refs[lookup];
133+
}
118134

119-
for (var key in obj) {
120-
if (obj.hasOwnProperty(key)) {
121-
if (Array.isArray(obj[key])) {
122-
compacted[key] = [];
135+
refs.push(obj);
123136

124-
for (var i = 0, l = obj[key].length; i < l; i++) {
125-
if (typeof obj[key][i] !== 'undefined') {
126-
compacted[key].push(obj[key][i]);
127-
}
128-
}
129-
}
130-
else {
131-
compacted[key] = exports.compact(obj[key]);
137+
if (Array.isArray(obj)) {
138+
var compacted = [];
139+
140+
for (var i = 0, l = obj.length; i < l; ++i) {
141+
if (typeof obj[i] !== 'undefined') {
142+
compacted.push(obj[i]);
132143
}
133144
}
145+
146+
return compacted;
147+
}
148+
149+
for (var key in obj) {
150+
if (obj.hasOwnProperty(key)) {
151+
obj[key] = exports.compact(obj[key], refs);
152+
}
134153
}
135154

136-
return compacted;
155+
return obj;
137156
};
138157

139158

test/parse.js

+11
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,15 @@ describe('#parse', function () {
337337
expect(Qs.parse('roomInfoList[0].childrenAges[0]=15&roomInfoList[0].numberOfAdults=2')).to.deep.equal({ roomInfoList: [['15', '2']] });
338338
done();
339339
});
340+
341+
it('does not crash when parsing circular references', function (done) {
342+
343+
var a = {};
344+
a.b = a;
345+
expect(function () {
346+
347+
Qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a });
348+
}).to.not.throw(Error);
349+
done();
350+
});
340351
});

0 commit comments

Comments
 (0)