1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 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. 27 28 // Flags: --allow-natives-syntax --expose-externalize-string 29 30 // Test JSON.stringify on the global object. 31 var a = 12345; 32 assertTrue(JSON.stringify(this).indexOf('"a":12345') > 0); 33 assertTrue(JSON.stringify(this, null, 0).indexOf('"a":12345') > 0); 34 35 // Test JSON.stringify of array in dictionary mode. 36 function TestStringify(expected, input) { 37 assertEquals(expected, JSON.stringify(input)); 38 assertEquals(expected, JSON.stringify(input, null, 0)); 39 } 40 41 var array_1 = []; 42 var array_2 = []; 43 array_1[1<<17] = 1; 44 array_2[1<<17] = function() { return 1; }; 45 var nulls = "null,"; 46 for (var i = 0; i < 17; i++) { 47 nulls += nulls; 48 } 49 50 expected_1 = '[' + nulls + '1]'; 51 expected_2 = '[' + nulls + 'null]'; 52 TestStringify(expected_1, array_1); 53 TestStringify(expected_2, array_2); 54 55 // Test JSValue with custom prototype. 56 var num_wrapper = Object(42); 57 num_wrapper.__proto__ = { __proto__: null, 58 toString: function() { return true; } }; 59 TestStringify('1', num_wrapper); 60 61 var str_wrapper = Object('2'); 62 str_wrapper.__proto__ = { __proto__: null, 63 toString: function() { return true; } }; 64 TestStringify('"true"', str_wrapper); 65 66 var bool_wrapper = Object(false); 67 bool_wrapper.__proto__ = { __proto__: null, 68 toString: function() { return true; } }; 69 // Note that toString function is not evaluated here! 70 TestStringify('false', bool_wrapper); 71 72 // Test getters. 73 var counter = 0; 74 var getter_obj = { get getter() { 75 counter++; 76 return 123; 77 } }; 78 TestStringify('{"getter":123}', getter_obj); 79 assertEquals(2, counter); 80 81 // Test toJSON function. 82 var tojson_obj = { toJSON: function() { 83 counter++; 84 return [1, 2]; 85 }, 86 a: 1}; 87 TestStringify('[1,2]', tojson_obj); 88 assertEquals(4, counter); 89 90 // Test that we don't recursively look for the toJSON function. 91 var tojson_proto_obj = { a: 'fail' }; 92 tojson_proto_obj.__proto__ = { toJSON: function() { 93 counter++; 94 return tojson_obj; 95 } }; 96 TestStringify('{"a":1}', tojson_proto_obj); 97 98 // Test toJSON produced by a getter. 99 var tojson_via_getter = { get toJSON() { 100 return function(x) { 101 counter++; 102 return 321; 103 }; 104 }, 105 a: 1 }; 106 TestStringify('321', tojson_via_getter); 107 108 assertThrows(function() { 109 JSON.stringify({ get toJSON() { throw "error"; } }); 110 }); 111 112 // Test toJSON with key. 113 tojson_obj = { toJSON: function(key) { return key + key; } }; 114 var tojson_with_key_1 = { a: tojson_obj, b: tojson_obj }; 115 TestStringify('{"a":"aa","b":"bb"}', tojson_with_key_1); 116 var tojson_with_key_2 = [ tojson_obj, tojson_obj ]; 117 TestStringify('["00","11"]', tojson_with_key_2); 118 119 // Test toJSON with exception. 120 var tojson_ex = { toJSON: function(key) { throw "123" } }; 121 assertThrows(function() { JSON.stringify(tojson_ex); }); 122 assertThrows(function() { JSON.stringify(tojson_ex, null, 0); }); 123 124 // Test toJSON with access to this. 125 var obj = { toJSON: function(key) { return this.a + key; }, a: "x" }; 126 TestStringify('{"y":"xy"}', {y: obj}); 127 128 // Test holes in arrays. 129 var fast_smi = [1, 2, 3, 4]; 130 fast_smi.__proto__ = [7, 7, 7, 7]; 131 delete fast_smi[2]; 132 assertTrue(%HasFastSmiElements(fast_smi)); 133 TestStringify("[1,2,7,4]", fast_smi); 134 135 var fast_double = [1.1, 2, 3, 4]; 136 fast_double.__proto__ = [7, 7, 7, 7]; 137 138 delete fast_double[2]; 139 assertTrue(%HasFastDoubleElements(fast_double)); 140 TestStringify("[1.1,2,7,4]", fast_double); 141 142 var fast_obj = [1, 2, {}, {}]; 143 fast_obj.__proto__ = [7, 7, 7, 7]; 144 145 delete fast_obj[2]; 146 assertTrue(%HasFastObjectElements(fast_obj)); 147 TestStringify("[1,2,7,{}]", fast_obj); 148 149 var getter_side_effect = { a: 1, 150 get b() { 151 delete this.a; 152 delete this.c; 153 this.e = 5; 154 return 2; 155 }, 156 c: 3, 157 d: 4 }; 158 assertEquals('{"a":1,"b":2,"d":4}', JSON.stringify(getter_side_effect)); 159 assertEquals('{"b":2,"d":4,"e":5}', JSON.stringify(getter_side_effect)); 160 161 getter_side_effect = { a: 1, 162 get b() { 163 delete this.a; 164 delete this.c; 165 this.e = 5; 166 return 2; 167 }, 168 c: 3, 169 d: 4 }; 170 assertEquals('{"a":1,"b":2,"d":4}', 171 JSON.stringify(getter_side_effect, null, 0)); 172 assertEquals('{"b":2,"d":4,"e":5}', 173 JSON.stringify(getter_side_effect, null, 0)); 174 175 var non_enum = {}; 176 non_enum.a = 1; 177 Object.defineProperty(non_enum, "b", { value: 2, enumerable: false }); 178 non_enum.c = 3; 179 TestStringify('{"a":1,"c":3}', non_enum); 180 181 var str = "external"; 182 try { 183 externalizeString(str, true); 184 } catch (e) { } 185 TestStringify("\"external\"", str, null, 0); 186