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