Chromium Code Reviews| 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 |