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