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 15.2.3.9 and
29// ES 15.2.3.12
30
31// Flags: --allow-natives-syntax --noalways-opt
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.seal(non_objects[key]);
39  } catch(e) {
40    exception = true;
41    assertTrue(/Object.seal 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.isSealed(non_objects[key]);
50  } catch(e) {
51    exception = true;
52    assertTrue(/Object.isSealed 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.isSealed(obj));
71
72Object.seal(obj);
73
74// Make sure we are no longer extensible.
75assertFalse(Object.isExtensible(obj));
76assertTrue(Object.isSealed(obj));
77
78// We should not be frozen, since we are still able to
79// update values.
80assertFalse(Object.isFrozen(obj));
81
82// We should not allow new properties to be added.
83obj.foo = 42;
84assertEquals(obj.foo, undefined);
85
86desc = Object.getOwnPropertyDescriptor(obj, 'x');
87assertTrue(desc.writable);
88assertFalse(desc.configurable);
89assertEquals(42, desc.value);
90
91desc = Object.getOwnPropertyDescriptor(obj, 'z');
92assertTrue(desc.writable);
93assertFalse(desc.configurable);
94assertEquals("foobar", desc.value);
95
96// Since writable is not affected by seal we should still be able to
97// update the values.
98obj.x = "43";
99assertEquals("43", obj.x);
100
101// Test on accessors.
102var obj2 = {};
103function get() { return 43; };
104function set() {};
105Object.defineProperty(obj2, 'x', { get: get, set: set, configurable: true });
106
107desc = Object.getOwnPropertyDescriptor(obj2, 'x');
108assertTrue(desc.configurable);
109assertEquals(undefined, desc.value);
110assertEquals(set, desc.set);
111assertEquals(get, desc.get);
112
113assertTrue(Object.isExtensible(obj2));
114assertFalse(Object.isSealed(obj2));
115Object.seal(obj2);
116
117// Since this is an accessor property the object is now effectively both
118// sealed and frozen (accessors has no writable attribute).
119assertTrue(Object.isFrozen(obj2));
120assertFalse(Object.isExtensible(obj2));
121assertTrue(Object.isSealed(obj2));
122
123desc = Object.getOwnPropertyDescriptor(obj2, 'x');
124assertFalse(desc.configurable);
125assertEquals(undefined, desc.value);
126assertEquals(set, desc.set);
127assertEquals(get, desc.get);
128
129obj2.foo = 42;
130assertEquals(obj2.foo, undefined);
131
132// Test seal on arrays.
133var arr = new Array(42,43);
134
135desc = Object.getOwnPropertyDescriptor(arr, '0');
136assertTrue(desc.configurable);
137assertTrue(desc.writable);
138assertEquals(42, desc.value);
139
140desc = Object.getOwnPropertyDescriptor(arr, '1');
141assertTrue(desc.configurable);
142assertTrue(desc.writable);
143assertEquals(43, desc.value);
144
145assertTrue(Object.isExtensible(arr));
146assertFalse(Object.isSealed(arr));
147Object.seal(arr);
148assertTrue(Object.isSealed(arr));
149assertFalse(Object.isExtensible(arr));
150// Since the values in the array is still writable this object
151// is not frozen.
152assertFalse(Object.isFrozen(arr));
153
154desc = Object.getOwnPropertyDescriptor(arr, '0');
155assertFalse(desc.configurable);
156assertTrue(desc.writable);
157assertEquals(42, desc.value);
158
159desc = Object.getOwnPropertyDescriptor(arr, '1');
160assertFalse(desc.configurable);
161assertTrue(desc.writable);
162assertEquals(43, desc.value);
163
164arr[0] = 'foo';
165
166// We should be able to overwrite the existing value.
167assertEquals('foo', arr[0]);
168
169
170// Test that isSealed returns the correct value even if configurable
171// has been set to false on all properties manually and the extensible
172// flag has also been set to false manually.
173var obj3 = { x: 42, y: 'foo' };
174
175assertFalse(Object.isFrozen(obj3));
176
177Object.defineProperty(obj3, 'x', {configurable: false, writable: true});
178Object.defineProperty(obj3, 'y', {configurable: false, writable: false});
179Object.preventExtensions(obj3);
180
181assertTrue(Object.isSealed(obj3));
182
183
184// Make sure that an object that has a configurable property
185// is not classified as sealed.
186var obj4 = {};
187Object.defineProperty(obj4, 'x', {configurable: true, writable: false});
188Object.defineProperty(obj4, 'y', {configurable: false, writable: false});
189Object.preventExtensions(obj4);
190
191assertFalse(Object.isSealed(obj4));
192
193// Make sure that Object.seal returns the sealed object.
194var obj4 = {};
195assertTrue(obj4 === Object.seal(obj4));
196
197//
198// Test that built-in array functions can't modify a sealed array.
199//
200obj = [1, 2, 3];
201var objControl = [4, 5, 6];
202
203// Allow these functions to set up monomorphic calls, using custom built-ins.
204var push_call = function(a) { a.push(10); return a; }
205var pop_call = function(a) { return a.pop(); }
206for (var i = 0; i < 3; i++) {
207  push_call(obj);
208  pop_call(obj);
209}
210
211Object.seal(obj);
212assertThrows(function() { push_call(obj); }, TypeError);
213assertThrows(function() { pop_call(obj); }, TypeError);
214
215// But the control object is fine at these sites.
216assertDoesNotThrow(function() { push_call(objControl); });
217assertDoesNotThrow(function() { pop_call(objControl); });
218
219assertDoesNotThrow(function() { obj.push(); });
220assertThrows(function() { obj.push(3); }, TypeError);
221assertThrows(function() { obj.pop(); }, TypeError);
222assertThrows(function() { obj.shift(3); }, TypeError);
223assertDoesNotThrow(function() { obj.unshift(); });
224assertThrows(function() { obj.unshift(1); }, TypeError);
225assertThrows(function() { obj.splice(0, 0, 100, 101, 102); }, TypeError);
226assertDoesNotThrow(function() { obj.splice(0,0); });
227
228assertDoesNotThrow(function() { objControl.push(3); });
229assertDoesNotThrow(function() { objControl.pop(); });
230assertDoesNotThrow(function() { objControl.shift(3); });
231assertDoesNotThrow(function() { objControl.unshift(); });
232assertDoesNotThrow(function() { objControl.splice(0, 0, 100, 101, 102); });
233
234// Verify that crankshaft still does the right thing.
235obj = [1, 2, 3];
236
237push_call = function(a) { a.push(1000); return a; }
238// Include a call site that doesn't have a custom built-in.
239var shift_call = function(a) { a.shift(1000); return a; }
240for (var i = 0; i < 3; i++) {
241  push_call(obj);
242  shift_call(obj);
243}
244
245%OptimizeFunctionOnNextCall(push_call);
246%OptimizeFunctionOnNextCall(shift_call);
247push_call(obj);
248shift_call(obj);
249assertOptimized(push_call);
250assertOptimized(shift_call);
251Object.seal(obj);
252assertThrows(function() { push_call(obj); }, TypeError);
253assertThrows(function() { shift_call(obj); }, TypeError);
254assertUnoptimized(push_call);
255assertUnoptimized(shift_call);
256assertDoesNotThrow(function() { push_call(objControl); });
257assertDoesNotThrow(function() { shift_call(objControl); });
258
259// Verify special behavior of splice on sealed objects.
260obj = [1,2,3];
261Object.seal(obj);
262assertDoesNotThrow(function() { obj.splice(0,1,100); });
263assertEquals(100, obj[0]);
264assertDoesNotThrow(function() { obj.splice(0,2,1,2); });
265assertDoesNotThrow(function() { obj.splice(1,2,1,2); });
266// Count of items to delete is clamped by length.
267assertDoesNotThrow(function() { obj.splice(1,2000,1,2); });
268assertThrows(function() { obj.splice(0,0,1); }, TypeError);
269assertThrows(function() { obj.splice(1,2000,1,2,3); }, TypeError);
270