1 // Copyright 2010 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 // Tests the Object.freeze and Object.isFrozen methods - ES 15.2.3.9 and 29 // ES 15.2.3.12 30 31 // Flags: --allow-natives-syntax 32 33 // Test that we throw an error if an object is not passed as argument. 34 var non_objects = new Array(undefined, null, 1, -1, 0, 42.43); 35 for (var key in non_objects) { 36 var exception = false; 37 try { 38 Object.freeze(non_objects[key]); 39 } catch(e) { 40 exception = true; 41 assertTrue(/Object.freeze called on non-object/.test(e)); 42 } 43 assertTrue(exception); 44 } 45 46 for (var key in non_objects) { 47 exception = false; 48 try { 49 Object.isFrozen(non_objects[key]); 50 } catch(e) { 51 exception = true; 52 assertTrue(/Object.isFrozen called on non-object/.test(e)); 53 } 54 assertTrue(exception); 55 } 56 57 // Test normal data properties. 58 var obj = { x: 42, z: 'foobar' }; 59 var desc = Object.getOwnPropertyDescriptor(obj, 'x'); 60 assertTrue(desc.writable); 61 assertTrue(desc.configurable); 62 assertEquals(42, desc.value); 63 64 desc = Object.getOwnPropertyDescriptor(obj, 'z'); 65 assertTrue(desc.writable); 66 assertTrue(desc.configurable); 67 assertEquals('foobar', desc.value); 68 69 assertTrue(Object.isExtensible(obj)); 70 assertFalse(Object.isFrozen(obj)); 71 72 Object.freeze(obj); 73 74 // Make sure we are no longer extensible. 75 assertFalse(Object.isExtensible(obj)); 76 assertTrue(Object.isFrozen(obj)); 77 78 obj.foo = 42; 79 assertEquals(obj.foo, undefined); 80 81 desc = Object.getOwnPropertyDescriptor(obj, 'x'); 82 assertFalse(desc.writable); 83 assertFalse(desc.configurable); 84 assertEquals(42, desc.value); 85 86 desc = Object.getOwnPropertyDescriptor(obj, 'z'); 87 assertFalse(desc.writable); 88 assertFalse(desc.configurable); 89 assertEquals("foobar", desc.value); 90 91 // Make sure that even if we try overwrite a value that is not writable, it is 92 // not changed. 93 obj.x = "tete"; 94 assertEquals(42, obj.x); 95 obj.x = { get: function() {return 43}, set: function() {} }; 96 assertEquals(42, obj.x); 97 98 // Test on accessors. 99 var obj2 = {}; 100 function get() { return 43; }; 101 function set() {}; 102 Object.defineProperty(obj2, 'x', { get: get, set: set, configurable: true }); 103 104 desc = Object.getOwnPropertyDescriptor(obj2, 'x'); 105 assertTrue(desc.configurable); 106 assertEquals(undefined, desc.value); 107 assertEquals(set, desc.set); 108 assertEquals(get, desc.get); 109 110 assertTrue(Object.isExtensible(obj2)); 111 assertFalse(Object.isFrozen(obj2)); 112 Object.freeze(obj2); 113 assertTrue(Object.isFrozen(obj2)); 114 assertFalse(Object.isExtensible(obj2)); 115 116 desc = Object.getOwnPropertyDescriptor(obj2, 'x'); 117 assertFalse(desc.configurable); 118 assertEquals(undefined, desc.value); 119 assertEquals(set, desc.set); 120 assertEquals(get, desc.get); 121 122 obj2.foo = 42; 123 assertEquals(obj2.foo, undefined); 124 125 126 // Test freeze on arrays. 127 var arr = new Array(42,43); 128 129 desc = Object.getOwnPropertyDescriptor(arr, '0'); 130 assertTrue(desc.configurable); 131 assertTrue(desc.writable); 132 assertEquals(42, desc.value); 133 134 desc = Object.getOwnPropertyDescriptor(arr, '1'); 135 assertTrue(desc.configurable); 136 assertTrue(desc.writable); 137 assertEquals(43, desc.value); 138 139 assertTrue(Object.isExtensible(arr)); 140 assertFalse(Object.isFrozen(arr)); 141 Object.freeze(arr); 142 assertTrue(Object.isFrozen(arr)); 143 assertFalse(Object.isExtensible(arr)); 144 145 desc = Object.getOwnPropertyDescriptor(arr, '0'); 146 assertFalse(desc.configurable); 147 assertFalse(desc.writable); 148 assertEquals(42, desc.value); 149 150 desc = Object.getOwnPropertyDescriptor(arr, '1'); 151 assertFalse(desc.configurable); 152 assertFalse(desc.writable); 153 assertEquals(43, desc.value); 154 155 arr[0] = 'foo'; 156 157 assertEquals(arr[0], 42); 158 159 160 // Test that isFrozen return the correct value even if configurable has been set 161 // to false on all properties manually and the extensible flag has also been set 162 // to false manually. 163 var obj3 = { x: 42, y: 'foo' }; 164 165 assertFalse(Object.isFrozen(obj3)); 166 167 Object.defineProperty(obj3, 'x', {configurable: false, writable: false}); 168 Object.defineProperty(obj3, 'y', {configurable: false, writable: false}); 169 Object.preventExtensions(obj3); 170 171 assertTrue(Object.isFrozen(obj3)); 172 173 174 // Make sure that an object that has only non-configurable, but one 175 // writable property, is not classified as frozen. 176 var obj4 = {}; 177 Object.defineProperty(obj4, 'x', {configurable: false, writable: true}); 178 Object.defineProperty(obj4, 'y', {configurable: false, writable: false}); 179 Object.preventExtensions(obj4); 180 181 assertFalse(Object.isFrozen(obj4)); 182 183 // Make sure that an object that has only non-writable, but one 184 // configurable property, is not classified as frozen. 185 var obj5 = {}; 186 Object.defineProperty(obj5, 'x', {configurable: true, writable: false}); 187 Object.defineProperty(obj5, 'y', {configurable: false, writable: false}); 188 Object.preventExtensions(obj5); 189 190 assertFalse(Object.isFrozen(obj5)); 191 192 // Make sure that Object.freeze returns the frozen object. 193 var obj6 = {} 194 assertTrue(obj6 === Object.freeze(obj6)) 195 196 // Test that the enumerable attribute is unperturbed by freezing. 197 obj = { x: 42, y: 'foo' }; 198 Object.defineProperty(obj, 'y', {enumerable: false}); 199 Object.freeze(obj); 200 assertTrue(Object.isFrozen(obj)); 201 desc = Object.getOwnPropertyDescriptor(obj, 'x'); 202 assertTrue(desc.enumerable); 203 desc = Object.getOwnPropertyDescriptor(obj, 'y'); 204 assertFalse(desc.enumerable); 205 206 // Fast properties should remain fast 207 obj = { x: 42, y: 'foo' }; 208 assertTrue(%HasFastProperties(obj)); 209 Object.freeze(obj); 210 assertTrue(Object.isFrozen(obj)); 211 assertTrue(%HasFastProperties(obj)); 212 213 // Frozen objects should share maps where possible 214 obj = { prop1: 1, prop2: 2 }; 215 obj2 = { prop1: 3, prop2: 4 }; 216 assertTrue(%HaveSameMap(obj, obj2)); 217 Object.freeze(obj); 218 Object.freeze(obj2); 219 assertTrue(Object.isFrozen(obj)); 220 assertTrue(Object.isFrozen(obj2)); 221 assertTrue(%HaveSameMap(obj, obj2)); 222 223 // Frozen objects should share maps even when they have elements 224 obj = { prop1: 1, prop2: 2, 75: 'foo' }; 225 obj2 = { prop1: 3, prop2: 4, 150: 'bar' }; 226 assertTrue(%HaveSameMap(obj, obj2)); 227 Object.freeze(obj); 228 Object.freeze(obj2); 229 assertTrue(Object.isFrozen(obj)); 230 assertTrue(Object.isFrozen(obj2)); 231 assertTrue(%HaveSameMap(obj, obj2)); 232 233 // Setting elements after freezing should not be allowed 234 obj = { prop: 'thing' }; 235 Object.freeze(obj); 236 assertTrue(Object.isFrozen(obj)); 237 obj[0] = 'hello'; 238 assertFalse(obj.hasOwnProperty(0)); 239 240 // Freezing an object in dictionary mode should work 241 // Also testing that getter/setter properties work after freezing 242 obj = { }; 243 for (var i = 0; i < 100; ++i) { 244 obj['x' + i] = i; 245 } 246 var accessorDidRun = false; 247 Object.defineProperty(obj, 'accessor', { 248 get: function() { return 42 }, 249 set: function() { accessorDidRun = true }, 250 configurable: true, 251 enumerable: true 252 }); 253 254 assertFalse(%HasFastProperties(obj)); 255 Object.freeze(obj); 256 assertFalse(%HasFastProperties(obj)); 257 assertTrue(Object.isFrozen(obj)); 258 assertFalse(Object.isExtensible(obj)); 259 for (var i = 0; i < 100; ++i) { 260 desc = Object.getOwnPropertyDescriptor(obj, 'x' + i); 261 assertFalse(desc.writable); 262 assertFalse(desc.configurable); 263 } 264 assertEquals(42, obj.accessor); 265 assertFalse(accessorDidRun); 266 obj.accessor = 'ignored value'; 267 assertTrue(accessorDidRun); 268 269 // Freezing arguments should work 270 var func = function(arg) { 271 Object.freeze(arguments); 272 assertTrue(Object.isFrozen(arguments)); 273 }; 274 func('hello', 'world'); 275 func('goodbye', 'world'); 276 277 // Freezing sparse arrays 278 var sparseArr = [0, 1]; 279 sparseArr[10000] = 10000; 280 Object.freeze(sparseArr); 281 assertTrue(Object.isFrozen(sparseArr)); 282 283 // Accessors on fast object should behavior properly after freezing 284 obj = {}; 285 Object.defineProperty(obj, 'accessor', { 286 get: function() { return 42 }, 287 set: function() { accessorDidRun = true }, 288 configurable: true, 289 enumerable: true 290 }); 291 assertTrue(%HasFastProperties(obj)); 292 Object.freeze(obj); 293 assertTrue(Object.isFrozen(obj)); 294 assertTrue(%HasFastProperties(obj)); 295 assertEquals(42, obj.accessor); 296 accessorDidRun = false; 297 obj.accessor = 'ignored value'; 298 assertTrue(accessorDidRun); 299 300 // Test for regression in mixed accessor/data property objects. 301 // The strict function is one such object. 302 assertTrue(Object.isFrozen(Object.freeze(function(){"use strict";}))); 303 304 // Also test a simpler case 305 obj = {}; 306 Object.defineProperty(obj, 'accessor', { 307 get: function() { return 42 }, 308 set: function() { accessorDidRun = true }, 309 configurable: true, 310 enumerable: true 311 }); 312 obj.data = 'foo'; 313 assertTrue(%HasFastProperties(obj)); 314 Object.freeze(obj); 315 assertTrue(%HasFastProperties(obj)); 316 assertTrue(Object.isFrozen(obj)); 317 318 // Test array built-in functions with freeze. 319 obj = [1,2,3]; 320 Object.freeze(obj); 321 // if frozen implies sealed, then the tests in object-seal.js are mostly 322 // sufficient. 323 assertTrue(Object.isSealed(obj)); 324 325 assertDoesNotThrow(function() { obj.push(); }); 326 assertDoesNotThrow(function() { obj.unshift(); }); 327 assertDoesNotThrow(function() { obj.splice(0,0); }); 328 assertTrue(Object.isFrozen(obj)); 329 330 // Verify that an item can't be changed with splice. 331 assertThrows(function() { obj.splice(0,1,1); }, TypeError); 332 333 // Verify that unshift() with no arguments will fail if it reifies from 334 // the prototype into the object. 335 obj = [1,,3]; 336 obj.__proto__[1] = 1; 337 assertEquals(1, obj[1]); 338 Object.freeze(obj); 339 assertThrows(function() { obj.unshift(); }, TypeError); 340