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 |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 TestInvalid('null ""'); | 218 TestInvalid('null ""'); |
219 TestInvalid('null ""'); | 219 TestInvalid('null ""'); |
220 TestInvalid('[] ""'); | 220 TestInvalid('[] ""'); |
221 TestInvalid('[true] ""'); | 221 TestInvalid('[true] ""'); |
222 TestInvalid('{} ""'); | 222 TestInvalid('{} ""'); |
223 TestInvalid('{"x":true} ""'); | 223 TestInvalid('{"x":true} ""'); |
224 TestInvalid('"Garbage""After string"'); | 224 TestInvalid('"Garbage""After string"'); |
225 | 225 |
226 // Stringify | 226 // Stringify |
227 | 227 |
228 assertEquals("true", JSON.stringify(true)); | 228 function TestStringify(expected, input) { |
229 assertEquals("false", JSON.stringify(false)); | 229 assertEquals(expected, JSON.stringify(input)); |
230 assertEquals("null", JSON.stringify(null)); | 230 assertEquals(expected, JSON.stringify(input, null, 0)); |
231 assertEquals("false", JSON.stringify({toJSON: function () { return false; }})); | 231 } |
232 assertEquals("4", JSON.stringify(4)); | |
233 assertEquals('"foo"', JSON.stringify("foo")); | |
234 assertEquals("null", JSON.stringify(Infinity)); | |
235 assertEquals("null", JSON.stringify(-Infinity)); | |
236 assertEquals("null", JSON.stringify(NaN)); | |
237 assertEquals("4", JSON.stringify(new Number(4))); | |
238 assertEquals('"bar"', JSON.stringify(new String("bar"))); | |
239 | 232 |
240 assertEquals('"foo\\u0000bar"', JSON.stringify("foo\0bar")); | 233 TestStringify("true", true); |
241 assertEquals('"f\\"o\'o\\\\b\\ba\\fr\\nb\\ra\\tz"', | 234 TestStringify("false", false); |
242 JSON.stringify("f\"o\'o\\b\ba\fr\nb\ra\tz")); | 235 TestStringify("null", null); |
| 236 TestStringify("false", {toJSON: function () { return false; }}); |
| 237 TestStringify("4", 4); |
| 238 TestStringify('"foo"', "foo"); |
| 239 TestStringify("null", Infinity); |
| 240 TestStringify("null", -Infinity); |
| 241 TestStringify("null", NaN); |
| 242 TestStringify("4", new Number(4)); |
| 243 TestStringify('"bar"', new String("bar")); |
243 | 244 |
244 assertEquals("[1,2,3]", JSON.stringify([1, 2, 3])); | 245 TestStringify('"foo\\u0000bar"', "foo\0bar"); |
| 246 TestStringify('"f\\"o\'o\\\\b\\ba\\fr\\nb\\ra\\tz"', |
| 247 "f\"o\'o\\b\ba\fr\nb\ra\tz"); |
| 248 |
| 249 TestStringify("[1,2,3]", [1, 2, 3]); |
245 assertEquals("[\n 1,\n 2,\n 3\n]", JSON.stringify([1, 2, 3], null, 1)); | 250 assertEquals("[\n 1,\n 2,\n 3\n]", JSON.stringify([1, 2, 3], null, 1)); |
246 assertEquals("[\n 1,\n 2,\n 3\n]", JSON.stringify([1, 2, 3], null, 2)); | 251 assertEquals("[\n 1,\n 2,\n 3\n]", JSON.stringify([1, 2, 3], null, 2)); |
247 assertEquals("[\n 1,\n 2,\n 3\n]", | 252 assertEquals("[\n 1,\n 2,\n 3\n]", |
248 JSON.stringify([1, 2, 3], null, new Number(2))); | 253 JSON.stringify([1, 2, 3], null, new Number(2))); |
249 assertEquals("[\n^1,\n^2,\n^3\n]", JSON.stringify([1, 2, 3], null, "^")); | 254 assertEquals("[\n^1,\n^2,\n^3\n]", JSON.stringify([1, 2, 3], null, "^")); |
250 assertEquals("[\n^1,\n^2,\n^3\n]", | 255 assertEquals("[\n^1,\n^2,\n^3\n]", |
251 JSON.stringify([1, 2, 3], null, new String("^"))); | 256 JSON.stringify([1, 2, 3], null, new String("^"))); |
252 assertEquals("[\n 1,\n 2,\n [\n 3,\n [\n 4\n ],\n 5\n ],\n 6,\n 7\n]", | 257 assertEquals("[\n 1,\n 2,\n [\n 3,\n [\n 4\n ],\n 5\n ],\n 6,\n 7\n]", |
253 JSON.stringify([1, 2, [3, [4], 5], 6, 7], null, 1)); | 258 JSON.stringify([1, 2, [3, [4], 5], 6, 7], null, 1)); |
254 assertEquals("[]", JSON.stringify([], null, 1)); | 259 assertEquals("[]", JSON.stringify([], null, 1)); |
255 assertEquals("[1,2,[3,[4],5],6,7]", | 260 assertEquals("[1,2,[3,[4],5],6,7]", |
256 JSON.stringify([1, 2, [3, [4], 5], 6, 7], null)); | 261 JSON.stringify([1, 2, [3, [4], 5], 6, 7], null)); |
257 assertEquals("[2,4,[6,[8],10],12,14]", | 262 assertEquals("[2,4,[6,[8],10],12,14]", |
258 JSON.stringify([1, 2, [3, [4], 5], 6, 7], DoubleNumbers)); | 263 JSON.stringify([1, 2, [3, [4], 5], 6, 7], DoubleNumbers)); |
259 assertEquals('["a","ab","abc"]', JSON.stringify(["a","ab","abc"])); | 264 TestStringify('["a","ab","abc"]', ["a","ab","abc"]); |
260 assertEquals('{"a":1,"c":true}', | 265 TestStringify('{"a":1,"c":true}', { a : 1, |
261 JSON.stringify({ a : 1, | 266 b : function() { 1 }, |
262 b : function() { 1 }, | 267 c : true, |
263 c : true, | 268 d : function() { 2 } }); |
264 d : function() { 2 } })); | 269 TestStringify('[1,null,true,null]', |
265 assertEquals('[1,null,true,null]', | 270 [1, function() { 1 }, true, function() { 2 }]); |
266 JSON.stringify([1, function() { 1 }, true, function() { 2 }])); | 271 TestStringify('"toJSON 123"', |
267 assertEquals('"toJSON 123"', | 272 { toJSON : function() { return 'toJSON 123'; } }); |
268 JSON.stringify({ toJSON : function() { return 'toJSON 123'; } })); | 273 TestStringify('{"a":321}', |
269 assertEquals('{"a":321}', | 274 { a : { toJSON : function() { return 321; } } }); |
270 JSON.stringify({ a : { toJSON : function() { return 321; } } })); | |
271 var counter = 0; | 275 var counter = 0; |
272 assertEquals('{"getter":123}', | 276 assertEquals('{"getter":123}', |
273 JSON.stringify({ get getter() { counter++; return 123; } })); | 277 JSON.stringify({ get getter() { counter++; return 123; } })); |
274 assertEquals(1, counter); | 278 assertEquals(1, counter); |
275 assertEquals('{"a":"abc","b":"\u1234bc"}', | 279 assertEquals('{"getter":123}', |
276 JSON.stringify({ a : "abc", b : "\u1234bc" })); | 280 JSON.stringify({ get getter() { counter++; return 123; } }, |
| 281 null, |
| 282 0)); |
| 283 assertEquals(2, counter); |
| 284 |
| 285 TestStringify('{"a":"abc","b":"\u1234bc"}', |
| 286 { a : "abc", b : "\u1234bc" }); |
277 | 287 |
278 | 288 |
279 var a = { a : 1, b : 2 }; | 289 var a = { a : 1, b : 2 }; |
280 delete a.a; | 290 delete a.a; |
281 assertEquals('{"b":2}', JSON.stringify(a)); | 291 TestStringify('{"b":2}', a); |
282 | 292 |
283 var b = {}; | 293 var b = {}; |
284 b.__proto__ = { toJSON : function() { return 321;} }; | 294 b.__proto__ = { toJSON : function() { return 321;} }; |
285 assertEquals("321", JSON.stringify(b)); | 295 TestStringify("321", b); |
286 | 296 |
287 var array = [""]; | 297 var array = [""]; |
288 var expected = '""'; | 298 var expected = '""'; |
289 for (var i = 0; i < 10000; i++) { | 299 for (var i = 0; i < 10000; i++) { |
290 array.push(""); | 300 array.push(""); |
291 expected = '"",' + expected; | 301 expected = '"",' + expected; |
292 } | 302 } |
293 expected = '[' + expected + ']'; | 303 expected = '[' + expected + ']'; |
294 assertEquals(expected, JSON.stringify(array)); | 304 TestStringify(expected, array); |
295 | 305 |
296 | 306 |
297 var circular = [1, 2, 3]; | 307 var circular = [1, 2, 3]; |
298 circular[2] = circular; | 308 circular[2] = circular; |
299 assertThrows(function () { JSON.stringify(circular); }, TypeError); | 309 assertThrows(function () { JSON.stringify(circular); }, TypeError); |
| 310 assertThrows(function () { JSON.stringify(circular, null, 0); }, TypeError); |
300 | 311 |
301 var singleton = []; | 312 var singleton = []; |
302 var multiOccurrence = [singleton, singleton, singleton]; | 313 var multiOccurrence = [singleton, singleton, singleton]; |
303 assertEquals("[[],[],[]]", JSON.stringify(multiOccurrence)); | 314 TestStringify("[[],[],[]]", multiOccurrence); |
304 | 315 |
305 assertEquals('{"x":5,"y":6}', JSON.stringify({x:5,y:6})); | 316 TestStringify('{"x":5,"y":6}', {x:5,y:6}); |
306 assertEquals('{"x":5}', JSON.stringify({x:5,y:6}, ['x'])); | 317 assertEquals('{"x":5}', JSON.stringify({x:5,y:6}, ['x'])); |
307 assertEquals('{\n "a": "b",\n "c": "d"\n}', | 318 assertEquals('{\n "a": "b",\n "c": "d"\n}', |
308 JSON.stringify({a:"b",c:"d"}, null, 1)); | 319 JSON.stringify({a:"b",c:"d"}, null, 1)); |
309 assertEquals('{"y":6,"x":5}', JSON.stringify({x:5,y:6}, ['y', 'x'])); | 320 assertEquals('{"y":6,"x":5}', JSON.stringify({x:5,y:6}, ['y', 'x'])); |
310 | 321 |
311 // toJSON get string keys. | 322 // toJSON get string keys. |
312 var checker = {}; | 323 var checker = {}; |
313 var array = [checker]; | 324 var array = [checker]; |
314 checker.toJSON = function(key) { return 1 + key; }; | 325 checker.toJSON = function(key) { return 1 + key; }; |
315 assertEquals('["10"]', JSON.stringify(array)); | 326 TestStringify('["10"]', array); |
316 | 327 |
317 // The gap is capped at ten characters if specified as string. | 328 // The gap is capped at ten characters if specified as string. |
318 assertEquals('{\n "a": "b",\n "c": "d"\n}', | 329 assertEquals('{\n "a": "b",\n "c": "d"\n}', |
319 JSON.stringify({a:"b",c:"d"}, null, | 330 JSON.stringify({a:"b",c:"d"}, null, |
320 " /*characters after 10th*/")); | 331 " /*characters after 10th*/")); |
321 | 332 |
322 //The gap is capped at ten characters if specified as number. | 333 //The gap is capped at ten characters if specified as number. |
323 assertEquals('{\n "a": "b",\n "c": "d"\n}', | 334 assertEquals('{\n "a": "b",\n "c": "d"\n}', |
324 JSON.stringify({a:"b",c:"d"}, null, 15)); | 335 JSON.stringify({a:"b",c:"d"}, null, 15)); |
325 | 336 |
326 // Replaced wrapped primitives are unwrapped. | 337 // Replaced wrapped primitives are unwrapped. |
327 function newx(k, v) { return (k == "x") ? new v(42) : v; } | 338 function newx(k, v) { return (k == "x") ? new v(42) : v; } |
328 assertEquals('{"x":"42"}', JSON.stringify({x: String}, newx)); | 339 assertEquals('{"x":"42"}', JSON.stringify({x: String}, newx)); |
329 assertEquals('{"x":42}', JSON.stringify({x: Number}, newx)); | 340 assertEquals('{"x":42}', JSON.stringify({x: Number}, newx)); |
330 assertEquals('{"x":true}', JSON.stringify({x: Boolean}, newx)); | 341 assertEquals('{"x":true}', JSON.stringify({x: Boolean}, newx)); |
331 | 342 |
332 assertEquals(undefined, JSON.stringify(undefined)); | 343 TestStringify(undefined, undefined); |
333 assertEquals(undefined, JSON.stringify(function () { })); | 344 TestStringify(undefined, function () { }); |
334 // Arrays with missing, undefined or function elements have those elements | 345 // Arrays with missing, undefined or function elements have those elements |
335 // replaced by null. | 346 // replaced by null. |
336 assertEquals("[null,null,null]", | 347 TestStringify("[null,null,null]", [undefined,,function(){}]); |
337 JSON.stringify([undefined,,function(){}])); | |
338 | 348 |
339 // Objects with undefined or function properties (including replaced properties) | 349 // Objects with undefined or function properties (including replaced properties) |
340 // have those properties ignored. | 350 // have those properties ignored. |
341 assertEquals('{}', | 351 assertEquals('{}', |
342 JSON.stringify({a: undefined, b: function(){}, c: 42, d: 42}, | 352 JSON.stringify({a: undefined, b: function(){}, c: 42, d: 42}, |
343 function(k, v) { if (k == "c") return undefined; | 353 function(k, v) { if (k == "c") return undefined; |
344 if (k == "d") return function(){}; | 354 if (k == "d") return function(){}; |
345 return v; })); | 355 return v; })); |
346 | 356 |
347 TestInvalid('1); throw "foo"; (1'); | 357 TestInvalid('1); throw "foo"; (1'); |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
408 var func = function() { /* Is callable */ }; | 418 var func = function() { /* Is callable */ }; |
409 | 419 |
410 var funcJSON = function() { /* Is callable */ }; | 420 var funcJSON = function() { /* Is callable */ }; |
411 funcJSON.toJSON = function() { return "has toJSON"; }; | 421 funcJSON.toJSON = function() { return "has toJSON"; }; |
412 | 422 |
413 var re = /Is callable/; | 423 var re = /Is callable/; |
414 | 424 |
415 var reJSON = /Is callable/; | 425 var reJSON = /Is callable/; |
416 reJSON.toJSON = function() { return "has toJSON"; }; | 426 reJSON.toJSON = function() { return "has toJSON"; }; |
417 | 427 |
418 assertEquals( | 428 TestStringify('[37,null,1,"foo","37","true",null,"has toJSON",{},"has toJSON"]', |
419 '[37,null,1,"foo","37","true",null,"has toJSON",{},"has toJSON"]', | 429 [num37, numFoo, numTrue, |
420 JSON.stringify([num37, numFoo, numTrue, | 430 strFoo, str37, strTrue, |
421 strFoo, str37, strTrue, | 431 func, funcJSON, re, reJSON]); |
422 func, funcJSON, re, reJSON])); | |
423 | 432 |
424 | 433 |
425 var oddball = Object(42); | 434 var oddball = Object(42); |
426 oddball.__proto__ = { __proto__: null, toString: function() { return true; } }; | 435 oddball.__proto__ = { __proto__: null, toString: function() { return true; } }; |
427 assertEquals('1', JSON.stringify(oddball)); | 436 TestStringify('1', oddball); |
428 | 437 |
429 var getCount = 0; | 438 var getCount = 0; |
430 var callCount = 0; | 439 var callCount = 0; |
431 var counter = { get toJSON() { getCount++; | 440 var counter = { get toJSON() { getCount++; |
432 return function() { callCount++; | 441 return function() { callCount++; |
433 return 42; }; } }; | 442 return 42; }; } }; |
434 | 443 |
435 // RegExps are not callable, so they are stringified as objects. | 444 // RegExps are not callable, so they are stringified as objects. |
436 assertEquals('{}', JSON.stringify(/regexp/)); | 445 TestStringify('{}', /regexp/); |
437 assertEquals('42', JSON.stringify(counter)); | 446 TestStringify('42', counter); |
438 assertEquals(1, getCount); | 447 assertEquals(2, getCount); |
439 assertEquals(1, callCount); | 448 assertEquals(2, callCount); |
440 | 449 |
441 var oddball2 = Object(42); | 450 var oddball2 = Object(42); |
442 var oddball3 = Object("foo"); | 451 var oddball3 = Object("foo"); |
443 oddball3.__proto__ = { __proto__: null, | 452 oddball3.__proto__ = { __proto__: null, |
444 toString: "not callable", | 453 toString: "not callable", |
445 valueOf: function() { return true; } }; | 454 valueOf: function() { return true; } }; |
446 oddball2.__proto__ = { __proto__: null, | 455 oddball2.__proto__ = { __proto__: null, |
447 toJSON: function () { return oddball3; } } | 456 toJSON: function () { return oddball3; } } |
448 assertEquals('"true"', JSON.stringify(oddball2)); | 457 TestStringify('"true"', oddball2); |
449 | 458 |
450 | 459 |
451 var falseNum = Object("37"); | 460 var falseNum = Object("37"); |
452 falseNum.__proto__ = Number.prototype; | 461 falseNum.__proto__ = Number.prototype; |
453 falseNum.toString = function() { return 42; }; | 462 falseNum.toString = function() { return 42; }; |
454 assertEquals('"42"', JSON.stringify(falseNum)); | 463 TestStringify('"42"', falseNum); |
455 | 464 |
456 // Parse an object value as __proto__. | 465 // Parse an object value as __proto__. |
457 var o1 = JSON.parse('{"__proto__":[]}'); | 466 var o1 = JSON.parse('{"__proto__":[]}'); |
458 assertEquals([], o1.__proto__); | 467 assertEquals([], o1.__proto__); |
459 assertEquals(["__proto__"], Object.keys(o1)); | 468 assertEquals(["__proto__"], Object.keys(o1)); |
460 assertEquals([], Object.getOwnPropertyDescriptor(o1, "__proto__").value); | 469 assertEquals([], Object.getOwnPropertyDescriptor(o1, "__proto__").value); |
461 assertEquals(["__proto__"], Object.getOwnPropertyNames(o1)); | 470 assertEquals(["__proto__"], Object.getOwnPropertyNames(o1)); |
462 assertTrue(o1.hasOwnProperty("__proto__")); | 471 assertTrue(o1.hasOwnProperty("__proto__")); |
463 assertTrue(Object.prototype.isPrototypeOf(o1)); | 472 assertTrue(Object.prototype.isPrototypeOf(o1)); |
464 | 473 |
465 // Parse a non-object value as __proto__. | 474 // Parse a non-object value as __proto__. |
466 var o2 = JSON.parse('{"__proto__":5}'); | 475 var o2 = JSON.parse('{"__proto__":5}'); |
467 assertEquals(5, o2.__proto__); | 476 assertEquals(5, o2.__proto__); |
468 assertEquals(["__proto__"], Object.keys(o2)); | 477 assertEquals(["__proto__"], Object.keys(o2)); |
469 assertEquals(5, Object.getOwnPropertyDescriptor(o2, "__proto__").value); | 478 assertEquals(5, Object.getOwnPropertyDescriptor(o2, "__proto__").value); |
470 assertEquals(["__proto__"], Object.getOwnPropertyNames(o2)); | 479 assertEquals(["__proto__"], Object.getOwnPropertyNames(o2)); |
471 assertTrue(o2.hasOwnProperty("__proto__")); | 480 assertTrue(o2.hasOwnProperty("__proto__")); |
472 assertTrue(Object.prototype.isPrototypeOf(o2)); | 481 assertTrue(Object.prototype.isPrototypeOf(o2)); |
473 | 482 |
474 var json = '{"stuff before slash\\\\stuff after slash":"whatever"}'; | 483 var json = '{"stuff before slash\\\\stuff after slash":"whatever"}'; |
475 assertEquals(json, JSON.stringify(JSON.parse(json))); | 484 TestStringify(json, JSON.parse(json)); |
OLD | NEW |