object-define-property.js revision 402d937239b0e2fd11bf2f4fe972ad78aa9fd481
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.defineProperty method - ES 15.2.3.6
29
30// Flags: --allow-natives-syntax
31
32// Check that an exception is thrown when null is passed as object.
33try {
34  Object.defineProperty(null, null, null);
35  assertTrue(false);
36} catch (e) {
37  assertTrue(/called on non-object/.test(e));
38}
39
40// Check that an exception is thrown when undefined is passed as object.
41try {
42  Object.defineProperty(undefined, undefined, undefined);
43  assertTrue(false);
44} catch (e) {
45  assertTrue(/called on non-object/.test(e));
46}
47
48// Check that an exception is thrown when non-object is passed as object.
49try {
50  Object.defineProperty(0, "foo", undefined);
51  assertTrue(false);
52} catch (e) {
53  assertTrue(/called on non-object/.test(e));
54}
55
56// Object
57var obj1 = {};
58
59// Values
60var val1 = 0;
61var val2 = 0;
62var val3 = 0;
63
64// Descriptors
65var emptyDesc = {};
66
67var accessorConfigurable = {
68    set: function() { val1++; },
69    get: function() { return val1; },
70    configurable: true
71};
72
73var accessorNoConfigurable = {
74    set: function() { val2++; },
75    get: function() { return val2; },
76    configurable: false
77};
78
79var accessorOnlySet = {
80  set: function() { val3++; },
81  configurable: true
82};
83
84var accessorOnlyGet = {
85  get: function() { return val3; },
86  configurable: true
87};
88
89var accessorDefault = {set: function(){} };
90
91var dataConfigurable = { value: 1000, configurable: true };
92
93var dataNoConfigurable = { value: 2000, configurable: false };
94
95var dataWritable = { value: 3000, writable: true};
96
97
98// Check that we can't add property with undefined attributes.
99try {
100  Object.defineProperty(obj1, "foo", undefined);
101  assertTrue(false);
102} catch (e) {
103  assertTrue(/must be an object/.test(e));
104}
105
106// Make sure that we can add a property with an empty descriptor and
107// that it has the default descriptor values.
108Object.defineProperty(obj1, "foo", emptyDesc);
109
110// foo should be undefined as it has no get, set or value
111assertEquals(undefined, obj1.foo);
112
113// We should, however, be able to retrieve the propertydescriptor which should
114// have all default values (according to 8.6.1).
115var desc = Object.getOwnPropertyDescriptor(obj1, "foo");
116assertFalse(desc.configurable);
117assertFalse(desc.enumerable);
118assertFalse(desc.writable);
119assertEquals(desc.get, undefined);
120assertEquals(desc.set, undefined);
121assertEquals(desc.value, undefined);
122
123// Make sure that getOwnPropertyDescriptor does not return a descriptor
124// with default values if called with non existing property (otherwise
125// the test above is invalid).
126desc = Object.getOwnPropertyDescriptor(obj1, "bar");
127assertEquals(desc, undefined);
128
129// Make sure that foo can't be reset (as configurable is false).
130try {
131  Object.defineProperty(obj1, "foo", accessorConfigurable);
132} catch (e) {
133  assertTrue(/Cannot redefine property/.test(e));
134}
135
136
137// Accessor properties
138
139Object.defineProperty(obj1, "bar", accessorConfigurable);
140desc = Object.getOwnPropertyDescriptor(obj1, "bar");
141assertTrue(desc.configurable);
142assertFalse(desc.enumerable);
143assertEquals(desc.writable, undefined);
144assertEquals(desc.get, accessorConfigurable.get);
145assertEquals(desc.set, accessorConfigurable.set);
146assertEquals(desc.value, undefined);
147assertEquals(1, obj1.bar = 1);
148assertEquals(1, val1);
149assertEquals(1, obj1.bar = 1);
150assertEquals(2, val1);
151assertEquals(2, obj1.bar);
152
153// Redefine bar with non configurable test
154Object.defineProperty(obj1, "bar", accessorNoConfigurable);
155desc = Object.getOwnPropertyDescriptor(obj1, "bar");
156assertFalse(desc.configurable);
157assertFalse(desc.enumerable);
158assertEquals(desc.writable, undefined);
159assertEquals(desc.get, accessorNoConfigurable.get);
160assertEquals(desc.set, accessorNoConfigurable.set);
161assertEquals(desc.value, undefined);
162assertEquals(1, obj1.bar = 1);
163assertEquals(2, val1);
164assertEquals(1, val2);
165assertEquals(1, obj1.bar = 1)
166assertEquals(2, val1);
167assertEquals(2, val2);
168assertEquals(2, obj1.bar);
169
170// Try to redefine bar again - should fail as configurable is false.
171try {
172  Object.defineProperty(obj1, "bar", accessorConfigurable);
173  assertTrue(false);
174} catch(e) {
175  assertTrue(/Cannot redefine property/.test(e));
176}
177
178// Try to redefine bar again using the data descriptor - should fail.
179try {
180  Object.defineProperty(obj1, "bar", dataConfigurable);
181  assertTrue(false);
182} catch(e) {
183  assertTrue(/Cannot redefine property/.test(e));
184}
185
186// Redefine using same descriptor - should succeed.
187Object.defineProperty(obj1, "bar", accessorNoConfigurable);
188desc = Object.getOwnPropertyDescriptor(obj1, "bar");
189assertFalse(desc.configurable);
190assertFalse(desc.enumerable);
191assertEquals(desc.writable, undefined);
192assertEquals(desc.get, accessorNoConfigurable.get);
193assertEquals(desc.set, accessorNoConfigurable.set);
194assertEquals(desc.value, undefined);
195assertEquals(1, obj1.bar = 1);
196assertEquals(2, val1);
197assertEquals(3, val2);
198assertEquals(1, obj1.bar = 1)
199assertEquals(2, val1);
200assertEquals(4, val2);
201assertEquals(4, obj1.bar);
202
203// Define an accessor that has only a setter
204Object.defineProperty(obj1, "setOnly", accessorOnlySet);
205desc = Object.getOwnPropertyDescriptor(obj1, "setOnly");
206assertTrue(desc.configurable);
207assertFalse(desc.enumerable);
208assertEquals(desc.set, accessorOnlySet.set);
209assertEquals(desc.writable, undefined);
210assertEquals(desc.value, undefined);
211assertEquals(desc.get, undefined);
212assertEquals(1, obj1.setOnly = 1);
213assertEquals(1, val3);
214
215// Add a getter - should not touch the setter
216Object.defineProperty(obj1, "setOnly", accessorOnlyGet);
217desc = Object.getOwnPropertyDescriptor(obj1, "setOnly");
218assertTrue(desc.configurable);
219assertFalse(desc.enumerable);
220assertEquals(desc.get, accessorOnlyGet.get);
221assertEquals(desc.set, accessorOnlySet.set);
222assertEquals(desc.writable, undefined);
223assertEquals(desc.value, undefined);
224assertEquals(1, obj1.setOnly = 1);
225assertEquals(2, val3);
226
227// The above should also work if redefining just a getter or setter on
228// an existing property with both a getter and a setter.
229Object.defineProperty(obj1, "both", accessorConfigurable);
230
231Object.defineProperty(obj1, "both", accessorOnlySet);
232desc = Object.getOwnPropertyDescriptor(obj1, "both");
233assertTrue(desc.configurable);
234assertFalse(desc.enumerable);
235assertEquals(desc.set, accessorOnlySet.set);
236assertEquals(desc.get, accessorConfigurable.get);
237assertEquals(desc.writable, undefined);
238assertEquals(desc.value, undefined);
239assertEquals(1, obj1.both = 1);
240assertEquals(3, val3);
241
242
243// Data properties
244
245Object.defineProperty(obj1, "foobar", dataConfigurable);
246desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
247assertEquals(obj1.foobar, 1000);
248assertEquals(desc.value, 1000);
249assertTrue(desc.configurable);
250assertFalse(desc.writable);
251assertFalse(desc.enumerable);
252assertEquals(desc.get, undefined);
253assertEquals(desc.set, undefined);
254//Try writing to non writable attribute - should remain 1000
255obj1.foobar = 1001;
256assertEquals(obj1.foobar, 1000);
257
258
259// Redefine to writable descriptor - now writing to foobar should be allowed
260Object.defineProperty(obj1, "foobar", dataWritable);
261desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
262assertEquals(obj1.foobar, 3000);
263assertEquals(desc.value, 3000);
264// Note that since dataWritable does not define configurable the configurable
265// setting from the redefined property (in this case true) is used.
266assertTrue(desc.configurable);
267assertTrue(desc.writable);
268assertFalse(desc.enumerable);
269assertEquals(desc.get, undefined);
270assertEquals(desc.set, undefined);
271// Writing to the property should now be allowed
272obj1.foobar = 1001;
273assertEquals(obj1.foobar, 1001);
274
275
276// Redefine with non configurable data property.
277Object.defineProperty(obj1, "foobar", dataNoConfigurable);
278desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
279assertEquals(obj1.foobar, 2000);
280assertEquals(desc.value, 2000);
281assertFalse(desc.configurable);
282assertFalse(desc.writable);
283assertFalse(desc.enumerable);
284assertEquals(desc.get, undefined);
285assertEquals(desc.set, undefined);
286
287// Try redefine again - shold fail because configurable is now false.
288try {
289  Object.defineProperty(obj1, "foobar", dataConfigurable);
290  assertTrue(false);
291} catch (e) {
292  assertTrue(/Cannot redefine property/.test(e));
293}
294
295// Try redefine again with accessor property - shold also fail.
296try {
297  Object.defineProperty(obj1, "foobar", dataConfigurable);
298  assertTrue(false);
299} catch (e) {
300  assertTrue(/Cannot redefine property/.test(e));
301}
302
303
304// Redifine with the same descriptor - should succeed (step 6).
305Object.defineProperty(obj1, "foobar", dataNoConfigurable);
306desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
307assertEquals(obj1.foobar, 2000);
308assertEquals(desc.value, 2000);
309assertFalse(desc.configurable);
310assertFalse(desc.writable);
311assertFalse(desc.enumerable);
312assertEquals(desc.get, undefined);
313assertEquals(desc.set, undefined);
314
315
316// New object
317var obj2 = {};
318
319// Make accessor - redefine to data
320Object.defineProperty(obj2, "foo", accessorConfigurable);
321
322// Redefine to data property
323Object.defineProperty(obj2, "foo", dataConfigurable);
324desc = Object.getOwnPropertyDescriptor(obj2, "foo");
325assertEquals(obj2.foo, 1000);
326assertEquals(desc.value, 1000);
327assertTrue(desc.configurable);
328assertFalse(desc.writable);
329assertFalse(desc.enumerable);
330assertEquals(desc.get, undefined);
331assertEquals(desc.set, undefined);
332
333
334// Redefine back to accessor
335Object.defineProperty(obj2, "foo", accessorConfigurable);
336desc = Object.getOwnPropertyDescriptor(obj2, "foo");
337assertTrue(desc.configurable);
338assertFalse(desc.enumerable);
339assertEquals(desc.writable, undefined);
340assertEquals(desc.get, accessorConfigurable.get);
341assertEquals(desc.set, accessorConfigurable.set);
342assertEquals(desc.value, undefined);
343assertEquals(1, obj2.foo = 1);
344assertEquals(3, val1);
345assertEquals(4, val2);
346assertEquals(3, obj2.foo);
347
348// Make data - redefine to accessor
349Object.defineProperty(obj2, "bar", dataConfigurable)
350
351// Redefine to accessor property
352Object.defineProperty(obj2, "bar", accessorConfigurable);
353desc = Object.getOwnPropertyDescriptor(obj2, "bar");
354assertTrue(desc.configurable);
355assertFalse(desc.enumerable);
356assertEquals(desc.writable, undefined);
357assertEquals(desc.get, accessorConfigurable.get);
358assertEquals(desc.set, accessorConfigurable.set);
359assertEquals(desc.value, undefined);
360assertEquals(1, obj2.bar = 1);
361assertEquals(4, val1);
362assertEquals(4, val2);
363assertEquals(4, obj2.foo);
364
365// Redefine back to data property
366Object.defineProperty(obj2, "bar", dataConfigurable);
367desc = Object.getOwnPropertyDescriptor(obj2, "bar");
368assertEquals(obj2.bar, 1000);
369assertEquals(desc.value, 1000);
370assertTrue(desc.configurable);
371assertFalse(desc.writable);
372assertFalse(desc.enumerable);
373assertEquals(desc.get, undefined);
374assertEquals(desc.set, undefined);
375
376
377// Redefinition of an accessor defined using __defineGetter__ and
378// __defineSetter__
379function get(){return this.x}
380function set(x){this.x=x};
381
382var obj3 = {x:1000};
383obj3.__defineGetter__("foo", get);
384obj3.__defineSetter__("foo", set);
385
386desc = Object.getOwnPropertyDescriptor(obj3, "foo");
387assertTrue(desc.configurable);
388assertTrue(desc.enumerable);
389assertEquals(desc.writable, undefined);
390assertEquals(desc.get, get);
391assertEquals(desc.set, set);
392assertEquals(desc.value, undefined);
393assertEquals(1, obj3.foo = 1);
394assertEquals(1, obj3.x);
395assertEquals(1, obj3.foo);
396
397// Redefine to accessor property (non configurable) - note that enumerable
398// which we do not redefine should remain the same (true).
399Object.defineProperty(obj3, "foo", accessorNoConfigurable);
400desc = Object.getOwnPropertyDescriptor(obj3, "foo");
401assertFalse(desc.configurable);
402assertTrue(desc.enumerable);
403assertEquals(desc.writable, undefined);
404assertEquals(desc.get, accessorNoConfigurable.get);
405assertEquals(desc.set, accessorNoConfigurable.set);
406assertEquals(desc.value, undefined);
407assertEquals(1, obj3.foo = 1);
408assertEquals(5, val2);
409assertEquals(5, obj3.foo);
410
411
412obj3.__defineGetter__("bar", get);
413obj3.__defineSetter__("bar", set);
414
415
416// Redefine back to data property
417Object.defineProperty(obj3, "bar", dataConfigurable);
418desc = Object.getOwnPropertyDescriptor(obj3, "bar");
419assertEquals(obj3.bar, 1000);
420assertEquals(desc.value, 1000);
421assertTrue(desc.configurable);
422assertFalse(desc.writable);
423assertTrue(desc.enumerable);
424assertEquals(desc.get, undefined);
425assertEquals(desc.set, undefined);
426
427
428var obj4 = {};
429var func = function (){return 42;};
430obj4.bar = func;
431assertEquals(42, obj4.bar());
432
433Object.defineProperty(obj4, "bar", accessorConfigurable);
434desc = Object.getOwnPropertyDescriptor(obj4, "bar");
435assertTrue(desc.configurable);
436assertTrue(desc.enumerable);
437assertEquals(desc.writable, undefined);
438assertEquals(desc.get, accessorConfigurable.get);
439assertEquals(desc.set, accessorConfigurable.set);
440assertEquals(desc.value, undefined);
441assertEquals(1, obj4.bar = 1);
442assertEquals(5, val1);
443assertEquals(5, obj4.bar);
444
445// Make sure an error is thrown when trying to access to redefined function
446try {
447  obj4.bar();
448  assertTrue(false);
449} catch (e) {
450  assertTrue(/is not a function/.test(e));
451}
452
453
454// Test runtime calls to DefineOrRedefineDataProperty and
455// DefineOrRedefineAccessorProperty - make sure we don't
456// crash
457try {
458  %DefineOrRedefineAccessorProperty(0, 0, 0, 0, 0);
459} catch (e) {
460  assertTrue(/illegal access/.test(e));
461}
462
463try {
464  %DefineOrRedefineDataProperty(0, 0, 0, 0);
465} catch (e) {
466  assertTrue(/illegal access/.test(e));
467}
468
469try {
470  %DefineOrRedefineDataProperty(null, null, null, null);
471} catch (e) {
472  assertTrue(/illegal access/.test(e));
473}
474
475try {
476  %DefineOrRedefineAccessorProperty(null, null, null, null, null);
477} catch (e) {
478  assertTrue(/illegal access/.test(e));
479}
480
481try {
482  %DefineOrRedefineDataProperty({}, null, null, null);
483} catch (e) {
484  assertTrue(/illegal access/.test(e));
485}
486
487// Defining properties null should fail even when we have
488// other allowed values
489try {
490  %DefineOrRedefineAccessorProperty(null, 'foo', 0, func, 0);
491} catch (e) {
492  assertTrue(/illegal access/.test(e));
493}
494
495try {
496  %DefineOrRedefineDataProperty(null, 'foo', 0, 0);
497} catch (e) {
498  assertTrue(/illegal access/.test(e));
499}
500