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