Commit b522319a authored by yangguo@chromium.org's avatar yangguo@chromium.org

Extend test coverage for JSON.stringify's slow path.

R=verwaest@chromium.org
BUG=

Review URL: https://chromiumcodereview.appspot.com/12702009

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14008 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a23caf9f
...@@ -225,23 +225,28 @@ TestInvalid('"Garbage""After string"'); ...@@ -225,23 +225,28 @@ TestInvalid('"Garbage""After string"');
// Stringify // Stringify
assertEquals("true", JSON.stringify(true)); function TestStringify(expected, input) {
assertEquals("false", JSON.stringify(false)); assertEquals(expected, JSON.stringify(input));
assertEquals("null", JSON.stringify(null)); assertEquals(expected, JSON.stringify(input, null, 0));
assertEquals("false", JSON.stringify({toJSON: function () { return false; }})); }
assertEquals("4", JSON.stringify(4));
assertEquals('"foo"', JSON.stringify("foo")); TestStringify("true", true);
assertEquals("null", JSON.stringify(Infinity)); TestStringify("false", false);
assertEquals("null", JSON.stringify(-Infinity)); TestStringify("null", null);
assertEquals("null", JSON.stringify(NaN)); TestStringify("false", {toJSON: function () { return false; }});
assertEquals("4", JSON.stringify(new Number(4))); TestStringify("4", 4);
assertEquals('"bar"', JSON.stringify(new String("bar"))); TestStringify('"foo"', "foo");
TestStringify("null", Infinity);
assertEquals('"foo\\u0000bar"', JSON.stringify("foo\0bar")); TestStringify("null", -Infinity);
assertEquals('"f\\"o\'o\\\\b\\ba\\fr\\nb\\ra\\tz"', TestStringify("null", NaN);
JSON.stringify("f\"o\'o\\b\ba\fr\nb\ra\tz")); TestStringify("4", new Number(4));
TestStringify('"bar"', new String("bar"));
assertEquals("[1,2,3]", JSON.stringify([1, 2, 3]));
TestStringify('"foo\\u0000bar"', "foo\0bar");
TestStringify('"f\\"o\'o\\\\b\\ba\\fr\\nb\\ra\\tz"',
"f\"o\'o\\b\ba\fr\nb\ra\tz");
TestStringify("[1,2,3]", [1, 2, 3]);
assertEquals("[\n 1,\n 2,\n 3\n]", JSON.stringify([1, 2, 3], null, 1)); assertEquals("[\n 1,\n 2,\n 3\n]", JSON.stringify([1, 2, 3], null, 1));
assertEquals("[\n 1,\n 2,\n 3\n]", JSON.stringify([1, 2, 3], null, 2)); assertEquals("[\n 1,\n 2,\n 3\n]", JSON.stringify([1, 2, 3], null, 2));
assertEquals("[\n 1,\n 2,\n 3\n]", assertEquals("[\n 1,\n 2,\n 3\n]",
...@@ -256,33 +261,38 @@ assertEquals("[1,2,[3,[4],5],6,7]", ...@@ -256,33 +261,38 @@ assertEquals("[1,2,[3,[4],5],6,7]",
JSON.stringify([1, 2, [3, [4], 5], 6, 7], null)); JSON.stringify([1, 2, [3, [4], 5], 6, 7], null));
assertEquals("[2,4,[6,[8],10],12,14]", assertEquals("[2,4,[6,[8],10],12,14]",
JSON.stringify([1, 2, [3, [4], 5], 6, 7], DoubleNumbers)); JSON.stringify([1, 2, [3, [4], 5], 6, 7], DoubleNumbers));
assertEquals('["a","ab","abc"]', JSON.stringify(["a","ab","abc"])); TestStringify('["a","ab","abc"]', ["a","ab","abc"]);
assertEquals('{"a":1,"c":true}', TestStringify('{"a":1,"c":true}', { a : 1,
JSON.stringify({ a : 1, b : function() { 1 },
b : function() { 1 }, c : true,
c : true, d : function() { 2 } });
d : function() { 2 } })); TestStringify('[1,null,true,null]',
assertEquals('[1,null,true,null]', [1, function() { 1 }, true, function() { 2 }]);
JSON.stringify([1, function() { 1 }, true, function() { 2 }])); TestStringify('"toJSON 123"',
assertEquals('"toJSON 123"', { toJSON : function() { return 'toJSON 123'; } });
JSON.stringify({ toJSON : function() { return 'toJSON 123'; } })); TestStringify('{"a":321}',
assertEquals('{"a":321}', { a : { toJSON : function() { return 321; } } });
JSON.stringify({ a : { toJSON : function() { return 321; } } }));
var counter = 0; var counter = 0;
assertEquals('{"getter":123}', assertEquals('{"getter":123}',
JSON.stringify({ get getter() { counter++; return 123; } })); JSON.stringify({ get getter() { counter++; return 123; } }));
assertEquals(1, counter); assertEquals(1, counter);
assertEquals('{"a":"abc","b":"\u1234bc"}', assertEquals('{"getter":123}',
JSON.stringify({ a : "abc", b : "\u1234bc" })); JSON.stringify({ get getter() { counter++; return 123; } },
null,
0));
assertEquals(2, counter);
TestStringify('{"a":"abc","b":"\u1234bc"}',
{ a : "abc", b : "\u1234bc" });
var a = { a : 1, b : 2 }; var a = { a : 1, b : 2 };
delete a.a; delete a.a;
assertEquals('{"b":2}', JSON.stringify(a)); TestStringify('{"b":2}', a);
var b = {}; var b = {};
b.__proto__ = { toJSON : function() { return 321;} }; b.__proto__ = { toJSON : function() { return 321;} };
assertEquals("321", JSON.stringify(b)); TestStringify("321", b);
var array = [""]; var array = [""];
var expected = '""'; var expected = '""';
...@@ -291,18 +301,19 @@ for (var i = 0; i < 10000; i++) { ...@@ -291,18 +301,19 @@ for (var i = 0; i < 10000; i++) {
expected = '"",' + expected; expected = '"",' + expected;
} }
expected = '[' + expected + ']'; expected = '[' + expected + ']';
assertEquals(expected, JSON.stringify(array)); TestStringify(expected, array);
var circular = [1, 2, 3]; var circular = [1, 2, 3];
circular[2] = circular; circular[2] = circular;
assertThrows(function () { JSON.stringify(circular); }, TypeError); assertThrows(function () { JSON.stringify(circular); }, TypeError);
assertThrows(function () { JSON.stringify(circular, null, 0); }, TypeError);
var singleton = []; var singleton = [];
var multiOccurrence = [singleton, singleton, singleton]; var multiOccurrence = [singleton, singleton, singleton];
assertEquals("[[],[],[]]", JSON.stringify(multiOccurrence)); TestStringify("[[],[],[]]", multiOccurrence);
assertEquals('{"x":5,"y":6}', JSON.stringify({x:5,y:6})); TestStringify('{"x":5,"y":6}', {x:5,y:6});
assertEquals('{"x":5}', JSON.stringify({x:5,y:6}, ['x'])); assertEquals('{"x":5}', JSON.stringify({x:5,y:6}, ['x']));
assertEquals('{\n "a": "b",\n "c": "d"\n}', assertEquals('{\n "a": "b",\n "c": "d"\n}',
JSON.stringify({a:"b",c:"d"}, null, 1)); JSON.stringify({a:"b",c:"d"}, null, 1));
...@@ -312,7 +323,7 @@ assertEquals('{"y":6,"x":5}', JSON.stringify({x:5,y:6}, ['y', 'x'])); ...@@ -312,7 +323,7 @@ assertEquals('{"y":6,"x":5}', JSON.stringify({x:5,y:6}, ['y', 'x']));
var checker = {}; var checker = {};
var array = [checker]; var array = [checker];
checker.toJSON = function(key) { return 1 + key; }; checker.toJSON = function(key) { return 1 + key; };
assertEquals('["10"]', JSON.stringify(array)); TestStringify('["10"]', array);
// The gap is capped at ten characters if specified as string. // The gap is capped at ten characters if specified as string.
assertEquals('{\n "a": "b",\n "c": "d"\n}', assertEquals('{\n "a": "b",\n "c": "d"\n}',
...@@ -329,12 +340,11 @@ assertEquals('{"x":"42"}', JSON.stringify({x: String}, newx)); ...@@ -329,12 +340,11 @@ assertEquals('{"x":"42"}', JSON.stringify({x: String}, newx));
assertEquals('{"x":42}', JSON.stringify({x: Number}, newx)); assertEquals('{"x":42}', JSON.stringify({x: Number}, newx));
assertEquals('{"x":true}', JSON.stringify({x: Boolean}, newx)); assertEquals('{"x":true}', JSON.stringify({x: Boolean}, newx));
assertEquals(undefined, JSON.stringify(undefined)); TestStringify(undefined, undefined);
assertEquals(undefined, JSON.stringify(function () { })); TestStringify(undefined, function () { });
// Arrays with missing, undefined or function elements have those elements // Arrays with missing, undefined or function elements have those elements
// replaced by null. // replaced by null.
assertEquals("[null,null,null]", TestStringify("[null,null,null]", [undefined,,function(){}]);
JSON.stringify([undefined,,function(){}]));
// Objects with undefined or function properties (including replaced properties) // Objects with undefined or function properties (including replaced properties)
// have those properties ignored. // have those properties ignored.
...@@ -415,16 +425,15 @@ var re = /Is callable/; ...@@ -415,16 +425,15 @@ var re = /Is callable/;
var reJSON = /Is callable/; var reJSON = /Is callable/;
reJSON.toJSON = function() { return "has toJSON"; }; reJSON.toJSON = function() { return "has toJSON"; };
assertEquals( TestStringify('[37,null,1,"foo","37","true",null,"has toJSON",{},"has toJSON"]',
'[37,null,1,"foo","37","true",null,"has toJSON",{},"has toJSON"]', [num37, numFoo, numTrue,
JSON.stringify([num37, numFoo, numTrue, strFoo, str37, strTrue,
strFoo, str37, strTrue, func, funcJSON, re, reJSON]);
func, funcJSON, re, reJSON]));
var oddball = Object(42); var oddball = Object(42);
oddball.__proto__ = { __proto__: null, toString: function() { return true; } }; oddball.__proto__ = { __proto__: null, toString: function() { return true; } };
assertEquals('1', JSON.stringify(oddball)); TestStringify('1', oddball);
var getCount = 0; var getCount = 0;
var callCount = 0; var callCount = 0;
...@@ -433,10 +442,10 @@ var counter = { get toJSON() { getCount++; ...@@ -433,10 +442,10 @@ var counter = { get toJSON() { getCount++;
return 42; }; } }; return 42; }; } };
// RegExps are not callable, so they are stringified as objects. // RegExps are not callable, so they are stringified as objects.
assertEquals('{}', JSON.stringify(/regexp/)); TestStringify('{}', /regexp/);
assertEquals('42', JSON.stringify(counter)); TestStringify('42', counter);
assertEquals(1, getCount); assertEquals(2, getCount);
assertEquals(1, callCount); assertEquals(2, callCount);
var oddball2 = Object(42); var oddball2 = Object(42);
var oddball3 = Object("foo"); var oddball3 = Object("foo");
...@@ -445,13 +454,13 @@ oddball3.__proto__ = { __proto__: null, ...@@ -445,13 +454,13 @@ oddball3.__proto__ = { __proto__: null,
valueOf: function() { return true; } }; valueOf: function() { return true; } };
oddball2.__proto__ = { __proto__: null, oddball2.__proto__ = { __proto__: null,
toJSON: function () { return oddball3; } } toJSON: function () { return oddball3; } }
assertEquals('"true"', JSON.stringify(oddball2)); TestStringify('"true"', oddball2);
var falseNum = Object("37"); var falseNum = Object("37");
falseNum.__proto__ = Number.prototype; falseNum.__proto__ = Number.prototype;
falseNum.toString = function() { return 42; }; falseNum.toString = function() { return 42; };
assertEquals('"42"', JSON.stringify(falseNum)); TestStringify('"42"', falseNum);
// Parse an object value as __proto__. // Parse an object value as __proto__.
var o1 = JSON.parse('{"__proto__":[]}'); var o1 = JSON.parse('{"__proto__":[]}');
...@@ -472,4 +481,4 @@ assertTrue(o2.hasOwnProperty("__proto__")); ...@@ -472,4 +481,4 @@ assertTrue(o2.hasOwnProperty("__proto__"));
assertTrue(Object.prototype.isPrototypeOf(o2)); assertTrue(Object.prototype.isPrototypeOf(o2));
var json = '{"stuff before slash\\\\stuff after slash":"whatever"}'; var json = '{"stuff before slash\\\\stuff after slash":"whatever"}';
assertEquals(json, JSON.stringify(JSON.parse(json))); TestStringify(json, JSON.parse(json));
...@@ -30,8 +30,14 @@ ...@@ -30,8 +30,14 @@
// Test JSON.stringify on the global object. // Test JSON.stringify on the global object.
var a = 12345; var a = 12345;
assertTrue(JSON.stringify(this).indexOf('"a":12345') > 0); assertTrue(JSON.stringify(this).indexOf('"a":12345') > 0);
assertTrue(JSON.stringify(this, null, 0).indexOf('"a":12345') > 0);
// Test JSON.stringify of array in dictionary mode. // Test JSON.stringify of array in dictionary mode.
function TestStringify(expected, input) {
assertEquals(expected, JSON.stringify(input));
assertEquals(expected, JSON.stringify(input, null, 0));
}
var array_1 = []; var array_1 = [];
var array_2 = []; var array_2 = [];
array_1[100000] = 1; array_1[100000] = 1;
...@@ -42,25 +48,25 @@ for (var i = 0; i < 100000; i++) { ...@@ -42,25 +48,25 @@ for (var i = 0; i < 100000; i++) {
} }
expected_1 = '[' + nulls + '1]'; expected_1 = '[' + nulls + '1]';
expected_2 = '[' + nulls + 'null]'; expected_2 = '[' + nulls + 'null]';
assertEquals(expected_1, JSON.stringify(array_1)); TestStringify(expected_1, array_1);
assertEquals(expected_2, JSON.stringify(array_2)); TestStringify(expected_2, array_2);
// Test JSValue with custom prototype. // Test JSValue with custom prototype.
var num_wrapper = Object(42); var num_wrapper = Object(42);
num_wrapper.__proto__ = { __proto__: null, num_wrapper.__proto__ = { __proto__: null,
toString: function() { return true; } }; toString: function() { return true; } };
assertEquals('1', JSON.stringify(num_wrapper)); TestStringify('1', num_wrapper);
var str_wrapper = Object('2'); var str_wrapper = Object('2');
str_wrapper.__proto__ = { __proto__: null, str_wrapper.__proto__ = { __proto__: null,
toString: function() { return true; } }; toString: function() { return true; } };
assertEquals('"true"', JSON.stringify(str_wrapper)); TestStringify('"true"', str_wrapper);
var bool_wrapper = Object(false); var bool_wrapper = Object(false);
bool_wrapper.__proto__ = { __proto__: null, bool_wrapper.__proto__ = { __proto__: null,
toString: function() { return true; } }; toString: function() { return true; } };
// Note that toString function is not evaluated here! // Note that toString function is not evaluated here!
assertEquals('false', JSON.stringify(bool_wrapper)); TestStringify('false', bool_wrapper);
// Test getters. // Test getters.
var counter = 0; var counter = 0;
...@@ -68,8 +74,8 @@ var getter_obj = { get getter() { ...@@ -68,8 +74,8 @@ var getter_obj = { get getter() {
counter++; counter++;
return 123; return 123;
} }; } };
assertEquals('{"getter":123}', JSON.stringify(getter_obj)); TestStringify('{"getter":123}', getter_obj);
assertEquals(1, counter); assertEquals(2, counter);
// Test toJSON function. // Test toJSON function.
var tojson_obj = { toJSON: function() { var tojson_obj = { toJSON: function() {
...@@ -77,8 +83,8 @@ var tojson_obj = { toJSON: function() { ...@@ -77,8 +83,8 @@ var tojson_obj = { toJSON: function() {
return [1, 2]; return [1, 2];
}, },
a: 1}; a: 1};
assertEquals('[1,2]', JSON.stringify(tojson_obj)); TestStringify('[1,2]', tojson_obj);
assertEquals(2, counter); assertEquals(4, counter);
// Test that we don't recursively look for the toJSON function. // Test that we don't recursively look for the toJSON function.
var tojson_proto_obj = { a: 'fail' }; var tojson_proto_obj = { a: 'fail' };
...@@ -86,7 +92,7 @@ tojson_proto_obj.__proto__ = { toJSON: function() { ...@@ -86,7 +92,7 @@ tojson_proto_obj.__proto__ = { toJSON: function() {
counter++; counter++;
return tojson_obj; return tojson_obj;
} }; } };
assertEquals('{"a":1}', JSON.stringify(tojson_proto_obj)); TestStringify('{"a":1}', tojson_proto_obj);
// Test toJSON produced by a getter. // Test toJSON produced by a getter.
var tojson_via_getter = { get toJSON() { var tojson_via_getter = { get toJSON() {
...@@ -96,43 +102,44 @@ var tojson_via_getter = { get toJSON() { ...@@ -96,43 +102,44 @@ var tojson_via_getter = { get toJSON() {
}; };
}, },
a: 1 }; a: 1 };
assertEquals('321', JSON.stringify(tojson_via_getter)); TestStringify('321', tojson_via_getter);
// Test toJSON with key. // Test toJSON with key.
tojson_obj = { toJSON: function(key) { return key + key; } }; tojson_obj = { toJSON: function(key) { return key + key; } };
var tojson_with_key_1 = { a: tojson_obj, b: tojson_obj }; var tojson_with_key_1 = { a: tojson_obj, b: tojson_obj };
assertEquals('{"a":"aa","b":"bb"}', JSON.stringify(tojson_with_key_1)); TestStringify('{"a":"aa","b":"bb"}', tojson_with_key_1);
var tojson_with_key_2 = [ tojson_obj, tojson_obj ]; var tojson_with_key_2 = [ tojson_obj, tojson_obj ];
assertEquals('["00","11"]', JSON.stringify(tojson_with_key_2)); TestStringify('["00","11"]', tojson_with_key_2);
// Test toJSON with exception. // Test toJSON with exception.
var tojson_ex = { toJSON: function(key) { throw "123" } }; var tojson_ex = { toJSON: function(key) { throw "123" } };
assertThrows(function() { JSON.stringify(tojson_ex); }); assertThrows(function() { JSON.stringify(tojson_ex); });
assertThrows(function() { JSON.stringify(tojson_ex, null, 0); });
// Test toJSON with access to this. // Test toJSON with access to this.
var obj = { toJSON: function(key) { return this.a + key; }, a: "x" }; var obj = { toJSON: function(key) { return this.a + key; }, a: "x" };
assertEquals('{"y":"xy"}', JSON.stringify({y: obj})); TestStringify('{"y":"xy"}', {y: obj});
// Test holes in arrays. // Test holes in arrays.
var fast_smi = [1, 2, 3, 4]; var fast_smi = [1, 2, 3, 4];
fast_smi.__proto__ = [7, 7, 7, 7]; fast_smi.__proto__ = [7, 7, 7, 7];
delete fast_smi[2]; delete fast_smi[2];
assertTrue(%HasFastSmiElements(fast_smi)); assertTrue(%HasFastSmiElements(fast_smi));
assertEquals("[1,2,7,4]", JSON.stringify(fast_smi)); TestStringify("[1,2,7,4]", fast_smi);
var fast_double = [1.1, 2, 3, 4]; var fast_double = [1.1, 2, 3, 4];
fast_double.__proto__ = [7, 7, 7, 7]; fast_double.__proto__ = [7, 7, 7, 7];
delete fast_double[2]; delete fast_double[2];
assertTrue(%HasFastDoubleElements(fast_double)); assertTrue(%HasFastDoubleElements(fast_double));
assertEquals("[1.1,2,7,4]", JSON.stringify(fast_double)); TestStringify("[1.1,2,7,4]", fast_double);
var fast_obj = [1, 2, {}, {}]; var fast_obj = [1, 2, {}, {}];
fast_obj.__proto__ = [7, 7, 7, 7]; fast_obj.__proto__ = [7, 7, 7, 7];
delete fast_obj[2]; delete fast_obj[2];
assertTrue(%HasFastObjectElements(fast_obj)); assertTrue(%HasFastObjectElements(fast_obj));
assertEquals("[1,2,7,{}]", JSON.stringify(fast_obj)); TestStringify("[1,2,7,{}]", fast_obj);
var getter_side_effect = { a: 1, var getter_side_effect = { a: 1,
get b() { get b() {
...@@ -146,8 +153,22 @@ var getter_side_effect = { a: 1, ...@@ -146,8 +153,22 @@ var getter_side_effect = { a: 1,
assertEquals('{"a":1,"b":2,"d":4}', JSON.stringify(getter_side_effect)); assertEquals('{"a":1,"b":2,"d":4}', JSON.stringify(getter_side_effect));
assertEquals('{"b":2,"d":4,"e":5}', JSON.stringify(getter_side_effect)); assertEquals('{"b":2,"d":4,"e":5}', JSON.stringify(getter_side_effect));
getter_side_effect = { a: 1,
get b() {
delete this.a;
delete this.c;
this.e = 5;
return 2;
},
c: 3,
d: 4 };
assertEquals('{"a":1,"b":2,"d":4}',
JSON.stringify(getter_side_effect, null, 0));
assertEquals('{"b":2,"d":4,"e":5}',
JSON.stringify(getter_side_effect, null, 0));
var non_enum = {}; var non_enum = {};
non_enum.a = 1; non_enum.a = 1;
Object.defineProperty(non_enum, "b", { value: 2, enumerable: false }); Object.defineProperty(non_enum, "b", { value: 2, enumerable: false });
non_enum.c = 3; non_enum.c = 3;
assertEquals('{"a":1,"c":3}', JSON.stringify(non_enum)); TestStringify('{"a":1,"c":3}', non_enum);
...@@ -31,3 +31,4 @@ var obj = JSON.parse(msg); ...@@ -31,3 +31,4 @@ var obj = JSON.parse(msg);
var obj2 = JSON.parse(msg); var obj2 = JSON.parse(msg);
assertEquals(JSON.stringify(obj), JSON.stringify(obj2)); assertEquals(JSON.stringify(obj), JSON.stringify(obj2));
assertEquals(JSON.stringify(obj, null, 0), JSON.stringify(obj2));
\ No newline at end of file
...@@ -29,3 +29,4 @@ var o = ["\u56e7", // Switch JSON stringifier to two-byte mode. ...@@ -29,3 +29,4 @@ var o = ["\u56e7", // Switch JSON stringifier to two-byte mode.
"\u00e6"]; // Latin-1 character. "\u00e6"]; // Latin-1 character.
assertEquals('["\u56e7","\u00e6"]', JSON.stringify(o)); assertEquals('["\u56e7","\u00e6"]', JSON.stringify(o));
assertEquals('["\u56e7","\u00e6"]', JSON.stringify(o, null, 0));
\ No newline at end of file
...@@ -32,5 +32,5 @@ ...@@ -32,5 +32,5 @@
// See: http://code.google.com/p/v8/issues/detail?id=753 // See: http://code.google.com/p/v8/issues/detail?id=753
var obj = {a1: {b1: [1,2,3,4], b2: {c1: 1, c2: 2}},a2: 'a2'}; var obj = {a1: {b1: [1,2,3,4], b2: {c1: 1, c2: 2}},a2: 'a2'};
assertEquals(JSON.stringify(obj,null, 5.99999), JSON.stringify(obj,null, 5)); assertEquals(JSON.stringify(obj, null, 5.99999), JSON.stringify(obj, null, 5));
...@@ -29,6 +29,7 @@ assertEquals(String.fromCharCode(97, 220, 256), 'a' + '\u00DC' + '\u0100'); ...@@ -29,6 +29,7 @@ assertEquals(String.fromCharCode(97, 220, 256), 'a' + '\u00DC' + '\u0100');
assertEquals(String.fromCharCode(97, 220, 256), 'a\u00DC\u0100'); assertEquals(String.fromCharCode(97, 220, 256), 'a\u00DC\u0100');
assertEquals(0x80, JSON.stringify("\x80").charCodeAt(1)); assertEquals(0x80, JSON.stringify("\x80").charCodeAt(1));
assertEquals(0x80, JSON.stringify("\x80", 0, null).charCodeAt(1));
assertEquals(['a', 'b', '\xdc'], ['b', '\xdc', 'a'].sort()); assertEquals(['a', 'b', '\xdc'], ['b', '\xdc', 'a'].sort());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment