OLD | NEW |
---|---|
1 // Copyright 2009 the V8 project authors. All rights reserved. | 1 // Copyright 2009 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
11 // with the distribution. | 11 // with the distribution. |
12 // * Neither the name of Google Inc. nor the names of its | 12 // * Neither the name of Google Inc. nor the names of its |
13 // contributors may be used to endorse or promote products derived | 13 // contributors may be used to endorse or promote products derived |
14 // from this software without specific prior written permission. | 14 // from this software without specific prior written permission. |
15 // | 15 // |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 function GenericToJSONChecks(Constructor, value, alternative) { | |
29 var n1 = new Constructor(value); | |
30 n1.valueOf = function () { return alternative; }; | |
31 assertEquals(alternative, n1.toJSON()); | |
32 var n2 = new Constructor(value); | |
33 n2.valueOf = null; | |
34 assertThrows(function () { n2.toJSON(); }, TypeError); | |
35 var n3 = new Constructor(value); | |
36 n3.valueOf = function () { return {}; }; | |
37 assertThrows(function () { n3.toJSON(); }, TypeError, 'result_not_primitive'); | |
38 var n4 = new Constructor(value); | |
39 n4.valueOf = function () { | |
40 assertEquals(0, arguments.length); | |
41 assertEquals(this, n4); | |
42 return null; | |
43 }; | |
44 assertEquals(null, n4.toJSON()); | |
45 } | |
46 | |
47 // Number toJSON | |
48 assertEquals(3, (3).toJSON()); | |
49 assertEquals(3, (3).toJSON(true)); | |
50 assertEquals(4, (new Number(4)).toJSON()); | |
51 GenericToJSONChecks(Number, 5, 6); | |
52 | |
53 // Boolean toJSON | |
54 assertEquals(true, (true).toJSON()); | |
55 assertEquals(true, (true).toJSON(false)); | |
56 assertEquals(false, (false).toJSON()); | |
57 assertEquals(true, (new Boolean(true)).toJSON()); | |
58 GenericToJSONChecks(Boolean, true, false); | |
59 GenericToJSONChecks(Boolean, false, true); | |
60 | |
61 // String toJSON | |
62 assertEquals("flot", "flot".toJSON()); | |
63 assertEquals("flot", "flot".toJSON(3)); | |
64 assertEquals("tolf", (new String("tolf")).toJSON()); | |
65 GenericToJSONChecks(String, "x", "y"); | |
66 | |
67 // Date toJSON | 28 // Date toJSON |
68 assertEquals("1970-01-01T00:00:00.000Z", new Date(0).toJSON()); | 29 assertEquals("1970-01-01T00:00:00.000Z", new Date(0).toJSON()); |
69 assertEquals("1979-01-11T08:00:00.000Z", new Date("1979-01-11 08:00 GMT").toJSON ()); | 30 assertEquals("1979-01-11T08:00:00.000Z", new Date("1979-01-11 08:00 GMT").toJSON ()); |
70 assertEquals("2005-05-05T05:05:05.000Z", new Date("2005-05-05 05:05:05 GMT").toJ SON()); | 31 assertEquals("2005-05-05T05:05:05.000Z", new Date("2005-05-05 05:05:05 GMT").toJ SON()); |
71 var n1 = new Date(10000); | 32 var n1 = new Date(10000); |
72 n1.toISOString = function () { return "foo"; }; | 33 n1.toISOString = function () { return "foo"; }; |
73 assertEquals("foo", n1.toJSON()); | 34 assertEquals("foo", n1.toJSON()); |
74 var n2 = new Date(10001); | 35 var n2 = new Date(10001); |
75 n2.toISOString = null; | 36 n2.toISOString = null; |
76 assertThrows(function () { n2.toJSON(); }, TypeError); | 37 assertThrows(function () { n2.toJSON(); }, TypeError); |
77 var n3 = new Date(10002); | |
78 n3.toISOString = function () { return {}; }; | |
79 assertThrows(function () { n3.toJSON(); }, TypeError, "result_not_primitive"); | |
80 var n4 = new Date(10003); | 38 var n4 = new Date(10003); |
81 n4.toISOString = function () { | 39 n4.toISOString = function () { |
82 assertEquals(0, arguments.length); | 40 assertEquals(0, arguments.length); |
83 assertEquals(this, n4); | 41 assertEquals(this, n4); |
84 return null; | 42 return null; |
85 }; | 43 }; |
86 assertEquals(null, n4.toJSON()); | 44 assertEquals(null, n4.toJSON()); |
87 | 45 |
88 assertTrue(Object.prototype === JSON.__proto__); | 46 assertTrue(Object.prototype === JSON.__proto__); |
89 assertEquals("[object JSON]", Object.prototype.toString.call(JSON)); | 47 assertEquals("[object JSON]", Object.prototype.toString.call(JSON)); |
90 | 48 |
49 //Test Date.prototype.toJSON as generic function. | |
50 var d = {toJSON: Date.prototype.toJSON, | |
51 toISOString: function() { return 42; }}; | |
52 assertEquals(42, d.toJSON()); | |
53 | |
54 var d2 = {toJSON: Date.prototype.toJSON, | |
55 valueOf: function() { return Infinity; }, | |
56 toISOString: function() { return 42; }}; | |
57 assertEquals(null, d2.toJSON()); | |
58 | |
59 var d3 = {toJSON: Date.prototype.toJSON, | |
60 valueOf: "not callable", | |
61 toString: function() { return Infinity; }, | |
62 toISOString: function() { return 42; }}; | |
63 | |
64 assertEquals(null, d3.toJSON()); | |
65 | |
66 var d4 = {toJSON: Date.prototype.toJSON, | |
67 valueOf: "not callable", | |
68 toString: "not callable either", | |
69 toISOString: function() { return 42; }}; | |
70 assertThrows("d4.toJSON()"); // ToPrimitive throws. | |
71 | |
72 var d5 = {toJSON: Date.prototype.toJSON, | |
73 valueOf: "not callable", | |
74 toString: function() { return "Infinity"; }, | |
75 toISOString: function() { return 42; }}; | |
76 assertEquals(42, d5.toJSON()); | |
77 | |
78 var d6 = {toJSON: Date.prototype.toJSON, | |
79 toISOString: function() { return ["not primitive"]; }}; | |
80 assertEquals(["not primitive"], d6.toJSON()); | |
81 | |
Rico
2010/12/15 09:17:04
Add a test that we throw an exception if we have a
Lasse Reichstein
2010/12/15 09:29:41
Added test:
var d7 = {toJSON: Date.prototype.toJS
| |
91 // DontEnum | 82 // DontEnum |
92 for (var p in this) | 83 for (var p in this) |
93 assertFalse(p == "JSON"); | 84 assertFalse(p == "JSON"); |
94 | 85 |
95 // Parse | 86 // Parse |
96 assertEquals({}, JSON.parse("{}")); | 87 assertEquals({}, JSON.parse("{}")); |
97 assertEquals({42:37}, JSON.parse('{"42":37}')); | 88 assertEquals({42:37}, JSON.parse('{"42":37}')); |
98 assertEquals(null, JSON.parse("null")); | 89 assertEquals(null, JSON.parse("null")); |
99 assertEquals(true, JSON.parse("true")); | 90 assertEquals(true, JSON.parse("true")); |
100 assertEquals(false, JSON.parse("false")); | 91 assertEquals(false, JSON.parse("false")); |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
279 assertEquals('{"y":6,"x":5}', JSON.stringify({x:5,y:6}, ['y', 'x'])); | 270 assertEquals('{"y":6,"x":5}', JSON.stringify({x:5,y:6}, ['y', 'x'])); |
280 | 271 |
281 // toJSON get string keys. | 272 // toJSON get string keys. |
282 var checker = {}; | 273 var checker = {}; |
283 var array = [checker]; | 274 var array = [checker]; |
284 checker.toJSON = function(key) { return 1 + key; }; | 275 checker.toJSON = function(key) { return 1 + key; }; |
285 assertEquals('["10"]', JSON.stringify(array)); | 276 assertEquals('["10"]', JSON.stringify(array)); |
286 | 277 |
287 // The gap is capped at ten characters if specified as string. | 278 // The gap is capped at ten characters if specified as string. |
288 assertEquals('{\n "a": "b",\n "c": "d"\n}', | 279 assertEquals('{\n "a": "b",\n "c": "d"\n}', |
289 JSON.stringify({a:"b",c:"d"}, null, | 280 JSON.stringify({a:"b",c:"d"}, null, |
290 " /*characters after 10th*/")); | 281 " /*characters after 10th*/")); |
291 | 282 |
292 //The gap is capped at ten characters if specified as number. | 283 //The gap is capped at ten characters if specified as number. |
293 assertEquals('{\n "a": "b",\n "c": "d"\n}', | 284 assertEquals('{\n "a": "b",\n "c": "d"\n}', |
294 JSON.stringify({a:"b",c:"d"}, null, 15)); | 285 JSON.stringify({a:"b",c:"d"}, null, 15)); |
295 | 286 |
296 // Replaced wrapped primitives are unwrapped. | 287 // Replaced wrapped primitives are unwrapped. |
297 function newx(k, v) { return (k == "x") ? new v(42) : v; } | 288 function newx(k, v) { return (k == "x") ? new v(42) : v; } |
298 assertEquals('{"x":"42"}', JSON.stringify({x: String}, newx)); | 289 assertEquals('{"x":"42"}', JSON.stringify({x: String}, newx)); |
299 assertEquals('{"x":42}', JSON.stringify({x: Number}, newx)); | 290 assertEquals('{"x":42}', JSON.stringify({x: Number}, newx)); |
300 assertEquals('{"x":true}', JSON.stringify({x: Boolean}, newx)); | 291 assertEquals('{"x":true}', JSON.stringify({x: Boolean}, newx)); |
301 | 292 |
302 assertEquals(undefined, JSON.stringify(undefined)); | 293 assertEquals(undefined, JSON.stringify(undefined)); |
303 assertEquals(undefined, JSON.stringify(function () { })); | 294 assertEquals(undefined, JSON.stringify(function () { })); |
304 // Arrays with missing, undefined or function elements have those elements | 295 // Arrays with missing, undefined or function elements have those elements |
305 // replaced by null. | 296 // replaced by null. |
306 assertEquals("[null,null,null]", | 297 assertEquals("[null,null,null]", |
307 JSON.stringify([undefined,,function(){}])); | 298 JSON.stringify([undefined,,function(){}])); |
308 | 299 |
309 // Objects with undefined or function properties (including replaced properties) | 300 // Objects with undefined or function properties (including replaced properties) |
310 // have those properties ignored. | 301 // have those properties ignored. |
311 assertEquals('{}', | 302 assertEquals('{}', |
312 JSON.stringify({a: undefined, b: function(){}, c: 42, d: 42}, | 303 JSON.stringify({a: undefined, b: function(){}, c: 42, d: 42}, |
313 function(k, v) { if (k == "c") return undefined; | 304 function(k, v) { if (k == "c") return undefined; |
314 if (k == "d") return function(){}; | 305 if (k == "d") return function(){}; |
315 return v; })); | 306 return v; })); |
316 | 307 |
317 TestInvalid('1); throw "foo"; (1'); | 308 TestInvalid('1); throw "foo"; (1'); |
318 | 309 |
319 var x = 0; | 310 var x = 0; |
320 eval("(1); x++; (1)"); | 311 eval("(1); x++; (1)"); |
321 TestInvalid('1); x++; (1'); | 312 TestInvalid('1); x++; (1'); |
322 | 313 |
323 // Test string conversion of argument. | 314 // Test string conversion of argument. |
324 var o = { toString: function() { return "42"; } }; | 315 var o = { toString: function() { return "42"; } }; |
325 assertEquals(42, JSON.parse(o)); | 316 assertEquals(42, JSON.parse(o)); |
326 | 317 |
327 | 318 |
328 for (var i = 0; i < 65536; i++) { | 319 for (var i = 0; i < 65536; i++) { |
329 var string = String.fromCharCode(i); | 320 var string = String.fromCharCode(i); |
330 var encoded = JSON.stringify(string); | 321 var encoded = JSON.stringify(string); |
331 var expected = "uninitialized"; | 322 var expected = "uninitialized"; |
332 // Following the ES5 specification of the abstraction function Quote. | 323 // Following the ES5 specification of the abstraction function Quote. |
333 if (string == '"' || string == '\\') { | 324 if (string == '"' || string == '\\') { |
334 // Step 2.a | 325 // Step 2.a |
335 expected = '\\' + string; | 326 expected = '\\' + string; |
336 } else if ("\b\t\n\r\f".indexOf(string) >= 0) { | 327 } else if ("\b\t\n\r\f".indexOf(string) >= 0) { |
337 // Step 2.b | 328 // Step 2.b |
338 if (string == '\b') expected = '\\b'; | 329 if (string == '\b') expected = '\\b'; |
339 else if (string == '\t') expected = '\\t'; | 330 else if (string == '\t') expected = '\\t'; |
340 else if (string == '\n') expected = '\\n'; | 331 else if (string == '\n') expected = '\\n'; |
341 else if (string == '\f') expected = '\\f'; | 332 else if (string == '\f') expected = '\\f'; |
342 else if (string == '\r') expected = '\\r'; | 333 else if (string == '\r') expected = '\\r'; |
343 } else if (i < 32) { | 334 } else if (i < 32) { |
344 // Step 2.c | 335 // Step 2.c |
345 if (i < 16) { | 336 if (i < 16) { |
346 expected = "\\u000" + i.toString(16); | 337 expected = "\\u000" + i.toString(16); |
347 } else { | 338 } else { |
348 expected = "\\u00" + i.toString(16); | 339 expected = "\\u00" + i.toString(16); |
349 } | 340 } |
350 } else { | 341 } else { |
351 expected = string; | 342 expected = string; |
352 } | 343 } |
353 assertEquals('"' + expected + '"', encoded, "Codepoint " + i); | 344 assertEquals('"' + expected + '"', encoded, "Codepoint " + i); |
354 } | 345 } |
346 | |
347 | |
348 // Ensure that wrappers and callables are handled correctly. | |
349 var num37 = new Number(42); | |
350 num37.valueOf = function() { return 37; }; | |
351 | |
352 var numFoo = new Number(42); | |
353 numFoo.valueOf = "not callable"; | |
354 numFoo.toString = function() { return "foo"; }; | |
355 | |
356 var numTrue = new Number(42); | |
357 numTrue.valueOf = function() { return true; } | |
358 | |
359 var strFoo = new String("bar"); | |
360 strFoo.toString = function() { return "foo"; }; | |
361 | |
362 var str37 = new String("bar"); | |
363 str37.toString = "not callable"; | |
364 str37.valueOf = function() { return 37; }; | |
365 | |
366 var strTrue = new String("bar"); | |
367 strTrue.toString = function() { return true; } | |
368 | |
369 var func = function() { /* Is callable */ }; | |
370 | |
371 var funcJSON = function() { /* Is callable */ }; | |
372 funcJSON.toJSON = function() { return "has toJSON"; }; | |
373 | |
374 var re = /Is callable/; | |
375 | |
376 var reJSON = /Is callable/; | |
377 reJSON.toJSON = function() { return "has toJSON"; }; | |
378 | |
379 assertEquals( | |
380 '[37,null,1,"foo","37","true",null,"has toJSON",null,"has toJSON"]', | |
381 JSON.stringify([num37, numFoo, numTrue, | |
382 strFoo, str37, strTrue, | |
383 func, funcJSON, re, reJSON])); | |
384 | |
385 | |
386 var oddball = Object(42); | |
387 oddball.__proto__ = { __proto__: null, toString: function() { return true; } }; | |
388 assertEquals('1', JSON.stringify(oddball)); | |
389 | |
390 var getCount = 0; | |
391 var callCount = 0; | |
392 var counter = { get toJSON() { getCount++; | |
393 return function() { callCount++; | |
394 return 42; }; } }; | |
395 assertEquals('42', JSON.stringify(counter)); | |
396 assertEquals(1, getCount); | |
397 assertEquals(1, callCount); | |
398 | |
399 var oddball2 = Object(42); | |
400 var oddball3 = Object("foo"); | |
401 oddball3.__proto__ = { __proto__: null, | |
402 toString: "not callable", | |
403 valueOf: function() { return true; } }; | |
404 oddball2.__proto__ = { __proto__: null, | |
405 toJSON: function () { return oddball3; } } | |
406 assertEquals('"true"', JSON.stringify(oddball2)); | |
407 | |
408 | |
409 var falseNum = Object("37"); | |
410 falseNum.__proto__ = Number.prototype; | |
411 falseNum.toString = function() { return 42; }; | |
412 assertEquals('"42"', JSON.stringify(falseNum)); | |
OLD | NEW |