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 24 description("Test behaviour of JSON reviver function.") 25 if (!Array.isArray) 26 Array.isArray = function(o) { return o.constructor === Array; } 27 28 function 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 } 103 expectedLength = 5; 104 var result = JSON.parse('["a value", "another value", "and another value", "to delete", "extra value"]', arrayReviver); 105 debug(""); 106 debug("Ensure that we created the root holder as specified in ES5"); 107 shouldBeTrue("'' in lastHolder"); 108 shouldBe("result", "lastHolder['']"); 109 debug(""); 110 debug("Ensure that a deleted value is revived if the reviver function returns a value"); 111 shouldBeTrue("result.hasOwnProperty(3)"); 112 113 function 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 170 debug(""); 171 debug("Test behaviour of revivor used in conjunction with an object"); 172 var seen = false; 173 var globalObject = this; 174 var result = JSON.parse('{"a property" : "a value", "another property" : "another value", "and another property" : "and another value", "to delete" : "will be deleted"}', objectReviver); 175 debug(""); 176 debug("Ensure that we created the root holder as specified in ES5"); 177 shouldBeTrue("lastHolder.hasOwnProperty('')"); 178 shouldBeFalse("result.hasOwnProperty('a property')"); 179 shouldBeTrue("result.hasOwnProperty('to delete')"); 180 shouldBe("result", "lastHolder['']"); 181 182 debug(""); 183 debug("Test behaviour of revivor that introduces a cycle"); 184 function reviveAddsCycle(i, v) { 185 if (i == 0) 186 this[1] = this; 187 return v; 188 } 189 190 shouldThrow('JSON.parse("[0,1]", reviveAddsCycle)'); 191 192 debug(""); 193 debug("Test behaviour of revivor that introduces a new array classed object (the result of a regex)"); 194 var createdBadness = false; 195 function reviveIntroducesNewArrayLikeObject(i, v) { 196 if (i == 0 && !createdBadness) { 197 this[1] = /(a)+/.exec("a"); 198 createdBadness = true; 199 } 200 return v; 201 } 202 203 shouldBe('JSON.stringify(JSON.parse("[0,1]", reviveIntroducesNewArrayLikeObject))', '\'[0,["a","a"]]\''); 204