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