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