1// Copyright 2013 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 24description("Test behaviour of JSON reviver function.") 25if (!Array.isArray) 26 Array.isArray = function(o) { return o.constructor === Array; } 27 28function arrayReviver(i,v) { 29 if (i != "") { 30 currentHolder = this; 31 debug(""); 32 debug("Ensure the holder for our array is indeed an array"); 33 shouldBeTrue("Array.isArray(currentHolder)"); 34 shouldBe("currentHolder.length", "" + expectedLength); 35 if (i > 0) { 36 debug(""); 37 debug("Ensure that we always get the same holder"); 38 shouldBe("currentHolder", "lastHolder"); 39 } 40 switch (Number(i)) { 41 case 0: 42 v = undefined; 43 debug(""); 44 debug("Ensure that the holder already has all the properties present at the start of filtering"); 45 shouldBe("currentHolder[0]", '"a value"'); 46 shouldBe("currentHolder[1]", '"another value"'); 47 shouldBe("currentHolder[2]", '"and another value"'); 48 shouldBe("currentHolder[3]", '"to delete"'); 49 shouldBe("currentHolder[4]", '"extra value"'); 50 break; 51 52 case 1: 53 debug(""); 54 debug("Ensure that returning undefined has removed the property 0 from the holder during filtering."); 55 shouldBeFalse("currentHolder.hasOwnProperty(0)"); 56 currentHolder[2] = "a replaced value"; 57 break; 58 59 case 2: 60 debug(""); 61 debug("Ensure that changing the value of a property is reflected while filtering.") 62 shouldBe("currentHolder[2]", '"a replaced value"'); 63 value = v; 64 debug(""); 65 debug("Ensure that the changed value is reflected in the arguments passed to the reviver"); 66 shouldBe("value", "currentHolder[2]"); 67 delete this[3]; 68 break; 69 70 case 3: 71 debug(""); 72 debug("Ensure that we visited a value that we have deleted, and that deletion is reflected while filtering."); 73 shouldBeFalse("currentHolder.hasOwnProperty(3)"); 74 value = v; 75 debug(""); 76 debug("Ensure that when visiting a deleted property value is undefined"); 77 shouldBeUndefined("value"); 78 this.length = 3; 79 v = "undelete the property"; 80 expectedLength = 4; 81 break; 82 83 case 4: 84 if (this.length != 4) { 85 testFailed("Did not call reviver for deleted property"); 86 expectedLength = this.length = 3; 87 break; 88 } 89 90 case 5: 91 testPassed("Ensured that property was visited despite Array length being reduced."); 92 value = v; 93 shouldBeUndefined("value"); 94 this[10] = "fail"; 95 break; 96 97 default: 98 testFailed("Visited unexpected property " + i + " with value " + v); 99 } 100 } 101 lastHolder = this; 102 return v; 103} 104expectedLength = 5; 105var result = JSON.parse('["a value", "another value", "and another value", "to delete", "extra value"]', arrayReviver); 106debug(""); 107debug("Ensure that we created the root holder as specified in ES5"); 108shouldBeTrue("'' in lastHolder"); 109shouldBe("result", "lastHolder['']"); 110debug(""); 111debug("Ensure that a deleted value is revived if the reviver function returns a value"); 112shouldBeTrue("result.hasOwnProperty(3)"); 113 114function objectReviver(i,v) { 115 if (i != "") { 116 currentHolder = this; 117 shouldBeTrue("currentHolder != globalObject"); 118 if (seen) { 119 debug(""); 120 debug("Ensure that we get the same holder object for each property"); 121 shouldBe("currentHolder", "lastHolder"); 122 } 123 seen = true; 124 switch (i) { 125 case "a property": 126 v = undefined; 127 debug(""); 128 debug("Ensure that the holder already has all the properties present at the start of filtering"); 129 shouldBe("currentHolder['a property']", '"a value"'); 130 shouldBe("currentHolder['another property']", '"another value"'); 131 shouldBe("currentHolder['and another property']", '"and another value"'); 132 shouldBe("currentHolder['to delete']", '"will be deleted"'); 133 break; 134 135 case "another property": 136 debug(""); 137 debug("Ensure that returning undefined has correctly removed the property 'a property' from the holder object"); 138 shouldBeFalse("currentHolder.hasOwnProperty('a property')"); 139 currentHolder['and another property'] = "a replaced value"; 140 break; 141 142 case "and another property": 143 debug("Ensure that changing the value of a property is reflected while filtering."); 144 shouldBe("currentHolder['and another property']", '"a replaced value"'); 145 value = v; 146 debug(""); 147 debug("Ensure that the changed value is reflected in the arguments passed to the reviver"); 148 shouldBe("value", '"a replaced value"'); 149 delete this["to delete"]; 150 break; 151 152 case "to delete": 153 debug(""); 154 debug("Ensure that we visited a value that we have deleted, and that deletion is reflected while filtering."); 155 shouldBeFalse("currentHolder.hasOwnProperty('to delete')"); 156 value = v; 157 debug(""); 158 debug("Ensure that when visiting a deleted property value is undefined"); 159 shouldBeUndefined("value"); 160 v = "undelete the property"; 161 this["new property"] = "fail"; 162 break; 163 default: 164 testFailed("Visited unexpected property " + i + " with value " + v); 165 } 166 } 167 lastHolder = this; 168 return v; 169} 170 171debug(""); 172debug("Test behaviour of revivor used in conjunction with an object"); 173var seen = false; 174var globalObject = this; 175var result = JSON.parse('{"a property" : "a value", "another property" : "another value", "and another property" : "and another value", "to delete" : "will be deleted"}', objectReviver); 176debug(""); 177debug("Ensure that we created the root holder as specified in ES5"); 178shouldBeTrue("lastHolder.hasOwnProperty('')"); 179shouldBeFalse("result.hasOwnProperty('a property')"); 180shouldBeTrue("result.hasOwnProperty('to delete')"); 181shouldBe("result", "lastHolder['']"); 182 183debug(""); 184debug("Test behaviour of revivor that introduces a cycle"); 185function reviveAddsCycle(i, v) { 186 if (i == 0) 187 this[1] = this; 188 return v; 189} 190 191shouldThrow('JSON.parse("[0,1]", reviveAddsCycle)'); 192 193debug(""); 194debug("Test behaviour of revivor that introduces a new array classed object (the result of a regex)"); 195var createdBadness = false; 196function reviveIntroducesNewArrayLikeObject(i, v) { 197 if (i == 0 && !createdBadness) { 198 this[1] = /(a)+/.exec("a"); 199 createdBadness = true; 200 } 201 return v; 202} 203 204shouldBe('JSON.stringify(JSON.parse("[0,1]", reviveIntroducesNewArrayLikeObject))', '\'[0,["a","a"]]\''); 205