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 d1 = {toJSON: Date.prototype.toJSON, |
| 51 toISOString: function() { return 42; }}; |
| 52 assertEquals(42, d1.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()", TypeError); // 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 |
| 82 var d7 = {toJSON: Date.prototype.toJSON, |
| 83 ISOString: "not callable"}; |
| 84 assertThrows("d7.toJSON()", TypeError); |
| 85 |
91 // DontEnum | 86 // DontEnum |
92 for (var p in this) | 87 for (var p in this) { |
93 assertFalse(p == "JSON"); | 88 assertFalse(p == "JSON"); |
| 89 } |
94 | 90 |
95 // Parse | 91 // Parse |
96 assertEquals({}, JSON.parse("{}")); | 92 assertEquals({}, JSON.parse("{}")); |
97 assertEquals({42:37}, JSON.parse('{"42":37}')); | 93 assertEquals({42:37}, JSON.parse('{"42":37}')); |
98 assertEquals(null, JSON.parse("null")); | 94 assertEquals(null, JSON.parse("null")); |
99 assertEquals(true, JSON.parse("true")); | 95 assertEquals(true, JSON.parse("true")); |
100 assertEquals(false, JSON.parse("false")); | 96 assertEquals(false, JSON.parse("false")); |
101 assertEquals("foo", JSON.parse('"foo"')); | 97 assertEquals("foo", JSON.parse('"foo"')); |
102 assertEquals("f\no", JSON.parse('"f\\no"')); | 98 assertEquals("f\no", JSON.parse('"f\\no"')); |
103 assertEquals("\b\f\n\r\t\"\u2028\/\\", | 99 assertEquals("\b\f\n\r\t\"\u2028\/\\", |
(...skipping 175 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'])); | 275 assertEquals('{"y":6,"x":5}', JSON.stringify({x:5,y:6}, ['y', 'x'])); |
280 | 276 |
281 // toJSON get string keys. | 277 // toJSON get string keys. |
282 var checker = {}; | 278 var checker = {}; |
283 var array = [checker]; | 279 var array = [checker]; |
284 checker.toJSON = function(key) { return 1 + key; }; | 280 checker.toJSON = function(key) { return 1 + key; }; |
285 assertEquals('["10"]', JSON.stringify(array)); | 281 assertEquals('["10"]', JSON.stringify(array)); |
286 | 282 |
287 // The gap is capped at ten characters if specified as string. | 283 // The gap is capped at ten characters if specified as string. |
288 assertEquals('{\n "a": "b",\n "c": "d"\n}', | 284 assertEquals('{\n "a": "b",\n "c": "d"\n}', |
289 JSON.stringify({a:"b",c:"d"}, null, | 285 JSON.stringify({a:"b",c:"d"}, null, |
290 " /*characters after 10th*/")); | 286 " /*characters after 10th*/")); |
291 | 287 |
292 //The gap is capped at ten characters if specified as number. | 288 //The gap is capped at ten characters if specified as number. |
293 assertEquals('{\n "a": "b",\n "c": "d"\n}', | 289 assertEquals('{\n "a": "b",\n "c": "d"\n}', |
294 JSON.stringify({a:"b",c:"d"}, null, 15)); | 290 JSON.stringify({a:"b",c:"d"}, null, 15)); |
295 | 291 |
296 // Replaced wrapped primitives are unwrapped. | 292 // Replaced wrapped primitives are unwrapped. |
297 function newx(k, v) { return (k == "x") ? new v(42) : v; } | 293 function newx(k, v) { return (k == "x") ? new v(42) : v; } |
298 assertEquals('{"x":"42"}', JSON.stringify({x: String}, newx)); | 294 assertEquals('{"x":"42"}', JSON.stringify({x: String}, newx)); |
299 assertEquals('{"x":42}', JSON.stringify({x: Number}, newx)); | 295 assertEquals('{"x":42}', JSON.stringify({x: Number}, newx)); |
300 assertEquals('{"x":true}', JSON.stringify({x: Boolean}, newx)); | 296 assertEquals('{"x":true}', JSON.stringify({x: Boolean}, newx)); |
301 | 297 |
302 assertEquals(undefined, JSON.stringify(undefined)); | 298 assertEquals(undefined, JSON.stringify(undefined)); |
303 assertEquals(undefined, JSON.stringify(function () { })); | 299 assertEquals(undefined, JSON.stringify(function () { })); |
304 // Arrays with missing, undefined or function elements have those elements | 300 // Arrays with missing, undefined or function elements have those elements |
305 // replaced by null. | 301 // replaced by null. |
306 assertEquals("[null,null,null]", | 302 assertEquals("[null,null,null]", |
307 JSON.stringify([undefined,,function(){}])); | 303 JSON.stringify([undefined,,function(){}])); |
308 | 304 |
309 // Objects with undefined or function properties (including replaced properties) | 305 // Objects with undefined or function properties (including replaced properties) |
310 // have those properties ignored. | 306 // have those properties ignored. |
311 assertEquals('{}', | 307 assertEquals('{}', |
312 JSON.stringify({a: undefined, b: function(){}, c: 42, d: 42}, | 308 JSON.stringify({a: undefined, b: function(){}, c: 42, d: 42}, |
313 function(k, v) { if (k == "c") return undefined; | 309 function(k, v) { if (k == "c") return undefined; |
314 if (k == "d") return function(){}; | 310 if (k == "d") return function(){}; |
315 return v; })); | 311 return v; })); |
316 | 312 |
317 TestInvalid('1); throw "foo"; (1'); | 313 TestInvalid('1); throw "foo"; (1'); |
318 | 314 |
319 var x = 0; | 315 var x = 0; |
320 eval("(1); x++; (1)"); | 316 eval("(1); x++; (1)"); |
321 TestInvalid('1); x++; (1'); | 317 TestInvalid('1); x++; (1'); |
322 | 318 |
323 // Test string conversion of argument. | 319 // Test string conversion of argument. |
324 var o = { toString: function() { return "42"; } }; | 320 var o = { toString: function() { return "42"; } }; |
325 assertEquals(42, JSON.parse(o)); | 321 assertEquals(42, JSON.parse(o)); |
326 | 322 |
327 | 323 |
328 for (var i = 0; i < 65536; i++) { | 324 for (var i = 0; i < 65536; i++) { |
329 var string = String.fromCharCode(i); | 325 var string = String.fromCharCode(i); |
330 var encoded = JSON.stringify(string); | 326 var encoded = JSON.stringify(string); |
331 var expected = "uninitialized"; | 327 var expected = "uninitialized"; |
332 // Following the ES5 specification of the abstraction function Quote. | 328 // Following the ES5 specification of the abstraction function Quote. |
333 if (string == '"' || string == '\\') { | 329 if (string == '"' || string == '\\') { |
334 // Step 2.a | 330 // Step 2.a |
335 expected = '\\' + string; | 331 expected = '\\' + string; |
336 } else if ("\b\t\n\r\f".indexOf(string) >= 0) { | 332 } else if ("\b\t\n\r\f".indexOf(string) >= 0) { |
337 // Step 2.b | 333 // Step 2.b |
338 if (string == '\b') expected = '\\b'; | 334 if (string == '\b') expected = '\\b'; |
339 else if (string == '\t') expected = '\\t'; | 335 else if (string == '\t') expected = '\\t'; |
340 else if (string == '\n') expected = '\\n'; | 336 else if (string == '\n') expected = '\\n'; |
341 else if (string == '\f') expected = '\\f'; | 337 else if (string == '\f') expected = '\\f'; |
342 else if (string == '\r') expected = '\\r'; | 338 else if (string == '\r') expected = '\\r'; |
343 } else if (i < 32) { | 339 } else if (i < 32) { |
344 // Step 2.c | 340 // Step 2.c |
345 if (i < 16) { | 341 if (i < 16) { |
346 expected = "\\u000" + i.toString(16); | 342 expected = "\\u000" + i.toString(16); |
347 } else { | 343 } else { |
348 expected = "\\u00" + i.toString(16); | 344 expected = "\\u00" + i.toString(16); |
349 } | 345 } |
350 } else { | 346 } else { |
351 expected = string; | 347 expected = string; |
352 } | 348 } |
353 assertEquals('"' + expected + '"', encoded, "Codepoint " + i); | 349 assertEquals('"' + expected + '"', encoded, "Codepoint " + i); |
354 } | 350 } |
| 351 |
| 352 |
| 353 // Ensure that wrappers and callables are handled correctly. |
| 354 var num37 = new Number(42); |
| 355 num37.valueOf = function() { return 37; }; |
| 356 |
| 357 var numFoo = new Number(42); |
| 358 numFoo.valueOf = "not callable"; |
| 359 numFoo.toString = function() { return "foo"; }; |
| 360 |
| 361 var numTrue = new Number(42); |
| 362 numTrue.valueOf = function() { return true; } |
| 363 |
| 364 var strFoo = new String("bar"); |
| 365 strFoo.toString = function() { return "foo"; }; |
| 366 |
| 367 var str37 = new String("bar"); |
| 368 str37.toString = "not callable"; |
| 369 str37.valueOf = function() { return 37; }; |
| 370 |
| 371 var strTrue = new String("bar"); |
| 372 strTrue.toString = function() { return true; } |
| 373 |
| 374 var func = function() { /* Is callable */ }; |
| 375 |
| 376 var funcJSON = function() { /* Is callable */ }; |
| 377 funcJSON.toJSON = function() { return "has toJSON"; }; |
| 378 |
| 379 var re = /Is callable/; |
| 380 |
| 381 var reJSON = /Is callable/; |
| 382 reJSON.toJSON = function() { return "has toJSON"; }; |
| 383 |
| 384 assertEquals( |
| 385 '[37,null,1,"foo","37","true",null,"has toJSON",null,"has toJSON"]', |
| 386 JSON.stringify([num37, numFoo, numTrue, |
| 387 strFoo, str37, strTrue, |
| 388 func, funcJSON, re, reJSON])); |
| 389 |
| 390 |
| 391 var oddball = Object(42); |
| 392 oddball.__proto__ = { __proto__: null, toString: function() { return true; } }; |
| 393 assertEquals('1', JSON.stringify(oddball)); |
| 394 |
| 395 var getCount = 0; |
| 396 var callCount = 0; |
| 397 var counter = { get toJSON() { getCount++; |
| 398 return function() { callCount++; |
| 399 return 42; }; } }; |
| 400 assertEquals('42', JSON.stringify(counter)); |
| 401 assertEquals(1, getCount); |
| 402 assertEquals(1, callCount); |
| 403 |
| 404 var oddball2 = Object(42); |
| 405 var oddball3 = Object("foo"); |
| 406 oddball3.__proto__ = { __proto__: null, |
| 407 toString: "not callable", |
| 408 valueOf: function() { return true; } }; |
| 409 oddball2.__proto__ = { __proto__: null, |
| 410 toJSON: function () { return oddball3; } } |
| 411 assertEquals('"true"', JSON.stringify(oddball2)); |
| 412 |
| 413 |
| 414 var falseNum = Object("37"); |
| 415 falseNum.__proto__ = Number.prototype; |
| 416 falseNum.toString = function() { return 42; }; |
| 417 assertEquals('"42"', JSON.stringify(falseNum)); |
OLD | NEW |