JSON-stringify.js revision cfb0617749a64f2e177386b030d46007b8c4b179
1
2function createTests() {
3    var simpleArray = ['a', 'b', 'c'];
4    var simpleObject = {a:"1", b:"2", c:"3"};
5    var complexArray = ['a', 'b', 'c',,,simpleObject, simpleArray, [simpleObject,simpleArray]];
6    var complexObject = {a:"1", b:"2", c:"3", d:undefined, e:null, "":12, get f(){ return simpleArray; }, array: complexArray};
7    var simpleArrayWithProto = ['d', 'e', 'f'];
8    simpleArrayWithProto.__proto__ = simpleObject;
9    var simpleObjectWithProto = {d:"4", e:"5", f:"6", __proto__:simpleObject};
10    var complexArrayWithProto = ['d', 'e', 'f',,,simpleObjectWithProto, simpleArrayWithProto, [simpleObjectWithProto,simpleArrayWithProto]];
11    complexArrayWithProto.__proto__ = simpleObjectWithProto;
12    var complexObjectWithProto = {d:"4", e:"5", f:"6", g:undefined, h:null, "":12, get i(){ return simpleArrayWithProto; }, array2: complexArrayWithProto, __proto__:complexObject};
13    var objectWithSideEffectGetter = {get b() {this.foo=1;}};
14    var objectWithSideEffectGetterAndProto = {__proto__:{foo:"bar"}, get b() {this.foo=1;}};
15    var arrayWithSideEffectGetter = [];
16    arrayWithSideEffectGetter.__defineGetter__("b", function(){this.foo=1;});
17    var arrayWithSideEffectGetterAndProto = [];
18    arrayWithSideEffectGetterAndProto.__defineGetter__("b", function(){this.foo=1;});
19    arrayWithSideEffectGetterAndProto.__proto__ = {foo:"bar"};
20    var result = [];
21    result.push(function(jsonObject){
22        return jsonObject.stringify(1);
23    });
24    result.push(function(jsonObject){
25        return jsonObject.stringify(1.5);
26    });
27    result.push(function(jsonObject){
28        return jsonObject.stringify(-1);
29    });
30    result.push(function(jsonObject){
31        return jsonObject.stringify(-1.5);
32    });
33    result.push(function(jsonObject){
34        return jsonObject.stringify(null);
35    });
36    result.push(function(jsonObject){
37        return jsonObject.stringify("string");
38    });
39    result.push(function(jsonObject){
40        return jsonObject.stringify(new Number(0));
41    });
42    result.push(function(jsonObject){
43        return jsonObject.stringify(new Number(1));
44    });
45    result.push(function(jsonObject){
46        return jsonObject.stringify(new Number(1.5));
47    });
48    result.push(function(jsonObject){
49        return jsonObject.stringify(new Number(-1));
50    });
51    result.push(function(jsonObject){
52        return jsonObject.stringify(new Number(-1.5));
53    });
54    result.push(function(jsonObject){
55        return jsonObject.stringify(new String("a string object"));
56    });
57    result.push(function(jsonObject){
58        return jsonObject.stringify(new Boolean(true));
59    });
60    result.push(function(jsonObject){
61        var value = new Number(1);
62        value.valueOf = function() { return 2; }
63        return jsonObject.stringify(value);
64    });
65    result[result.length - 1].expected = '2';
66    result.push(function(jsonObject){
67        var value = new Boolean(true);
68        value.valueOf = function() { return 2; }
69        return jsonObject.stringify(value);
70    });
71    result[result.length - 1].expected = '2';
72    result.push(function(jsonObject){
73        var value = new String("fail");
74        value.toString = function() { return "converted string"; }
75        return jsonObject.stringify(value);
76    });
77    result[result.length - 1].expected = '"converted string"';
78    result.push(function(jsonObject){
79        return jsonObject.stringify(true);
80    });
81    result.push(function(jsonObject){
82        return jsonObject.stringify(false);
83    });
84    result.push(function(jsonObject){
85        return jsonObject.stringify(new Date(0));
86    });
87    result.push(function(jsonObject){
88        return jsonObject.stringify({toJSON: Date.prototype.toJSON});
89    });
90    result[result.length - 1].throws = true;
91    result.push(function(jsonObject){
92        return jsonObject.stringify({toJSON: Date.prototype.toJSON, toISOString: function(){ return "custom toISOString"; }});
93    });
94    result.push(function(jsonObject){
95        return jsonObject.stringify({toJSON: Date.prototype.toJSON, toISOString: function(){ return {}; }});
96    });
97    result[result.length - 1].throws = true;
98    result.push(function(jsonObject){
99        return jsonObject.stringify({toJSON: Date.prototype.toJSON, toISOString: function(){ throw "An exception"; }});
100    });
101    result[result.length - 1].throws = true;
102    result.push(function(jsonObject){
103        var d = new Date(0);
104        d.toISOString = null;
105        return jsonObject.stringify(d);
106    });
107    result[result.length - 1].throws = true;
108    result.push(function(jsonObject){
109        var d = new Date(0);
110        d.toJSON = undefined;
111        return jsonObject.stringify(d);
112    });
113    result.push(function(jsonObject){
114        return jsonObject.stringify({get Foo() { return "bar"; }});
115    });
116    result.push(function(jsonObject){
117        return jsonObject.stringify({get Foo() { this.foo="wibble"; return "bar"; }});
118    });
119    result.push(function(jsonObject){
120        var count = 0;
121        jsonObject.stringify({get Foo() { count++; return "bar"; }});
122        return count;
123    });
124    result.push(function(jsonObject){
125        var count = 0;
126        return jsonObject.stringify({get Foo() { count++; delete this.bar; return "bar"; }, bar: "wibble"});
127    });
128    result.push(function(jsonObject){
129        var count = 0;
130        return jsonObject.stringify({a:"1", b:"2", c:"3", 5:4, 4:5, 2:6, 1:7});
131    });
132    result.push(function(jsonObject){
133        var allString = true;
134        jsonObject.stringify({a:"1", b:"2", c:"3", 5:4, 4:5, 2:6, 1:7}, function(k,v){allString = allString && (typeof k == "string"); return v});
135        return allString;
136    });
137    result.push(function(jsonObject){
138        var allString = true;
139        jsonObject.stringify([1,2,3,4,5], function(k,v){allString = allString && (typeof k == "string"); return v});
140        return allString;
141    });
142    result.push(function(jsonObject){
143        var allString = true;
144        var array = [];
145        return jsonObject.stringify({a:"1", b:"2", c:"3", 5:4, 4:5, 2:6, 1:7}, array);
146    });
147    result.push(function(jsonObject){
148        var allString = true;
149        var array = ["a"];
150        return jsonObject.stringify({get a(){return 1;array[1]="b";array[2]="c"}, b:"2", c:"3"}, array);
151    });
152    result.push(function(jsonObject){
153        var allString = true;
154        var array = [{toString:function(){array[0]='a'; array[1]='c'; array[2]='b'; return 'a'}}];
155        return jsonObject.stringify(simpleObject, array);
156    });
157    result.push(function(jsonObject){
158        var allString = true;
159        var array = [{toString:function(){array[0]='a'; array[1]='c'; array[2]='b'; return 'a'}}];
160        return jsonObject.stringify(simpleObjectWithProto, array);
161    });
162    result.push(function(jsonObject){
163        var allString = true;
164        var array = [1, new Number(2), NaN, Infinity, -Infinity, new String("str")];
165        return jsonObject.stringify({"1":"1","2":"2","NaN":"NaN","Infinity":"Infinity","-Infinity":"-Infinity","str":"str"}, array);
166    });
167    result[result.length - 1].expected = '{"1":"1","2":"2","NaN":"NaN","Infinity":"Infinity","-Infinity":"-Infinity","str":"str"}';
168    result.push(function(jsonObject){
169        var allString = true;
170        var array = ["1","2","3"];
171        return jsonObject.stringify({1:'a', 2:'b', 3:'c'}, array);
172    });
173    result.push(function(jsonObject){
174        var allString = true;
175        var array = ["1","2","3"];
176        return jsonObject.stringify(simpleArray, array);
177    });
178    result.push(function(jsonObject){
179        return jsonObject.stringify(simpleArray, null, "  ");
180    });
181    result.push(function(jsonObject){
182        return jsonObject.stringify(simpleArray, null, 4);
183    });
184    result.push(function(jsonObject){
185        return jsonObject.stringify(simpleArray, null, "ab");
186    });
187    result.push(function(jsonObject){
188        return jsonObject.stringify(simpleArray, null, 4);
189    });
190    result.push(function(jsonObject){
191        return jsonObject.stringify(simpleObject, null, "  ");
192    });
193    result.push(function(jsonObject){
194        return jsonObject.stringify(simpleObject, null, 4);
195    });
196    result.push(function(jsonObject){
197        return jsonObject.stringify(simpleObject, null, "ab");
198    });
199    result.push(function(jsonObject){
200        return jsonObject.stringify(simpleObject, null, 4);
201    });
202    result.push(function(jsonObject){
203        return jsonObject.stringify(simpleObject, null, 10);
204    });
205    result.push(function(jsonObject){
206        return jsonObject.stringify(simpleObject, null, 11);
207    });
208    result[result.length - 1].expected = JSON.stringify(simpleObject, null, 10);
209    result.push(function(jsonObject){
210        return jsonObject.stringify(simpleObject, null, "          ");
211    });
212    result[result.length - 1].expected = JSON.stringify(simpleObject, null, 10);
213    result.push(function(jsonObject){
214        return jsonObject.stringify(simpleObject, null, "           ");
215    });
216    result[result.length - 1].expected = JSON.stringify(simpleObject, null, 10);
217    result.push(function(jsonObject){
218        return jsonObject.stringify(complexArray, null, "  ");
219    });
220    result.push(function(jsonObject){
221        return jsonObject.stringify(complexArray, null, 4);
222    });
223    result.push(function(jsonObject){
224        return jsonObject.stringify(complexArray, null, "ab");
225    });
226    result.push(function(jsonObject){
227        return jsonObject.stringify(complexArray, null, 4);
228    });
229    result.push(function(jsonObject){
230        return jsonObject.stringify(complexObject, null, "  ");
231    });
232    result.push(function(jsonObject){
233        return jsonObject.stringify(complexObject, null, 4);
234    });
235    result.push(function(jsonObject){
236        return jsonObject.stringify(complexObject, null, "ab");
237    });
238    result.push(function(jsonObject){
239        return jsonObject.stringify(complexObject, null, 4);
240    });
241    result.push(function(jsonObject){
242        var allString = true;
243        var array = ["1","2","3"];
244        return jsonObject.stringify(simpleArrayWithProto, array);
245    });
246    result.push(function(jsonObject){
247        return jsonObject.stringify(simpleArrayWithProto, null, "  ");
248    });
249    result.push(function(jsonObject){
250        return jsonObject.stringify(simpleArrayWithProto, null, 4);
251    });
252    result.push(function(jsonObject){
253        return jsonObject.stringify(simpleArrayWithProto, null, "ab");
254    });
255    result.push(function(jsonObject){
256        return jsonObject.stringify(simpleArrayWithProto, null, 4);
257    });
258    result.push(function(jsonObject){
259        return jsonObject.stringify(simpleObjectWithProto, null, "  ");
260    });
261    result.push(function(jsonObject){
262        return jsonObject.stringify(simpleObjectWithProto, null, 4);
263    });
264    result.push(function(jsonObject){
265        return jsonObject.stringify(simpleObjectWithProto, null, "ab");
266    });
267    result.push(function(jsonObject){
268        return jsonObject.stringify(simpleObjectWithProto, null, 4);
269    });
270    result.push(function(jsonObject){
271        return jsonObject.stringify(simpleObjectWithProto, null, 10);
272    });
273    result.push(function(jsonObject){
274        return jsonObject.stringify(simpleObjectWithProto, null, 11);
275    });
276    result[result.length - 1].expected = JSON.stringify(simpleObjectWithProto, null, 10);
277    result.push(function(jsonObject){
278        return jsonObject.stringify(simpleObjectWithProto, null, "          ");
279    });
280    result[result.length - 1].expected = JSON.stringify(simpleObjectWithProto, null, 10);
281    result.push(function(jsonObject){
282        return jsonObject.stringify(simpleObjectWithProto, null, "           ");
283    });
284    result[result.length - 1].expected = JSON.stringify(simpleObjectWithProto, null, 10);
285    result.push(function(jsonObject){
286        return jsonObject.stringify(complexArrayWithProto, null, "  ");
287    });
288    result.push(function(jsonObject){
289        return jsonObject.stringify(complexArrayWithProto, null, 4);
290    });
291    result.push(function(jsonObject){
292        return jsonObject.stringify(complexArrayWithProto, null, "ab");
293    });
294    result.push(function(jsonObject){
295        return jsonObject.stringify(complexArrayWithProto, null, 4);
296    });
297    result.push(function(jsonObject){
298        return jsonObject.stringify(complexObjectWithProto, null, "  ");
299    });
300    result.push(function(jsonObject){
301        return jsonObject.stringify(complexObjectWithProto, null, 4);
302    });
303    result.push(function(jsonObject){
304        return jsonObject.stringify(complexObjectWithProto, null, "ab");
305    });
306    result.push(function(jsonObject){
307        return jsonObject.stringify(complexObjectWithProto, null, 4);
308    });
309    result.push(function(jsonObject){
310        return jsonObject.stringify(objectWithSideEffectGetter);
311    });
312    result.push(function(jsonObject){
313        return jsonObject.stringify(objectWithSideEffectGetterAndProto);
314    });
315    result.push(function(jsonObject){
316        return jsonObject.stringify(arrayWithSideEffectGetter);
317    });
318    result.push(function(jsonObject){
319        return jsonObject.stringify(arrayWithSideEffectGetterAndProto);
320    });
321    var replaceTracker;
322    function replaceFunc(key, value) {
323        replaceTracker += key + "("+(typeof key)+")" + JSON.stringify(value) + ";";
324        return value;
325    }
326    result.push(function(jsonObject){
327        replaceTracker = "";
328        jsonObject.stringify([1,2,3,,,,4,5,6], replaceFunc);
329        return replaceTracker;
330    });
331    result[result.length - 1].expected = '(string)[1,2,3,null,null,null,4,5,6];0(number)1;1(number)2;2(number)3;3(number)undefined;4(number)undefined;5(number)undefined;6(number)4;7(number)5;8(number)6;'
332    result.push(function(jsonObject){
333        replaceTracker = "";
334        jsonObject.stringify({a:"a", b:"b", c:"c", 3: "d", 2: "e", 1: "f"}, replaceFunc);
335        return replaceTracker;
336    });
337    result[result.length - 1].expected = '(string){"a":"a","b":"b","c":"c","3":"d","2":"e","1":"f"};a(string)"a";b(string)"b";c(string)"c";3(string)"d";2(string)"e";1(string)"f";';
338    result.push(function(jsonObject){
339        var count = 0;
340        var array = [{toString:function(){count++; array[0]='a'; array[1]='c'; array[2]='b'; return 'a'}}];
341        jsonObject.stringify(simpleObject, array);
342        return count;
343    });
344    result.push(function(jsonObject){
345        var allString = true;
346        var array = [{toString:function(){array[0]='a'; array[1]='c'; array[2]='b'; return 'a'}}, 'b', 'c'];
347        return jsonObject.stringify(simpleObject, array);
348    });
349    result.push(function(jsonObject){
350        var count = 0;
351        var array = [{toString:function(){count++; array[0]='a'; array[1]='c'; array[2]='b'; return 'a'}}, 'b', 'c'];
352        jsonObject.stringify(simpleObject, array);
353        return count;
354    });
355    result.push(function(jsonObject){
356        return jsonObject.stringify({a:"1", get b() { this.a="foo"; return "getter"; }, c:"3"});
357    });
358    result.push(function(jsonObject){
359        return jsonObject.stringify({a:"1", get b() { this.c="foo"; return "getter"; }, c:"3"});
360    });
361    result.push(function(jsonObject){
362        var setterCalled = false;
363        jsonObject.stringify({a:"1", set b(s) { setterCalled = true; return "setter"; }, c:"3"});
364        return setterCalled;
365    });
366    result.push(function(jsonObject){
367        return jsonObject.stringify({a:"1", get b(){ return "getter"; }, set b(s) { return "setter"; }, c:"3"});
368    });
369    result.push(function(jsonObject){
370        return jsonObject.stringify(new Array(10));
371    });
372    result.push(function(jsonObject){
373        return jsonObject.stringify([undefined,,null,0,false]);
374    });
375    result.push(function(jsonObject){
376        return jsonObject.stringify({p1:undefined,p2:null,p3:0,p4:false});
377    });
378    var cycleTracker = "";
379    var cyclicObject = { get preSelf1() { cycleTracker+="preSelf1,"; return "preSelf1"; },
380                             preSelf2: {toJSON:function(){cycleTracker+="preSelf2,"; return "preSelf2"}},
381                             self: [],
382                         get postSelf1() { cycleTracker+="postSelf1,"; return "postSelf1" },
383                             postSelf2: {toJSON:function(){cycleTracker+="postSelf2,"; return "postSelf2"}},
384                             toJSON : function(key) { cycleTracker += key + "("+(typeof key)+"):" + this; return this; }
385                       };
386    cyclicObject.self = cyclicObject;
387    result.push(function(jsonObject){
388        cycleTracker = "";
389        return jsonObject.stringify(cyclicObject);
390    });
391    result[result.length - 1].throws = true;
392    result.push(function(jsonObject){
393        cycleTracker = "";
394        try { jsonObject.stringify(cyclicObject); } catch(e) { cycleTracker += " -> exception" }
395        return cycleTracker;
396    });
397    result[result.length - 1].expected = "(string):[object Object]preSelf1,preSelf2,self(string):[object Object] -> exception"
398    var cyclicArray = [{toJSON : function(key,value) { cycleTracker += key + "("+(typeof key)+"):" + this; cycleTracker += "first,"; return this; }},
399                       cyclicArray,
400                       {toJSON : function(key,value) { cycleTracker += key + "("+(typeof key)+"):" + this; cycleTracker += "second,"; return this; }}];
401    cyclicArray[1] = cyclicArray;
402    result.push(function(jsonObject){
403        cycleTracker = "";
404        return jsonObject.stringify(cyclicArray);
405    });
406    result[result.length - 1].throws = true;
407    result.push(function(jsonObject){
408        cycleTracker = "";
409        try { jsonObject.stringify(cyclicArray); } catch(e) { cycleTracker += " -> exception" }
410        return cycleTracker;
411    });
412    result[result.length - 1].expected = "0(number):[object Object]first, -> exception";
413    function createArray(len, o) { var r = []; for (var i = 0; i < len; i++) r[i] = o; return r; }
414    var getterCalls;
415    var magicObject = createArray(10, {abcdefg: [1,2,5,"ab", null, undefined, true, false,,],
416                                       get calls() {return ++getterCalls; },
417                                       "123":createArray(15, "foo"),
418                                       "":{a:"b"}});
419    result.push(function(jsonObject){
420        getterCalls = 0;
421        return jsonObject.stringify(magicObject) + " :: getter calls = " + getterCalls;
422    });
423    result.push(function(jsonObject){
424        return jsonObject.stringify(undefined);
425    });
426    result.push(function(jsonObject){
427        return jsonObject.stringify(null);
428    });
429    result.push(function(jsonObject){
430        return jsonObject.stringify({toJSON:function(){ return undefined; }});
431    });
432    result.push(function(jsonObject){
433        return jsonObject.stringify({toJSON:function(){ return null; }});
434    });
435    result.push(function(jsonObject){
436        return jsonObject.stringify([{toJSON:function(){ return undefined; }}]);
437    });
438    result.push(function(jsonObject){
439        return jsonObject.stringify([{toJSON:function(){ return null; }}]);
440    });
441    result.push(function(jsonObject){
442        return jsonObject.stringify({a:{toJSON:function(){ return undefined; }}});
443    });
444    result.push(function(jsonObject){
445        return jsonObject.stringify({a:{toJSON:function(){ return null; }}});
446    });
447    result.push(function(jsonObject){
448        return jsonObject.stringify({a:{toJSON:function(){ return function(){}; }}});
449    });
450    result.push(function(jsonObject){
451        return jsonObject.stringify({a:function(){}});
452    });
453    result.push(function(jsonObject){
454        var deepObject = {};
455        for (var i = 0; i < 2048; i++)
456            deepObject = {next:deepObject};
457        return jsonObject.stringify(deepObject);
458    });
459    result.push(function(jsonObject){
460        var deepArray = [];
461        for (var i = 0; i < 2048; i++)
462            deepArray = [deepArray];
463        return jsonObject.stringify(deepArray);
464    });
465    result.push(function(jsonObject){
466        var depth = 0;
467        function toDeepVirtualJSONObject() {
468            if (++depth >= 2048)
469                return {};
470            var r = {};
471            r.toJSON = toDeepVirtualJSONObject;
472            return {recurse: r};
473        }
474        return jsonObject.stringify(toDeepVirtualJSONObject());
475    });
476    result.push(function(jsonObject){
477        var depth = 0;
478        function toDeepVirtualJSONArray() {
479            if (++depth >= 2048)
480                return [];
481            var r = [];
482            r.toJSON = toDeepJSONArray;
483            return [r];
484        }
485        return jsonObject.stringify(toDeepVirtualJSONArray());
486    });
487    var fullCharsetString = "";
488    for (var i = 0; i < 65536; i++)
489        fullCharsetString += String.fromCharCode(i);
490    result.push(function(jsonObject){
491        return jsonObject.stringify(fullCharsetString);
492    });
493    return result;
494}
495var tests = createTests();
496for (var i = 0; i < tests.length; i++) {
497    try {
498        debug(tests[i]);
499        if (tests[i].throws)
500            shouldThrow('tests[i](nativeJSON)');
501        else if (tests[i].expected)
502            shouldBe('tests[i](nativeJSON)',  "tests[i].expected");
503        else
504            shouldBe('tests[i](nativeJSON)',  "tests[i](JSON)");
505    }catch(e){}
506}
507successfullyParsed = true;
508
509