1// Copyright 2014 the V8 project authors. All rights reserved. 2// Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions 6// are met: 7// 1. Redistributions of source code must retain the above copyright 8// notice, this list of conditions and the following disclaimer. 9// 2. Redistributions in binary form must reproduce the above copyright 10// notice, this list of conditions and the following disclaimer in the 11// documentation and/or other materials provided with the distribution. 12// 13// THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16// DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 24 25function createTests() { 26 var simpleArray = ['a', 'b', 'c']; 27 var simpleObject = {a:"1", b:"2", c:"3"}; 28 var complexArray = ['a', 'b', 'c',,,simpleObject, simpleArray, [simpleObject,simpleArray]]; 29 var complexObject = {a:"1", b:"2", c:"3", d:undefined, e:null, "":12, get f(){ return simpleArray; }, array: complexArray}; 30 var simpleArrayWithProto = ['d', 'e', 'f']; 31 simpleArrayWithProto.__proto__ = simpleObject; 32 var simpleObjectWithProto = {d:"4", e:"5", f:"6", __proto__:simpleObject}; 33 var complexArrayWithProto = ['d', 'e', 'f',,,simpleObjectWithProto, simpleArrayWithProto, [simpleObjectWithProto,simpleArrayWithProto]]; 34 complexArrayWithProto.__proto__ = simpleObjectWithProto; 35 var complexObjectWithProto = {d:"4", e:"5", f:"6", g:undefined, h:null, "":12, get i(){ return simpleArrayWithProto; }, array2: complexArrayWithProto, __proto__:complexObject}; 36 var objectWithSideEffectGetter = {get b() {this.foo=1;}}; 37 var objectWithSideEffectGetterAndProto = {__proto__:{foo:"bar"}, get b() {this.foo=1;}}; 38 var arrayWithSideEffectGetter = []; 39 arrayWithSideEffectGetter.__defineGetter__("b", function(){this.foo=1;}); 40 var arrayWithSideEffectGetterAndProto = []; 41 arrayWithSideEffectGetterAndProto.__defineGetter__("b", function(){this.foo=1;}); 42 arrayWithSideEffectGetterAndProto.__proto__ = {foo:"bar"}; 43 var result = []; 44 result.push(function(jsonObject){ 45 return jsonObject.stringify(1); 46 }); 47 result.push(function(jsonObject){ 48 return jsonObject.stringify(1.5); 49 }); 50 result.push(function(jsonObject){ 51 return jsonObject.stringify(-1); 52 }); 53 result.push(function(jsonObject){ 54 return jsonObject.stringify(-1.5); 55 }); 56 result.push(function(jsonObject){ 57 return jsonObject.stringify(null); 58 }); 59 result.push(function(jsonObject){ 60 return jsonObject.stringify("string"); 61 }); 62 result.push(function(jsonObject){ 63 return jsonObject.stringify(new Number(0)); 64 }); 65 result.push(function(jsonObject){ 66 return jsonObject.stringify(new Number(1)); 67 }); 68 result.push(function(jsonObject){ 69 return jsonObject.stringify(new Number(1.5)); 70 }); 71 result.push(function(jsonObject){ 72 return jsonObject.stringify(new Number(-1)); 73 }); 74 result.push(function(jsonObject){ 75 return jsonObject.stringify(new Number(-1.5)); 76 }); 77 result.push(function(jsonObject){ 78 return jsonObject.stringify(new String("a string object")); 79 }); 80 result.push(function(jsonObject){ 81 return jsonObject.stringify(new Boolean(true)); 82 }); 83 result.push(function(jsonObject){ 84 var value = new Number(1); 85 value.valueOf = function() { return 2; } 86 return jsonObject.stringify(value); 87 }); 88 result[result.length - 1].expected = '2'; 89 result.push(function(jsonObject){ 90 var value = new Boolean(true); 91 value.valueOf = function() { return 2; } 92 return jsonObject.stringify(value); 93 }); 94 result[result.length - 1].expected = '2'; 95 result.push(function(jsonObject){ 96 var value = new String("fail"); 97 value.toString = function() { return "converted string"; } 98 return jsonObject.stringify(value); 99 }); 100 result[result.length - 1].expected = '"converted string"'; 101 result.push(function(jsonObject){ 102 return jsonObject.stringify(true); 103 }); 104 result.push(function(jsonObject){ 105 return jsonObject.stringify(false); 106 }); 107 result.push(function(jsonObject){ 108 return jsonObject.stringify(new Date(0)); 109 }); 110 result.push(function(jsonObject){ 111 return jsonObject.stringify({toJSON: Date.prototype.toJSON}); 112 }); 113 result[result.length - 1].throws = true; 114 result.push(function(jsonObject){ 115 return jsonObject.stringify({toJSON: Date.prototype.toJSON, toISOString: function(){ return "custom toISOString"; }}); 116 }); 117 result.push(function(jsonObject){ 118 return jsonObject.stringify({toJSON: Date.prototype.toJSON, toISOString: function(){ return {}; }}); 119 }); 120 result[result.length - 1].throws = true; 121 result.push(function(jsonObject){ 122 return jsonObject.stringify({toJSON: Date.prototype.toJSON, toISOString: function(){ throw "An exception"; }}); 123 }); 124 result[result.length - 1].throws = true; 125 result.push(function(jsonObject){ 126 var d = new Date(0); 127 d.toISOString = null; 128 return jsonObject.stringify(d); 129 }); 130 result[result.length - 1].throws = true; 131 result.push(function(jsonObject){ 132 var d = new Date(0); 133 d.toJSON = undefined; 134 return jsonObject.stringify(d); 135 }); 136 result.push(function(jsonObject){ 137 return jsonObject.stringify({get Foo() { return "bar"; }}); 138 }); 139 result.push(function(jsonObject){ 140 return jsonObject.stringify({get Foo() { this.foo="wibble"; return "bar"; }}); 141 }); 142 result.push(function(jsonObject){ 143 var count = 0; 144 jsonObject.stringify({get Foo() { count++; return "bar"; }}); 145 return count; 146 }); 147 result.push(function(jsonObject){ 148 var count = 0; 149 return jsonObject.stringify({get Foo() { count++; delete this.bar; return "bar"; }, bar: "wibble"}); 150 }); 151 result.push(function(jsonObject){ 152 var count = 0; 153 return jsonObject.stringify({a:"1", b:"2", c:"3", 5:4, 4:5, 2:6, 1:7}); 154 }); 155 result.push(function(jsonObject){ 156 var allString = true; 157 jsonObject.stringify({a:"1", b:"2", c:"3", 5:4, 4:5, 2:6, 1:7}, function(k,v){allString = allString && (typeof k == "string"); return v}); 158 return allString; 159 }); 160 result.push(function(jsonObject){ 161 var allString = true; 162 jsonObject.stringify([1,2,3,4,5], function(k,v){allString = allString && (typeof k == "string"); return v}); 163 return allString; 164 }); 165 result.push(function(jsonObject){ 166 var allString = true; 167 var array = []; 168 return jsonObject.stringify({a:"1", b:"2", c:"3", 5:4, 4:5, 2:6, 1:7}, array); 169 }); 170 result.push(function(jsonObject){ 171 var allString = true; 172 var array = ["a"]; 173 return jsonObject.stringify({get a(){return 1;array[1]="b";array[2]="c"}, b:"2", c:"3"}, array); 174 }); 175 result.push(function(jsonObject){ 176 var allString = true; 177 var array = [{toString:function(){array[0]='a'; array[1]='c'; array[2]='b'; return 'a'}}]; 178 return jsonObject.stringify(simpleObject, array); 179 }); 180 result.push(function(jsonObject){ 181 var allString = true; 182 var array = [{toString:function(){array[0]='a'; array[1]='c'; array[2]='b'; return 'a'}}]; 183 return jsonObject.stringify(simpleObjectWithProto, array); 184 }); 185 result.push(function(jsonObject){ 186 var allString = true; 187 var array = [1, new Number(2), NaN, Infinity, -Infinity, new String("str")]; 188 return jsonObject.stringify({"1":"1","2":"2","NaN":"NaN","Infinity":"Infinity","-Infinity":"-Infinity","str":"str"}, array); 189 }); 190 result[result.length - 1].expected = '{"1":"1","2":"2","NaN":"NaN","Infinity":"Infinity","-Infinity":"-Infinity","str":"str"}'; 191 result.push(function(jsonObject){ 192 var allString = true; 193 var array = ["1","2","3"]; 194 return jsonObject.stringify({1:'a', 2:'b', 3:'c'}, array); 195 }); 196 result.push(function(jsonObject){ 197 var allString = true; 198 var array = ["1","2","3"]; 199 return jsonObject.stringify(simpleArray, array); 200 }); 201 result.push(function(jsonObject){ 202 return jsonObject.stringify(simpleArray, null, " "); 203 }); 204 result.push(function(jsonObject){ 205 return jsonObject.stringify(simpleArray, null, 4); 206 }); 207 result.push(function(jsonObject){ 208 return jsonObject.stringify(simpleArray, null, "ab"); 209 }); 210 result.push(function(jsonObject){ 211 return jsonObject.stringify(simpleArray, null, 4); 212 }); 213 result.push(function(jsonObject){ 214 return jsonObject.stringify(simpleObject, null, " "); 215 }); 216 result.push(function(jsonObject){ 217 return jsonObject.stringify(simpleObject, null, 4); 218 }); 219 result.push(function(jsonObject){ 220 return jsonObject.stringify(simpleObject, null, "ab"); 221 }); 222 result.push(function(jsonObject){ 223 return jsonObject.stringify(simpleObject, null, 4); 224 }); 225 result.push(function(jsonObject){ 226 return jsonObject.stringify(simpleObject, null, 10); 227 }); 228 result.push(function(jsonObject){ 229 return jsonObject.stringify(simpleObject, null, 11); 230 }); 231 result[result.length - 1].expected = JSON.stringify(simpleObject, null, 10); 232 result.push(function(jsonObject){ 233 return jsonObject.stringify(simpleObject, null, " "); 234 }); 235 result[result.length - 1].expected = JSON.stringify(simpleObject, null, 10); 236 result.push(function(jsonObject){ 237 return jsonObject.stringify(simpleObject, null, " "); 238 }); 239 result[result.length - 1].expected = JSON.stringify(simpleObject, null, 10); 240 result.push(function(jsonObject){ 241 return jsonObject.stringify(complexArray, null, " "); 242 }); 243 result.push(function(jsonObject){ 244 return jsonObject.stringify(complexArray, null, 4); 245 }); 246 result.push(function(jsonObject){ 247 return jsonObject.stringify(complexArray, null, "ab"); 248 }); 249 result.push(function(jsonObject){ 250 return jsonObject.stringify(complexArray, null, 4); 251 }); 252 result.push(function(jsonObject){ 253 return jsonObject.stringify(complexObject, null, " "); 254 }); 255 result.push(function(jsonObject){ 256 return jsonObject.stringify(complexObject, null, 4); 257 }); 258 result.push(function(jsonObject){ 259 return jsonObject.stringify(complexObject, null, "ab"); 260 }); 261 result.push(function(jsonObject){ 262 return jsonObject.stringify(complexObject, null, 4); 263 }); 264 result.push(function(jsonObject){ 265 var allString = true; 266 var array = ["1","2","3"]; 267 return jsonObject.stringify(simpleArrayWithProto, array); 268 }); 269 result.push(function(jsonObject){ 270 return jsonObject.stringify(simpleArrayWithProto, null, " "); 271 }); 272 result.push(function(jsonObject){ 273 return jsonObject.stringify(simpleArrayWithProto, null, 4); 274 }); 275 result.push(function(jsonObject){ 276 return jsonObject.stringify(simpleArrayWithProto, null, "ab"); 277 }); 278 result.push(function(jsonObject){ 279 return jsonObject.stringify(simpleArrayWithProto, null, 4); 280 }); 281 result.push(function(jsonObject){ 282 return jsonObject.stringify(simpleObjectWithProto, null, " "); 283 }); 284 result.push(function(jsonObject){ 285 return jsonObject.stringify(simpleObjectWithProto, null, 4); 286 }); 287 result.push(function(jsonObject){ 288 return jsonObject.stringify(simpleObjectWithProto, null, "ab"); 289 }); 290 result.push(function(jsonObject){ 291 return jsonObject.stringify(simpleObjectWithProto, null, 4); 292 }); 293 result.push(function(jsonObject){ 294 return jsonObject.stringify(simpleObjectWithProto, null, 10); 295 }); 296 result.push(function(jsonObject){ 297 return jsonObject.stringify(simpleObjectWithProto, null, 11); 298 }); 299 result[result.length - 1].expected = JSON.stringify(simpleObjectWithProto, null, 10); 300 result.push(function(jsonObject){ 301 return jsonObject.stringify(simpleObjectWithProto, null, " "); 302 }); 303 result[result.length - 1].expected = JSON.stringify(simpleObjectWithProto, null, 10); 304 result.push(function(jsonObject){ 305 return jsonObject.stringify(simpleObjectWithProto, null, " "); 306 }); 307 result[result.length - 1].expected = JSON.stringify(simpleObjectWithProto, null, 10); 308 result.push(function(jsonObject){ 309 return jsonObject.stringify(complexArrayWithProto, null, " "); 310 }); 311 result.push(function(jsonObject){ 312 return jsonObject.stringify(complexArrayWithProto, null, 4); 313 }); 314 result.push(function(jsonObject){ 315 return jsonObject.stringify(complexArrayWithProto, null, "ab"); 316 }); 317 result.push(function(jsonObject){ 318 return jsonObject.stringify(complexArrayWithProto, null, 4); 319 }); 320 result.push(function(jsonObject){ 321 return jsonObject.stringify(complexObjectWithProto, null, " "); 322 }); 323 result.push(function(jsonObject){ 324 return jsonObject.stringify(complexObjectWithProto, null, 4); 325 }); 326 result.push(function(jsonObject){ 327 return jsonObject.stringify(complexObjectWithProto, null, "ab"); 328 }); 329 result.push(function(jsonObject){ 330 return jsonObject.stringify(complexObjectWithProto, null, 4); 331 }); 332 result.push(function(jsonObject){ 333 return jsonObject.stringify(objectWithSideEffectGetter); 334 }); 335 result.push(function(jsonObject){ 336 return jsonObject.stringify(objectWithSideEffectGetterAndProto); 337 }); 338 result.push(function(jsonObject){ 339 return jsonObject.stringify(arrayWithSideEffectGetter); 340 }); 341 result.push(function(jsonObject){ 342 return jsonObject.stringify(arrayWithSideEffectGetterAndProto); 343 }); 344 var replaceTracker; 345 function replaceFunc(key, value) { 346 replaceTracker += key + "("+(typeof key)+")" + JSON.stringify(value) + ";"; 347 return value; 348 } 349 result.push(function(jsonObject){ 350 replaceTracker = ""; 351 jsonObject.stringify([1,2,3,,,,4,5,6], replaceFunc); 352 return replaceTracker; 353 }); 354 result[result.length - 1].expected = '(string)[1,2,3,null,null,null,4,5,6];0(number)1;1(number)2;2(number)3;3(number)undefined;4(number)undefined;5(number)undefined;6(number)4;7(number)5;8(number)6;' 355 result.push(function(jsonObject){ 356 replaceTracker = ""; 357 jsonObject.stringify({a:"a", b:"b", c:"c", 3: "d", 2: "e", 1: "f"}, replaceFunc); 358 return replaceTracker; 359 }); 360 result[result.length - 1].expected = '(string){"1":"f","2":"e","3":"d","a":"a","b":"b","c":"c"};1(string)"f";2(string)"e";3(string)"d";a(string)"a";b(string)"b";c(string)"c";'; 361 result.push(function(jsonObject){ 362 var count = 0; 363 var array = [{toString:function(){count++; array[0]='a'; array[1]='c'; array[2]='b'; return 'a'}}]; 364 jsonObject.stringify(simpleObject, array); 365 return count; 366 }); 367 result.push(function(jsonObject){ 368 var allString = true; 369 var array = [{toString:function(){array[0]='a'; array[1]='c'; array[2]='b'; return 'a'}}, 'b', 'c']; 370 return jsonObject.stringify(simpleObject, array); 371 }); 372 result.push(function(jsonObject){ 373 var count = 0; 374 var array = [{toString:function(){count++; array[0]='a'; array[1]='c'; array[2]='b'; return 'a'}}, 'b', 'c']; 375 jsonObject.stringify(simpleObject, array); 376 return count; 377 }); 378 result.push(function(jsonObject){ 379 return jsonObject.stringify({a:"1", get b() { this.a="foo"; return "getter"; }, c:"3"}); 380 }); 381 result.push(function(jsonObject){ 382 return jsonObject.stringify({a:"1", get b() { this.c="foo"; return "getter"; }, c:"3"}); 383 }); 384 result.push(function(jsonObject){ 385 var setterCalled = false; 386 jsonObject.stringify({a:"1", set b(s) { setterCalled = true; return "setter"; }, c:"3"}); 387 return setterCalled; 388 }); 389 result.push(function(jsonObject){ 390 return jsonObject.stringify({a:"1", get b(){ return "getter"; }, set b(s) { return "setter"; }, c:"3"}); 391 }); 392 result.push(function(jsonObject){ 393 return jsonObject.stringify(new Array(10)); 394 }); 395 result.push(function(jsonObject){ 396 return jsonObject.stringify([undefined,,null,0,false]); 397 }); 398 result.push(function(jsonObject){ 399 return jsonObject.stringify({p1:undefined,p2:null,p3:0,p4:false}); 400 }); 401 var cycleTracker = ""; 402 var cyclicObject = { get preSelf1() { cycleTracker+="preSelf1,"; return "preSelf1"; }, 403 preSelf2: {toJSON:function(){cycleTracker+="preSelf2,"; return "preSelf2"}}, 404 self: [], 405 get postSelf1() { cycleTracker+="postSelf1,"; return "postSelf1" }, 406 postSelf2: {toJSON:function(){cycleTracker+="postSelf2,"; return "postSelf2"}}, 407 toJSON : function(key) { cycleTracker += key + "("+(typeof key)+"):" + this; return this; } 408 }; 409 cyclicObject.self = cyclicObject; 410 result.push(function(jsonObject){ 411 cycleTracker = ""; 412 return jsonObject.stringify(cyclicObject); 413 }); 414 result[result.length - 1].throws = true; 415 result.push(function(jsonObject){ 416 cycleTracker = ""; 417 try { jsonObject.stringify(cyclicObject); } catch(e) { cycleTracker += " -> exception" } 418 return cycleTracker; 419 }); 420 result[result.length - 1].expected = "(string):[object Object]preSelf1,preSelf2,self(string):[object Object] -> exception" 421 var cyclicArray = [{toJSON : function(key,value) { cycleTracker += key + "("+(typeof key)+"):" + this; cycleTracker += "first,"; return this; }}, 422 cyclicArray, 423 {toJSON : function(key,value) { cycleTracker += key + "("+(typeof key)+"):" + this; cycleTracker += "second,"; return this; }}]; 424 cyclicArray[1] = cyclicArray; 425 result.push(function(jsonObject){ 426 cycleTracker = ""; 427 return jsonObject.stringify(cyclicArray); 428 }); 429 result[result.length - 1].throws = true; 430 result.push(function(jsonObject){ 431 cycleTracker = ""; 432 try { jsonObject.stringify(cyclicArray); } catch(e) { cycleTracker += " -> exception" } 433 return cycleTracker; 434 }); 435 result[result.length - 1].expected = "0(number):[object Object]first, -> exception"; 436 function createArray(len, o) { var r = []; for (var i = 0; i < len; i++) r[i] = o; return r; } 437 var getterCalls; 438 var magicObject = createArray(10, {abcdefg: [1,2,5,"ab", null, undefined, true, false,,], 439 get calls() {return ++getterCalls; }, 440 "123":createArray(15, "foo"), 441 "":{a:"b"}}); 442 result.push(function(jsonObject){ 443 getterCalls = 0; 444 return jsonObject.stringify(magicObject) + " :: getter calls = " + getterCalls; 445 }); 446 result.push(function(jsonObject){ 447 return jsonObject.stringify(undefined); 448 }); 449 result.push(function(jsonObject){ 450 return jsonObject.stringify(null); 451 }); 452 result.push(function(jsonObject){ 453 return jsonObject.stringify({toJSON:function(){ return undefined; }}); 454 }); 455 result.push(function(jsonObject){ 456 return jsonObject.stringify({toJSON:function(){ return null; }}); 457 }); 458 result.push(function(jsonObject){ 459 return jsonObject.stringify([{toJSON:function(){ return undefined; }}]); 460 }); 461 result.push(function(jsonObject){ 462 return jsonObject.stringify([{toJSON:function(){ return null; }}]); 463 }); 464 result.push(function(jsonObject){ 465 return jsonObject.stringify({a:{toJSON:function(){ return undefined; }}}); 466 }); 467 result.push(function(jsonObject){ 468 return jsonObject.stringify({a:{toJSON:function(){ return null; }}}); 469 }); 470 result.push(function(jsonObject){ 471 return jsonObject.stringify({a:{toJSON:function(){ return function(){}; }}}); 472 }); 473 result.push(function(jsonObject){ 474 return jsonObject.stringify({a:function(){}}); 475 }); 476 result.push(function(jsonObject){ 477 var deepObject = {}; 478 for (var i = 0; i < 1024; i++) 479 deepObject = {next:deepObject}; 480 return jsonObject.stringify(deepObject); 481 }); 482 result.push(function(jsonObject){ 483 var deepArray = []; 484 for (var i = 0; i < 1024; i++) 485 deepArray = [deepArray]; 486 return jsonObject.stringify(deepArray); 487 }); 488 result.push(function(jsonObject){ 489 var depth = 0; 490 function toDeepVirtualJSONObject() { 491 if (++depth >= 1024) 492 return {}; 493 var r = {}; 494 r.toJSON = toDeepVirtualJSONObject; 495 return {recurse: r}; 496 } 497 return jsonObject.stringify(toDeepVirtualJSONObject()); 498 }); 499 result.push(function(jsonObject){ 500 var depth = 0; 501 function toDeepVirtualJSONArray() { 502 if (++depth >= 1024) 503 return []; 504 var r = []; 505 r.toJSON = toDeepJSONArray; 506 return [r]; 507 } 508 return jsonObject.stringify(toDeepVirtualJSONArray()); 509 }); 510 var fullCharsetString = ""; 511 for (var i = 0; i < 65536; i++) 512 fullCharsetString += String.fromCharCode(i); 513 result.push(function(jsonObject){ 514 return jsonObject.stringify(fullCharsetString); 515 }); 516 return result; 517} 518var tests = createTests(); 519for (var i = 0; i < tests.length; i++) { 520 try { 521 debug(tests[i]); 522 if (tests[i].throws) 523 shouldThrow('tests[i](nativeJSON)'); 524 else if (tests[i].expected) 525 shouldBe('tests[i](nativeJSON)', "tests[i].expected"); 526 else 527 shouldBe('tests[i](nativeJSON)', "tests[i](JSON)"); 528 }catch(e){} 529} 530