From b522319a9813735b8e9110efc8c7e85b41a5e6fe Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Wed, 20 Mar 2013 14:07:30 +0000 Subject: [PATCH] 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 --- test/mjsunit/json.js | 117 +++++++++++++++++--------------- test/mjsunit/json2.js | 57 +++++++++++----- test/mjsunit/regress/regress-2374.js | 1 + test/mjsunit/regress/regress-2570.js | 1 + test/mjsunit/regress/regress-753.js | 2 +- test/mjsunit/regress/regress-latin-1.js | 1 + 6 files changed, 106 insertions(+), 73 deletions(-) diff --git a/test/mjsunit/json.js b/test/mjsunit/json.js index 79826db..c72c153 100644 --- a/test/mjsunit/json.js +++ b/test/mjsunit/json.js @@ -225,23 +225,28 @@ TestInvalid('"Garbage""After string"'); // Stringify -assertEquals("true", JSON.stringify(true)); -assertEquals("false", JSON.stringify(false)); -assertEquals("null", JSON.stringify(null)); -assertEquals("false", JSON.stringify({toJSON: function () { return false; }})); -assertEquals("4", JSON.stringify(4)); -assertEquals('"foo"', JSON.stringify("foo")); -assertEquals("null", JSON.stringify(Infinity)); -assertEquals("null", JSON.stringify(-Infinity)); -assertEquals("null", JSON.stringify(NaN)); -assertEquals("4", JSON.stringify(new Number(4))); -assertEquals('"bar"', JSON.stringify(new String("bar"))); - -assertEquals('"foo\\u0000bar"', JSON.stringify("foo\0bar")); -assertEquals('"f\\"o\'o\\\\b\\ba\\fr\\nb\\ra\\tz"', - JSON.stringify("f\"o\'o\\b\ba\fr\nb\ra\tz")); - -assertEquals("[1,2,3]", JSON.stringify([1, 2, 3])); +function TestStringify(expected, input) { + assertEquals(expected, JSON.stringify(input)); + assertEquals(expected, JSON.stringify(input, null, 0)); +} + +TestStringify("true", true); +TestStringify("false", false); +TestStringify("null", null); +TestStringify("false", {toJSON: function () { return false; }}); +TestStringify("4", 4); +TestStringify('"foo"', "foo"); +TestStringify("null", Infinity); +TestStringify("null", -Infinity); +TestStringify("null", NaN); +TestStringify("4", new Number(4)); +TestStringify('"bar"', new String("bar")); + +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, 2)); assertEquals("[\n 1,\n 2,\n 3\n]", @@ -256,33 +261,38 @@ assertEquals("[1,2,[3,[4],5],6,7]", JSON.stringify([1, 2, [3, [4], 5], 6, 7], null)); assertEquals("[2,4,[6,[8],10],12,14]", JSON.stringify([1, 2, [3, [4], 5], 6, 7], DoubleNumbers)); -assertEquals('["a","ab","abc"]', JSON.stringify(["a","ab","abc"])); -assertEquals('{"a":1,"c":true}', - JSON.stringify({ a : 1, - b : function() { 1 }, - c : true, - d : function() { 2 } })); -assertEquals('[1,null,true,null]', - JSON.stringify([1, function() { 1 }, true, function() { 2 }])); -assertEquals('"toJSON 123"', - JSON.stringify({ toJSON : function() { return 'toJSON 123'; } })); -assertEquals('{"a":321}', - JSON.stringify({ a : { toJSON : function() { return 321; } } })); +TestStringify('["a","ab","abc"]', ["a","ab","abc"]); +TestStringify('{"a":1,"c":true}', { a : 1, + b : function() { 1 }, + c : true, + d : function() { 2 } }); +TestStringify('[1,null,true,null]', + [1, function() { 1 }, true, function() { 2 }]); +TestStringify('"toJSON 123"', + { toJSON : function() { return 'toJSON 123'; } }); +TestStringify('{"a":321}', + { a : { toJSON : function() { return 321; } } }); var counter = 0; assertEquals('{"getter":123}', JSON.stringify({ get getter() { counter++; return 123; } })); assertEquals(1, counter); -assertEquals('{"a":"abc","b":"\u1234bc"}', - JSON.stringify({ a : "abc", b : "\u1234bc" })); +assertEquals('{"getter":123}', + 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 }; delete a.a; -assertEquals('{"b":2}', JSON.stringify(a)); +TestStringify('{"b":2}', a); var b = {}; b.__proto__ = { toJSON : function() { return 321;} }; -assertEquals("321", JSON.stringify(b)); +TestStringify("321", b); var array = [""]; var expected = '""'; @@ -291,18 +301,19 @@ for (var i = 0; i < 10000; i++) { expected = '"",' + expected; } expected = '[' + expected + ']'; -assertEquals(expected, JSON.stringify(array)); +TestStringify(expected, array); var circular = [1, 2, 3]; circular[2] = circular; assertThrows(function () { JSON.stringify(circular); }, TypeError); +assertThrows(function () { JSON.stringify(circular, null, 0); }, TypeError); var 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('{\n "a": "b",\n "c": "d"\n}', 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'])); var checker = {}; var array = [checker]; 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. assertEquals('{\n "a": "b",\n "c": "d"\n}', @@ -329,12 +340,11 @@ assertEquals('{"x":"42"}', JSON.stringify({x: String}, newx)); assertEquals('{"x":42}', JSON.stringify({x: Number}, newx)); assertEquals('{"x":true}', JSON.stringify({x: Boolean}, newx)); -assertEquals(undefined, JSON.stringify(undefined)); -assertEquals(undefined, JSON.stringify(function () { })); +TestStringify(undefined, undefined); +TestStringify(undefined, function () { }); // Arrays with missing, undefined or function elements have those elements // replaced by null. -assertEquals("[null,null,null]", - JSON.stringify([undefined,,function(){}])); +TestStringify("[null,null,null]", [undefined,,function(){}]); // Objects with undefined or function properties (including replaced properties) // have those properties ignored. @@ -415,16 +425,15 @@ var re = /Is callable/; var reJSON = /Is callable/; reJSON.toJSON = function() { return "has toJSON"; }; -assertEquals( - '[37,null,1,"foo","37","true",null,"has toJSON",{},"has toJSON"]', - JSON.stringify([num37, numFoo, numTrue, - strFoo, str37, strTrue, - func, funcJSON, re, reJSON])); +TestStringify('[37,null,1,"foo","37","true",null,"has toJSON",{},"has toJSON"]', + [num37, numFoo, numTrue, + strFoo, str37, strTrue, + func, funcJSON, re, reJSON]); var oddball = Object(42); oddball.__proto__ = { __proto__: null, toString: function() { return true; } }; -assertEquals('1', JSON.stringify(oddball)); +TestStringify('1', oddball); var getCount = 0; var callCount = 0; @@ -433,10 +442,10 @@ var counter = { get toJSON() { getCount++; return 42; }; } }; // RegExps are not callable, so they are stringified as objects. -assertEquals('{}', JSON.stringify(/regexp/)); -assertEquals('42', JSON.stringify(counter)); -assertEquals(1, getCount); -assertEquals(1, callCount); +TestStringify('{}', /regexp/); +TestStringify('42', counter); +assertEquals(2, getCount); +assertEquals(2, callCount); var oddball2 = Object(42); var oddball3 = Object("foo"); @@ -445,13 +454,13 @@ oddball3.__proto__ = { __proto__: null, valueOf: function() { return true; } }; oddball2.__proto__ = { __proto__: null, toJSON: function () { return oddball3; } } -assertEquals('"true"', JSON.stringify(oddball2)); +TestStringify('"true"', oddball2); var falseNum = Object("37"); falseNum.__proto__ = Number.prototype; falseNum.toString = function() { return 42; }; -assertEquals('"42"', JSON.stringify(falseNum)); +TestStringify('"42"', falseNum); // Parse an object value as __proto__. var o1 = JSON.parse('{"__proto__":[]}'); @@ -472,4 +481,4 @@ assertTrue(o2.hasOwnProperty("__proto__")); assertTrue(Object.prototype.isPrototypeOf(o2)); var json = '{"stuff before slash\\\\stuff after slash":"whatever"}'; -assertEquals(json, JSON.stringify(JSON.parse(json))); +TestStringify(json, JSON.parse(json)); diff --git a/test/mjsunit/json2.js b/test/mjsunit/json2.js index 4c0b8f5..6a72051 100644 --- a/test/mjsunit/json2.js +++ b/test/mjsunit/json2.js @@ -30,8 +30,14 @@ // Test JSON.stringify on the global object. var a = 12345; 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. +function TestStringify(expected, input) { + assertEquals(expected, JSON.stringify(input)); + assertEquals(expected, JSON.stringify(input, null, 0)); +} + var array_1 = []; var array_2 = []; array_1[100000] = 1; @@ -42,25 +48,25 @@ for (var i = 0; i < 100000; i++) { } expected_1 = '[' + nulls + '1]'; expected_2 = '[' + nulls + 'null]'; -assertEquals(expected_1, JSON.stringify(array_1)); -assertEquals(expected_2, JSON.stringify(array_2)); +TestStringify(expected_1, array_1); +TestStringify(expected_2, array_2); // Test JSValue with custom prototype. var num_wrapper = Object(42); num_wrapper.__proto__ = { __proto__: null, toString: function() { return true; } }; -assertEquals('1', JSON.stringify(num_wrapper)); +TestStringify('1', num_wrapper); var str_wrapper = Object('2'); str_wrapper.__proto__ = { __proto__: null, toString: function() { return true; } }; -assertEquals('"true"', JSON.stringify(str_wrapper)); +TestStringify('"true"', str_wrapper); var bool_wrapper = Object(false); bool_wrapper.__proto__ = { __proto__: null, toString: function() { return true; } }; // Note that toString function is not evaluated here! -assertEquals('false', JSON.stringify(bool_wrapper)); +TestStringify('false', bool_wrapper); // Test getters. var counter = 0; @@ -68,8 +74,8 @@ var getter_obj = { get getter() { counter++; return 123; } }; -assertEquals('{"getter":123}', JSON.stringify(getter_obj)); -assertEquals(1, counter); +TestStringify('{"getter":123}', getter_obj); +assertEquals(2, counter); // Test toJSON function. var tojson_obj = { toJSON: function() { @@ -77,8 +83,8 @@ var tojson_obj = { toJSON: function() { return [1, 2]; }, a: 1}; -assertEquals('[1,2]', JSON.stringify(tojson_obj)); -assertEquals(2, counter); +TestStringify('[1,2]', tojson_obj); +assertEquals(4, counter); // Test that we don't recursively look for the toJSON function. var tojson_proto_obj = { a: 'fail' }; @@ -86,7 +92,7 @@ tojson_proto_obj.__proto__ = { toJSON: function() { counter++; return tojson_obj; } }; -assertEquals('{"a":1}', JSON.stringify(tojson_proto_obj)); +TestStringify('{"a":1}', tojson_proto_obj); // Test toJSON produced by a getter. var tojson_via_getter = { get toJSON() { @@ -96,43 +102,44 @@ var tojson_via_getter = { get toJSON() { }; }, a: 1 }; -assertEquals('321', JSON.stringify(tojson_via_getter)); +TestStringify('321', tojson_via_getter); // Test toJSON with key. tojson_obj = { toJSON: function(key) { return key + key; } }; 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 ]; -assertEquals('["00","11"]', JSON.stringify(tojson_with_key_2)); +TestStringify('["00","11"]', tojson_with_key_2); // Test toJSON with exception. var tojson_ex = { toJSON: function(key) { throw "123" } }; assertThrows(function() { JSON.stringify(tojson_ex); }); +assertThrows(function() { JSON.stringify(tojson_ex, null, 0); }); // Test toJSON with access to this. 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. var fast_smi = [1, 2, 3, 4]; fast_smi.__proto__ = [7, 7, 7, 7]; delete fast_smi[2]; 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]; fast_double.__proto__ = [7, 7, 7, 7]; delete fast_double[2]; 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, {}, {}]; fast_obj.__proto__ = [7, 7, 7, 7]; delete fast_obj[2]; assertTrue(%HasFastObjectElements(fast_obj)); -assertEquals("[1,2,7,{}]", JSON.stringify(fast_obj)); +TestStringify("[1,2,7,{}]", fast_obj); var getter_side_effect = { a: 1, get b() { @@ -146,8 +153,22 @@ var getter_side_effect = { a: 1, assertEquals('{"a":1,"b":2,"d":4}', 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 = {}; non_enum.a = 1; Object.defineProperty(non_enum, "b", { value: 2, enumerable: false }); non_enum.c = 3; -assertEquals('{"a":1,"c":3}', JSON.stringify(non_enum)); +TestStringify('{"a":1,"c":3}', non_enum); diff --git a/test/mjsunit/regress/regress-2374.js b/test/mjsunit/regress/regress-2374.js index b12e5f2..b333720 100644 --- a/test/mjsunit/regress/regress-2374.js +++ b/test/mjsunit/regress/regress-2374.js @@ -31,3 +31,4 @@ var obj = JSON.parse(msg); var obj2 = JSON.parse(msg); assertEquals(JSON.stringify(obj), JSON.stringify(obj2)); +assertEquals(JSON.stringify(obj, null, 0), JSON.stringify(obj2)); \ No newline at end of file diff --git a/test/mjsunit/regress/regress-2570.js b/test/mjsunit/regress/regress-2570.js index f9060e8..4e32a21 100644 --- a/test/mjsunit/regress/regress-2570.js +++ b/test/mjsunit/regress/regress-2570.js @@ -29,3 +29,4 @@ var o = ["\u56e7", // Switch JSON stringifier to two-byte mode. "\u00e6"]; // Latin-1 character. assertEquals('["\u56e7","\u00e6"]', JSON.stringify(o)); +assertEquals('["\u56e7","\u00e6"]', JSON.stringify(o, null, 0)); \ No newline at end of file diff --git a/test/mjsunit/regress/regress-753.js b/test/mjsunit/regress/regress-753.js index 6a6d87b..4621de6 100644 --- a/test/mjsunit/regress/regress-753.js +++ b/test/mjsunit/regress/regress-753.js @@ -32,5 +32,5 @@ // 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'}; -assertEquals(JSON.stringify(obj,null, 5.99999), JSON.stringify(obj,null, 5)); +assertEquals(JSON.stringify(obj, null, 5.99999), JSON.stringify(obj, null, 5)); diff --git a/test/mjsunit/regress/regress-latin-1.js b/test/mjsunit/regress/regress-latin-1.js index a988ebd..e7f3136 100644 --- a/test/mjsunit/regress/regress-latin-1.js +++ b/test/mjsunit/regress/regress-latin-1.js @@ -29,6 +29,7 @@ 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", 0, null).charCodeAt(1)); assertEquals(['a', 'b', '\xdc'], ['b', '\xdc', 'a'].sort()); -- 2.7.4