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.seal and Object.isSealed methods - ES 19.1.2.17 and 29// ES 19.1.2.13 30 31// Flags: --allow-natives-syntax --noalways-opt 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.seal(non_objects[key])); 37} 38 39// Test that isFrozen always returns true for non-objects 40for (var key in non_objects) { 41 assertTrue(Object.isSealed(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.isSealed(obj)); 58 59Object.seal(obj); 60 61// Make sure we are no longer extensible. 62assertFalse(Object.isExtensible(obj)); 63assertTrue(Object.isSealed(obj)); 64 65// We should not be frozen, since we are still able to 66// update values. 67assertFalse(Object.isFrozen(obj)); 68 69// We should not allow new properties to be added. 70obj.foo = 42; 71assertEquals(obj.foo, undefined); 72 73desc = Object.getOwnPropertyDescriptor(obj, 'x'); 74assertTrue(desc.writable); 75assertFalse(desc.configurable); 76assertEquals(42, desc.value); 77 78desc = Object.getOwnPropertyDescriptor(obj, 'z'); 79assertTrue(desc.writable); 80assertFalse(desc.configurable); 81assertEquals("foobar", desc.value); 82 83// Since writable is not affected by seal we should still be able to 84// update the values. 85obj.x = "43"; 86assertEquals("43", obj.x); 87 88// Test on accessors. 89var obj2 = {}; 90function get() { return 43; }; 91function set() {}; 92Object.defineProperty(obj2, 'x', { get: get, set: set, configurable: true }); 93 94desc = Object.getOwnPropertyDescriptor(obj2, 'x'); 95assertTrue(desc.configurable); 96assertEquals(undefined, desc.value); 97assertEquals(set, desc.set); 98assertEquals(get, desc.get); 99 100assertTrue(Object.isExtensible(obj2)); 101assertFalse(Object.isSealed(obj2)); 102Object.seal(obj2); 103 104// Since this is an accessor property the object is now effectively both 105// sealed and frozen (accessors has no writable attribute). 106assertTrue(Object.isFrozen(obj2)); 107assertFalse(Object.isExtensible(obj2)); 108assertTrue(Object.isSealed(obj2)); 109 110desc = Object.getOwnPropertyDescriptor(obj2, 'x'); 111assertFalse(desc.configurable); 112assertEquals(undefined, desc.value); 113assertEquals(set, desc.set); 114assertEquals(get, desc.get); 115 116obj2.foo = 42; 117assertEquals(obj2.foo, undefined); 118 119// Test seal on arrays. 120var arr = new Array(42,43); 121 122desc = Object.getOwnPropertyDescriptor(arr, '0'); 123assertTrue(desc.configurable); 124assertTrue(desc.writable); 125assertEquals(42, desc.value); 126 127desc = Object.getOwnPropertyDescriptor(arr, '1'); 128assertTrue(desc.configurable); 129assertTrue(desc.writable); 130assertEquals(43, desc.value); 131 132assertTrue(Object.isExtensible(arr)); 133assertFalse(Object.isSealed(arr)); 134Object.seal(arr); 135assertTrue(Object.isSealed(arr)); 136assertFalse(Object.isExtensible(arr)); 137// Since the values in the array is still writable this object 138// is not frozen. 139assertFalse(Object.isFrozen(arr)); 140 141desc = Object.getOwnPropertyDescriptor(arr, '0'); 142assertFalse(desc.configurable); 143assertTrue(desc.writable); 144assertEquals(42, desc.value); 145 146desc = Object.getOwnPropertyDescriptor(arr, '1'); 147assertFalse(desc.configurable); 148assertTrue(desc.writable); 149assertEquals(43, desc.value); 150 151arr[0] = 'foo'; 152 153// We should be able to overwrite the existing value. 154assertEquals('foo', arr[0]); 155 156 157// Test that isSealed returns the correct value even if configurable 158// has been set to false on all properties manually and the extensible 159// flag has also been set to false manually. 160var obj3 = { x: 42, y: 'foo' }; 161 162assertFalse(Object.isFrozen(obj3)); 163 164Object.defineProperty(obj3, 'x', {configurable: false, writable: true}); 165Object.defineProperty(obj3, 'y', {configurable: false, writable: false}); 166Object.preventExtensions(obj3); 167 168assertTrue(Object.isSealed(obj3)); 169 170 171// Make sure that an object that has a configurable property 172// is not classified as sealed. 173var obj4 = {}; 174Object.defineProperty(obj4, 'x', {configurable: true, writable: false}); 175Object.defineProperty(obj4, 'y', {configurable: false, writable: false}); 176Object.preventExtensions(obj4); 177 178assertFalse(Object.isSealed(obj4)); 179 180// Make sure that Object.seal returns the sealed object. 181var obj4 = {}; 182assertTrue(obj4 === Object.seal(obj4)); 183 184// 185// Test that built-in array functions can't modify a sealed array. 186// 187obj = [1, 2, 3]; 188var objControl = [4, 5, 6]; 189 190// Allow these functions to set up monomorphic calls, using custom built-ins. 191var push_call = function(a) { a.push(10); return a; } 192var pop_call = function(a) { return a.pop(); } 193for (var i = 0; i < 3; i++) { 194 push_call(obj); 195 pop_call(obj); 196} 197 198Object.seal(obj); 199assertThrows(function() { push_call(obj); }, TypeError); 200assertThrows(function() { pop_call(obj); }, TypeError); 201 202// But the control object is fine at these sites. 203assertDoesNotThrow(function() { push_call(objControl); }); 204assertDoesNotThrow(function() { pop_call(objControl); }); 205 206assertDoesNotThrow(function() { obj.push(); }); 207assertThrows(function() { obj.push(3); }, TypeError); 208assertThrows(function() { obj.pop(); }, TypeError); 209assertThrows(function() { obj.shift(3); }, TypeError); 210assertDoesNotThrow(function() { obj.unshift(); }); 211assertThrows(function() { obj.unshift(1); }, TypeError); 212assertThrows(function() { obj.splice(0, 0, 100, 101, 102); }, TypeError); 213assertDoesNotThrow(function() { obj.splice(0,0); }); 214 215assertDoesNotThrow(function() { objControl.push(3); }); 216assertDoesNotThrow(function() { objControl.pop(); }); 217assertDoesNotThrow(function() { objControl.shift(3); }); 218assertDoesNotThrow(function() { objControl.unshift(); }); 219assertDoesNotThrow(function() { objControl.splice(0, 0, 100, 101, 102); }); 220 221// Verify that crankshaft still does the right thing. 222obj = [1, 2, 3]; 223 224push_call = function(a) { a.push(1000); return a; } 225// Include a call site that doesn't have a custom built-in. 226var shift_call = function(a) { a.shift(1000); return a; } 227for (var i = 0; i < 3; i++) { 228 push_call(obj); 229 shift_call(obj); 230} 231 232%OptimizeFunctionOnNextCall(push_call); 233%OptimizeFunctionOnNextCall(shift_call); 234push_call(obj); 235shift_call(obj); 236assertOptimized(push_call); 237assertOptimized(shift_call); 238Object.seal(obj); 239assertThrows(function() { push_call(obj); }, TypeError); 240assertThrows(function() { shift_call(obj); }, TypeError); 241assertUnoptimized(push_call); 242assertUnoptimized(shift_call); 243assertDoesNotThrow(function() { push_call(objControl); }); 244assertDoesNotThrow(function() { shift_call(objControl); }); 245 246// Verify special behavior of splice on sealed objects. 247obj = [1,2,3]; 248Object.seal(obj); 249assertDoesNotThrow(function() { obj.splice(0,1,100); }); 250assertEquals(100, obj[0]); 251assertDoesNotThrow(function() { obj.splice(0,2,1,2); }); 252assertDoesNotThrow(function() { obj.splice(1,2,1,2); }); 253// Count of items to delete is clamped by length. 254assertDoesNotThrow(function() { obj.splice(1,2000,1,2); }); 255assertThrows(function() { obj.splice(0,0,1); }, TypeError); 256assertThrows(function() { obj.splice(1,2000,1,2,3); }, TypeError); 257 258// Test that the enumerable attribute is unperturbed by sealing. 259obj = { x: 42, y: 'foo' }; 260Object.defineProperty(obj, 'y', {enumerable: false}); 261Object.seal(obj); 262assertTrue(Object.isSealed(obj)); 263assertFalse(Object.isFrozen(obj)); 264desc = Object.getOwnPropertyDescriptor(obj, 'x'); 265assertTrue(desc.enumerable); 266desc = Object.getOwnPropertyDescriptor(obj, 'y'); 267assertFalse(desc.enumerable); 268 269// Fast properties should remain fast 270obj = { x: 42, y: 'foo' }; 271assertTrue(%HasFastProperties(obj)); 272Object.seal(obj); 273assertTrue(Object.isSealed(obj)); 274assertFalse(Object.isFrozen(obj)); 275assertTrue(%HasFastProperties(obj)); 276 277// Sealed objects should share maps where possible 278obj = { prop1: 1, prop2: 2 }; 279obj2 = { prop1: 3, prop2: 4 }; 280assertTrue(%HaveSameMap(obj, obj2)); 281Object.seal(obj); 282Object.seal(obj2); 283assertTrue(Object.isSealed(obj)); 284assertTrue(Object.isSealed(obj2)); 285assertFalse(Object.isFrozen(obj)); 286assertFalse(Object.isFrozen(obj2)); 287assertTrue(%HaveSameMap(obj, obj2)); 288 289// Sealed objects should share maps even when they have elements 290obj = { prop1: 1, prop2: 2, 75: 'foo' }; 291obj2 = { prop1: 3, prop2: 4, 150: 'bar' }; 292assertTrue(%HaveSameMap(obj, obj2)); 293Object.seal(obj); 294Object.seal(obj2); 295assertTrue(Object.isSealed(obj)); 296assertTrue(Object.isSealed(obj2)); 297assertFalse(Object.isFrozen(obj)); 298assertFalse(Object.isFrozen(obj)); 299assertTrue(%HaveSameMap(obj, obj2)); 300 301// Setting elements after sealing should not be allowed 302obj = { prop: 'thing' }; 303Object.seal(obj); 304assertTrue(Object.isSealed(obj)); 305assertFalse(Object.isFrozen(obj)); 306obj[0] = 'hello'; 307assertFalse(obj.hasOwnProperty(0)); 308 309// Sealing an object in dictionary mode should work 310// Also testing that getter/setter properties work after sealing 311obj = { }; 312for (var i = 0; i < 100; ++i) { 313 obj['x' + i] = i; 314} 315var accessorDidRun = false; 316Object.defineProperty(obj, 'accessor', { 317 get: function() { return 42 }, 318 set: function() { accessorDidRun = true }, 319 configurable: true, 320 enumerable: true 321}); 322 323assertFalse(%HasFastProperties(obj)); 324Object.seal(obj); 325assertFalse(%HasFastProperties(obj)); 326assertTrue(Object.isSealed(obj)); 327assertFalse(Object.isFrozen(obj)); 328assertFalse(Object.isExtensible(obj)); 329for (var i = 0; i < 100; ++i) { 330 desc = Object.getOwnPropertyDescriptor(obj, 'x' + i); 331 assertFalse(desc.configurable); 332} 333assertEquals(42, obj.accessor); 334assertFalse(accessorDidRun); 335obj.accessor = 'ignored value'; 336assertTrue(accessorDidRun); 337 338// Sealing arguments should work 339var func = function(arg) { 340 Object.seal(arguments); 341 assertTrue(Object.isSealed(arguments)); 342}; 343func('hello', 'world'); 344func('goodbye', 'world'); 345 346// Sealing sparse arrays 347var sparseArr = [0, 1]; 348sparseArr[10000] = 10000; 349Object.seal(sparseArr); 350assertTrue(Object.isSealed(sparseArr)); 351 352// Accessors on fast object should behavior properly after sealing 353obj = {}; 354Object.defineProperty(obj, 'accessor', { 355 get: function() { return 42 }, 356 set: function() { accessorDidRun = true }, 357 configurable: true, 358 enumerable: true 359}); 360assertTrue(%HasFastProperties(obj)); 361Object.seal(obj); 362assertTrue(Object.isSealed(obj)); 363assertTrue(%HasFastProperties(obj)); 364assertEquals(42, obj.accessor); 365accessorDidRun = false; 366obj.accessor = 'ignored value'; 367assertTrue(accessorDidRun); 368 369// Test for regression in mixed accessor/data property objects. 370// The strict function is one such object. 371assertTrue(Object.isSealed(Object.seal(function(){"use strict";}))); 372 373// Also test a simpler case 374obj = {}; 375Object.defineProperty(obj, 'accessor2', { 376 get: function() { return 42 }, 377 set: function() { accessorDidRun = true }, 378 configurable: true, 379 enumerable: true 380}); 381obj.data = 'foo'; 382assertTrue(%HasFastProperties(obj)); 383Object.seal(obj); 384assertTrue(%HasFastProperties(obj)); 385assertTrue(Object.isSealed(obj)); 386 387function Sealed() {} 388Object.seal(Sealed); 389assertDoesNotThrow(function() { return new Sealed(); }); 390Sealed.prototype.prototypeExists = true; 391assertTrue((new Sealed()).prototypeExists); 392