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