blob: ef1b16d9061d0d9bb75539812d577aa29e4622c7 [file] [log] [blame]
Ben Murdoch3bec4d22010-07-22 14:51:16 +01001// 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
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000028// Tests the Object.seal and Object.isSealed methods - ES 19.1.2.17 and
29// ES 19.1.2.13
Ben Murdoch3bec4d22010-07-22 14:51:16 +010030
Ben Murdochb8a8cc12014-11-26 15:28:44 +000031// Flags: --allow-natives-syntax --noalways-opt
Ben Murdoch3bec4d22010-07-22 14:51:16 +010032
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000033// 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"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +010035for (var key in non_objects) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000036 assertSame(non_objects[key], Object.seal(non_objects[key]));
Ben Murdoch3bec4d22010-07-22 14:51:16 +010037}
38
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000039// Test that isFrozen always returns true for non-objects
Ben Murdoch3bec4d22010-07-22 14:51:16 +010040for (var key in non_objects) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000041 assertTrue(Object.isSealed(non_objects[key]));
Ben Murdoch3bec4d22010-07-22 14:51:16 +010042}
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.
Steve Block44f0eee2011-05-26 01:26:41 +010070obj.foo = 42;
71assertEquals(obj.foo, undefined);
Ben Murdoch3bec4d22010-07-22 14:51:16 +010072
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";
Ben Murdoch257744e2011-11-30 15:57:28 +000086assertEquals("43", obj.x);
Ben Murdoch3bec4d22010-07-22 14:51:16 +010087
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
Steve Block44f0eee2011-05-26 01:26:41 +0100116obj2.foo = 42;
117assertEquals(obj2.foo, undefined);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100118
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));
Ben Murdoch257744e2011-11-30 15:57:28 +0000137// Since the values in the array is still writable this object
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100138// 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));
Ben Murdochbb769b22010-08-11 14:56:33 +0100179
180// Make sure that Object.seal returns the sealed object.
Ben Murdoch257744e2011-11-30 15:57:28 +0000181var obj4 = {};
182assertTrue(obj4 === Object.seal(obj4));
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000183
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);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400257
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));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000386
387function Sealed() {}
388Object.seal(Sealed);
389assertDoesNotThrow(function() { return new Sealed(); });
390Sealed.prototype.prototypeExists = true;
391assertTrue((new Sealed()).prototypeExists);