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