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