Make the runtime entry for setting/changing accessors "atomic".
Previously, there were 1 or 2 calls to the runtime when accessors were changed
or set. This doesn't really work well with property attributes, leading to some
hacks and complicates things even further when trying to share maps in presence
of accessors. Therefore, the runtime entry now takes the full triple (getter,
setter, attributes), where the getter and/or the setter can be null in case they
shouldn't be changed.
For now, we do basically the same on the native side as we did before on the
JavaScript side, but this will change in future CLs, the current CL is already
large enough.
Note that object literals with a getter and a setter for the same property still
do 2 calls, but this is a little bit more tricky to fix and will be handled in a
separate CL.
Review URL: https://chromiumcodereview.appspot.com/9616016
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10956 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-03-07 13:24:44 +00:00
|
|
|
// Copyright 2012 the V8 project authors. All rights reserved.
|
2010-02-03 13:10:03 +00:00
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are
|
|
|
|
// met:
|
|
|
|
//
|
|
|
|
// * Redistributions of source code must retain the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer.
|
|
|
|
// * Redistributions in binary form must reproduce the above
|
|
|
|
// copyright notice, this list of conditions and the following
|
|
|
|
// disclaimer in the documentation and/or other materials provided
|
|
|
|
// with the distribution.
|
|
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
|
|
// contributors may be used to endorse or promote products derived
|
|
|
|
// from this software without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
// Tests the object.defineProperty method - ES 15.2.3.6
|
|
|
|
|
2012-07-04 11:40:51 +00:00
|
|
|
// Flags: --allow-natives-syntax --es5-readonly
|
2010-02-03 13:10:03 +00:00
|
|
|
|
|
|
|
// Check that an exception is thrown when null is passed as object.
|
2011-05-31 08:08:42 +00:00
|
|
|
var exception = false;
|
2010-02-03 13:10:03 +00:00
|
|
|
try {
|
|
|
|
Object.defineProperty(null, null, null);
|
|
|
|
} catch (e) {
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = true;
|
2010-02-03 13:10:03 +00:00
|
|
|
assertTrue(/called on non-object/.test(e));
|
|
|
|
}
|
2011-05-31 08:08:42 +00:00
|
|
|
assertTrue(exception);
|
2010-02-03 13:10:03 +00:00
|
|
|
|
|
|
|
// Check that an exception is thrown when undefined is passed as object.
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = false;
|
2010-02-03 13:10:03 +00:00
|
|
|
try {
|
|
|
|
Object.defineProperty(undefined, undefined, undefined);
|
|
|
|
} catch (e) {
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = true;
|
2010-02-03 13:10:03 +00:00
|
|
|
assertTrue(/called on non-object/.test(e));
|
|
|
|
}
|
2011-05-31 08:08:42 +00:00
|
|
|
assertTrue(exception);
|
2010-02-03 13:10:03 +00:00
|
|
|
|
|
|
|
// Check that an exception is thrown when non-object is passed as object.
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = false;
|
2010-02-03 13:10:03 +00:00
|
|
|
try {
|
|
|
|
Object.defineProperty(0, "foo", undefined);
|
|
|
|
} catch (e) {
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = true;
|
2010-02-03 13:10:03 +00:00
|
|
|
assertTrue(/called on non-object/.test(e));
|
|
|
|
}
|
2011-05-31 08:08:42 +00:00
|
|
|
assertTrue(exception);
|
2010-02-03 13:10:03 +00:00
|
|
|
|
2010-05-25 06:25:27 +00:00
|
|
|
// Object.
|
2010-02-03 13:10:03 +00:00
|
|
|
var obj1 = {};
|
|
|
|
|
2010-05-25 06:25:27 +00:00
|
|
|
// Values.
|
2010-02-03 13:10:03 +00:00
|
|
|
var val1 = 0;
|
|
|
|
var val2 = 0;
|
|
|
|
var val3 = 0;
|
|
|
|
|
2010-05-25 06:25:27 +00:00
|
|
|
function setter1() {val1++; }
|
|
|
|
function getter1() {return val1; }
|
|
|
|
|
|
|
|
function setter2() {val2++; }
|
|
|
|
function getter2() {return val2; }
|
|
|
|
|
|
|
|
function setter3() {val3++; }
|
|
|
|
function getter3() {return val3; }
|
|
|
|
|
|
|
|
|
|
|
|
// Descriptors.
|
2010-02-03 13:10:03 +00:00
|
|
|
var emptyDesc = {};
|
|
|
|
|
2010-12-16 12:21:08 +00:00
|
|
|
var accessorConfigurable = {
|
2010-05-25 06:25:27 +00:00
|
|
|
set: setter1,
|
|
|
|
get: getter1,
|
2010-02-03 13:10:03 +00:00
|
|
|
configurable: true
|
|
|
|
};
|
|
|
|
|
|
|
|
var accessorNoConfigurable = {
|
2010-05-25 06:25:27 +00:00
|
|
|
set: setter2,
|
|
|
|
get: getter2,
|
2010-12-16 12:21:08 +00:00
|
|
|
configurable: false
|
2010-02-03 13:10:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
var accessorOnlySet = {
|
2010-05-25 06:25:27 +00:00
|
|
|
set: setter3,
|
2010-02-03 13:10:03 +00:00
|
|
|
configurable: true
|
|
|
|
};
|
|
|
|
|
|
|
|
var accessorOnlyGet = {
|
2010-05-25 06:25:27 +00:00
|
|
|
get: getter3,
|
2010-02-03 13:10:03 +00:00
|
|
|
configurable: true
|
|
|
|
};
|
|
|
|
|
|
|
|
var accessorDefault = {set: function(){} };
|
|
|
|
|
|
|
|
var dataConfigurable = { value: 1000, configurable: true };
|
|
|
|
|
|
|
|
var dataNoConfigurable = { value: 2000, configurable: false };
|
|
|
|
|
|
|
|
var dataWritable = { value: 3000, writable: true};
|
|
|
|
|
|
|
|
|
|
|
|
// Check that we can't add property with undefined attributes.
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj1, "foo", undefined);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/must be an object/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure that we can add a property with an empty descriptor and
|
|
|
|
// that it has the default descriptor values.
|
|
|
|
Object.defineProperty(obj1, "foo", emptyDesc);
|
|
|
|
|
|
|
|
// foo should be undefined as it has no get, set or value
|
|
|
|
assertEquals(undefined, obj1.foo);
|
|
|
|
|
|
|
|
// We should, however, be able to retrieve the propertydescriptor which should
|
|
|
|
// have all default values (according to 8.6.1).
|
|
|
|
var desc = Object.getOwnPropertyDescriptor(obj1, "foo");
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertEquals(desc.get, undefined);
|
|
|
|
assertEquals(desc.set, undefined);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
|
|
|
|
// Make sure that getOwnPropertyDescriptor does not return a descriptor
|
|
|
|
// with default values if called with non existing property (otherwise
|
|
|
|
// the test above is invalid).
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "bar");
|
|
|
|
assertEquals(desc, undefined);
|
|
|
|
|
|
|
|
// Make sure that foo can't be reset (as configurable is false).
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj1, "foo", accessorConfigurable);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Accessor properties
|
|
|
|
|
|
|
|
Object.defineProperty(obj1, "bar", accessorConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "bar");
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.get, accessorConfigurable.get);
|
|
|
|
assertEquals(desc.set, accessorConfigurable.set);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(1, obj1.bar = 1);
|
|
|
|
assertEquals(1, val1);
|
|
|
|
assertEquals(1, obj1.bar = 1);
|
|
|
|
assertEquals(2, val1);
|
|
|
|
assertEquals(2, obj1.bar);
|
|
|
|
|
|
|
|
// Redefine bar with non configurable test
|
|
|
|
Object.defineProperty(obj1, "bar", accessorNoConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "bar");
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.get, accessorNoConfigurable.get);
|
|
|
|
assertEquals(desc.set, accessorNoConfigurable.set);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(1, obj1.bar = 1);
|
|
|
|
assertEquals(2, val1);
|
|
|
|
assertEquals(1, val2);
|
|
|
|
assertEquals(1, obj1.bar = 1)
|
|
|
|
assertEquals(2, val1);
|
|
|
|
assertEquals(2, val2);
|
|
|
|
assertEquals(2, obj1.bar);
|
|
|
|
|
|
|
|
// Try to redefine bar again - should fail as configurable is false.
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj1, "bar", accessorConfigurable);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch(e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to redefine bar again using the data descriptor - should fail.
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj1, "bar", dataConfigurable);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch(e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Redefine using same descriptor - should succeed.
|
|
|
|
Object.defineProperty(obj1, "bar", accessorNoConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "bar");
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.get, accessorNoConfigurable.get);
|
|
|
|
assertEquals(desc.set, accessorNoConfigurable.set);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(1, obj1.bar = 1);
|
|
|
|
assertEquals(2, val1);
|
|
|
|
assertEquals(3, val2);
|
|
|
|
assertEquals(1, obj1.bar = 1)
|
|
|
|
assertEquals(2, val1);
|
|
|
|
assertEquals(4, val2);
|
|
|
|
assertEquals(4, obj1.bar);
|
|
|
|
|
2010-05-25 06:25:27 +00:00
|
|
|
// Define an accessor that has only a setter.
|
2010-02-03 13:10:03 +00:00
|
|
|
Object.defineProperty(obj1, "setOnly", accessorOnlySet);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "setOnly");
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.set, accessorOnlySet.set);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(desc.get, undefined);
|
|
|
|
assertEquals(1, obj1.setOnly = 1);
|
|
|
|
assertEquals(1, val3);
|
|
|
|
|
2010-05-25 06:25:27 +00:00
|
|
|
// Add a getter - should not touch the setter.
|
2010-02-03 13:10:03 +00:00
|
|
|
Object.defineProperty(obj1, "setOnly", accessorOnlyGet);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "setOnly");
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.get, accessorOnlyGet.get);
|
|
|
|
assertEquals(desc.set, accessorOnlySet.set);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(1, obj1.setOnly = 1);
|
|
|
|
assertEquals(2, val3);
|
|
|
|
|
2010-12-16 12:21:08 +00:00
|
|
|
// The above should also work if redefining just a getter or setter on
|
2010-02-03 13:10:03 +00:00
|
|
|
// an existing property with both a getter and a setter.
|
|
|
|
Object.defineProperty(obj1, "both", accessorConfigurable);
|
|
|
|
|
|
|
|
Object.defineProperty(obj1, "both", accessorOnlySet);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "both");
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.set, accessorOnlySet.set);
|
|
|
|
assertEquals(desc.get, accessorConfigurable.get);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(1, obj1.both = 1);
|
|
|
|
assertEquals(3, val3);
|
|
|
|
|
|
|
|
|
|
|
|
// Data properties
|
|
|
|
|
|
|
|
Object.defineProperty(obj1, "foobar", dataConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
|
|
|
|
assertEquals(obj1.foobar, 1000);
|
|
|
|
assertEquals(desc.value, 1000);
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.get, undefined);
|
|
|
|
assertEquals(desc.set, undefined);
|
|
|
|
//Try writing to non writable attribute - should remain 1000
|
|
|
|
obj1.foobar = 1001;
|
|
|
|
assertEquals(obj1.foobar, 1000);
|
|
|
|
|
|
|
|
|
2010-05-25 06:25:27 +00:00
|
|
|
// Redefine to writable descriptor - now writing to foobar should be allowed.
|
2010-02-03 13:10:03 +00:00
|
|
|
Object.defineProperty(obj1, "foobar", dataWritable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
|
|
|
|
assertEquals(obj1.foobar, 3000);
|
|
|
|
assertEquals(desc.value, 3000);
|
|
|
|
// Note that since dataWritable does not define configurable the configurable
|
|
|
|
// setting from the redefined property (in this case true) is used.
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertTrue(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.get, undefined);
|
|
|
|
assertEquals(desc.set, undefined);
|
|
|
|
// Writing to the property should now be allowed
|
|
|
|
obj1.foobar = 1001;
|
|
|
|
assertEquals(obj1.foobar, 1001);
|
|
|
|
|
|
|
|
|
|
|
|
// Redefine with non configurable data property.
|
|
|
|
Object.defineProperty(obj1, "foobar", dataNoConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
|
|
|
|
assertEquals(obj1.foobar, 2000);
|
|
|
|
assertEquals(desc.value, 2000);
|
|
|
|
assertFalse(desc.configurable);
|
2010-05-26 08:31:57 +00:00
|
|
|
assertTrue(desc.writable);
|
2010-02-03 13:10:03 +00:00
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.get, undefined);
|
|
|
|
assertEquals(desc.set, undefined);
|
|
|
|
|
|
|
|
// Try redefine again - shold fail because configurable is now false.
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj1, "foobar", dataConfigurable);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try redefine again with accessor property - shold also fail.
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj1, "foobar", dataConfigurable);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Redifine with the same descriptor - should succeed (step 6).
|
|
|
|
Object.defineProperty(obj1, "foobar", dataNoConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
|
|
|
|
assertEquals(obj1.foobar, 2000);
|
|
|
|
assertEquals(desc.value, 2000);
|
|
|
|
assertFalse(desc.configurable);
|
2010-05-26 08:31:57 +00:00
|
|
|
assertTrue(desc.writable);
|
2010-02-03 13:10:03 +00:00
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.get, undefined);
|
|
|
|
assertEquals(desc.set, undefined);
|
|
|
|
|
|
|
|
|
|
|
|
// New object
|
|
|
|
var obj2 = {};
|
|
|
|
|
|
|
|
// Make accessor - redefine to data
|
|
|
|
Object.defineProperty(obj2, "foo", accessorConfigurable);
|
|
|
|
|
|
|
|
// Redefine to data property
|
|
|
|
Object.defineProperty(obj2, "foo", dataConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj2, "foo");
|
|
|
|
assertEquals(obj2.foo, 1000);
|
|
|
|
assertEquals(desc.value, 1000);
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.get, undefined);
|
|
|
|
assertEquals(desc.set, undefined);
|
|
|
|
|
|
|
|
|
|
|
|
// Redefine back to accessor
|
|
|
|
Object.defineProperty(obj2, "foo", accessorConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj2, "foo");
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.get, accessorConfigurable.get);
|
|
|
|
assertEquals(desc.set, accessorConfigurable.set);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(1, obj2.foo = 1);
|
|
|
|
assertEquals(3, val1);
|
|
|
|
assertEquals(4, val2);
|
|
|
|
assertEquals(3, obj2.foo);
|
|
|
|
|
|
|
|
// Make data - redefine to accessor
|
|
|
|
Object.defineProperty(obj2, "bar", dataConfigurable)
|
|
|
|
|
|
|
|
// Redefine to accessor property
|
|
|
|
Object.defineProperty(obj2, "bar", accessorConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj2, "bar");
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.get, accessorConfigurable.get);
|
|
|
|
assertEquals(desc.set, accessorConfigurable.set);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(1, obj2.bar = 1);
|
|
|
|
assertEquals(4, val1);
|
|
|
|
assertEquals(4, val2);
|
|
|
|
assertEquals(4, obj2.foo);
|
|
|
|
|
|
|
|
// Redefine back to data property
|
|
|
|
Object.defineProperty(obj2, "bar", dataConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj2, "bar");
|
|
|
|
assertEquals(obj2.bar, 1000);
|
|
|
|
assertEquals(desc.value, 1000);
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertEquals(desc.get, undefined);
|
|
|
|
assertEquals(desc.set, undefined);
|
|
|
|
|
|
|
|
|
2010-12-16 12:21:08 +00:00
|
|
|
// Redefinition of an accessor defined using __defineGetter__ and
|
2010-05-25 06:25:27 +00:00
|
|
|
// __defineSetter__.
|
2010-02-03 13:10:03 +00:00
|
|
|
function get(){return this.x}
|
|
|
|
function set(x){this.x=x};
|
|
|
|
|
|
|
|
var obj3 = {x:1000};
|
|
|
|
obj3.__defineGetter__("foo", get);
|
|
|
|
obj3.__defineSetter__("foo", set);
|
|
|
|
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj3, "foo");
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.get, get);
|
|
|
|
assertEquals(desc.set, set);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(1, obj3.foo = 1);
|
|
|
|
assertEquals(1, obj3.x);
|
|
|
|
assertEquals(1, obj3.foo);
|
|
|
|
|
|
|
|
// Redefine to accessor property (non configurable) - note that enumerable
|
|
|
|
// which we do not redefine should remain the same (true).
|
|
|
|
Object.defineProperty(obj3, "foo", accessorNoConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj3, "foo");
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.get, accessorNoConfigurable.get);
|
|
|
|
assertEquals(desc.set, accessorNoConfigurable.set);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(1, obj3.foo = 1);
|
|
|
|
assertEquals(5, val2);
|
|
|
|
assertEquals(5, obj3.foo);
|
|
|
|
|
|
|
|
|
|
|
|
obj3.__defineGetter__("bar", get);
|
|
|
|
obj3.__defineSetter__("bar", set);
|
|
|
|
|
|
|
|
|
|
|
|
// Redefine back to data property
|
|
|
|
Object.defineProperty(obj3, "bar", dataConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj3, "bar");
|
|
|
|
assertEquals(obj3.bar, 1000);
|
|
|
|
assertEquals(desc.value, 1000);
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertEquals(desc.get, undefined);
|
|
|
|
assertEquals(desc.set, undefined);
|
|
|
|
|
|
|
|
|
|
|
|
var obj4 = {};
|
|
|
|
var func = function (){return 42;};
|
|
|
|
obj4.bar = func;
|
|
|
|
assertEquals(42, obj4.bar());
|
|
|
|
|
|
|
|
Object.defineProperty(obj4, "bar", accessorConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj4, "bar");
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertEquals(desc.writable, undefined);
|
|
|
|
assertEquals(desc.get, accessorConfigurable.get);
|
|
|
|
assertEquals(desc.set, accessorConfigurable.set);
|
|
|
|
assertEquals(desc.value, undefined);
|
|
|
|
assertEquals(1, obj4.bar = 1);
|
|
|
|
assertEquals(5, val1);
|
|
|
|
assertEquals(5, obj4.bar);
|
|
|
|
|
2010-05-25 06:25:27 +00:00
|
|
|
// Make sure an error is thrown when trying to access to redefined function.
|
2010-02-03 13:10:03 +00:00
|
|
|
try {
|
|
|
|
obj4.bar();
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/is not a function/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-04 19:43:56 +00:00
|
|
|
// Test runtime calls to DefineOrRedefineDataProperty and
|
2010-12-16 12:21:08 +00:00
|
|
|
// DefineOrRedefineAccessorProperty - make sure we don't
|
2010-05-25 06:25:27 +00:00
|
|
|
// crash.
|
2010-02-04 19:43:56 +00:00
|
|
|
try {
|
|
|
|
%DefineOrRedefineAccessorProperty(0, 0, 0, 0, 0);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/illegal access/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
%DefineOrRedefineDataProperty(0, 0, 0, 0);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/illegal access/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
%DefineOrRedefineDataProperty(null, null, null, null);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/illegal access/.test(e));
|
|
|
|
}
|
2010-02-03 13:10:03 +00:00
|
|
|
|
2010-02-04 19:43:56 +00:00
|
|
|
try {
|
|
|
|
%DefineOrRedefineAccessorProperty(null, null, null, null, null);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/illegal access/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
%DefineOrRedefineDataProperty({}, null, null, null);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/illegal access/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Defining properties null should fail even when we have
|
|
|
|
// other allowed values
|
|
|
|
try {
|
Make the runtime entry for setting/changing accessors "atomic".
Previously, there were 1 or 2 calls to the runtime when accessors were changed
or set. This doesn't really work well with property attributes, leading to some
hacks and complicates things even further when trying to share maps in presence
of accessors. Therefore, the runtime entry now takes the full triple (getter,
setter, attributes), where the getter and/or the setter can be null in case they
shouldn't be changed.
For now, we do basically the same on the native side as we did before on the
JavaScript side, but this will change in future CLs, the current CL is already
large enough.
Note that object literals with a getter and a setter for the same property still
do 2 calls, but this is a little bit more tricky to fix and will be handled in a
separate CL.
Review URL: https://chromiumcodereview.appspot.com/9616016
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10956 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-03-07 13:24:44 +00:00
|
|
|
%DefineOrRedefineAccessorProperty(null, 'foo', func, null, 0);
|
2010-02-04 19:43:56 +00:00
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/illegal access/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
%DefineOrRedefineDataProperty(null, 'foo', 0, 0);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/illegal access/.test(e));
|
|
|
|
}
|
2010-05-25 06:25:27 +00:00
|
|
|
|
|
|
|
// Test that all possible differences in step 6 in DefineOwnProperty are
|
|
|
|
// exercised, i.e., any difference in the given property descriptor and the
|
|
|
|
// existing properties should not return true, but throw an error if the
|
2010-12-16 12:21:08 +00:00
|
|
|
// existing configurable property is false.
|
2010-05-25 06:25:27 +00:00
|
|
|
|
|
|
|
var obj5 = {};
|
|
|
|
// Enumerable will default to false.
|
|
|
|
Object.defineProperty(obj5, 'foo', accessorNoConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj5, 'foo');
|
|
|
|
// First, test that we are actually allowed to set the accessor if all
|
|
|
|
// values are of the descriptor are the same as the existing one.
|
|
|
|
Object.defineProperty(obj5, 'foo', accessorNoConfigurable);
|
|
|
|
|
|
|
|
// Different setter.
|
|
|
|
var descDifferent = {
|
|
|
|
configurable:false,
|
|
|
|
enumerable:false,
|
|
|
|
set: setter1,
|
|
|
|
get: getter2
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj5, 'foo', descDifferent);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Different getter.
|
|
|
|
descDifferent = {
|
|
|
|
configurable:false,
|
|
|
|
enumerable:false,
|
|
|
|
set: setter2,
|
|
|
|
get: getter1
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj5, 'foo', descDifferent);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Different enumerable.
|
|
|
|
descDifferent = {
|
|
|
|
configurable:false,
|
|
|
|
enumerable:true,
|
|
|
|
set: setter2,
|
|
|
|
get: getter2
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj5, 'foo', descDifferent);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Different configurable.
|
|
|
|
descDifferent = {
|
|
|
|
configurable:false,
|
|
|
|
enumerable:true,
|
|
|
|
set: setter2,
|
|
|
|
get: getter2
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj5, 'foo', descDifferent);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// No difference.
|
|
|
|
descDifferent = {
|
|
|
|
configurable:false,
|
|
|
|
enumerable:false,
|
|
|
|
set: setter2,
|
|
|
|
get: getter2
|
|
|
|
};
|
|
|
|
// Make sure we can still redefine if all properties are the same.
|
|
|
|
Object.defineProperty(obj5, 'foo', descDifferent);
|
|
|
|
|
|
|
|
// Make sure that obj5 still holds the original values.
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj5, 'foo');
|
|
|
|
assertEquals(desc.get, getter2);
|
|
|
|
assertEquals(desc.set, setter2);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
|
|
|
|
|
|
|
|
// Also exercise step 6 on data property, writable and enumerable
|
|
|
|
// defaults to false.
|
|
|
|
Object.defineProperty(obj5, 'bar', dataNoConfigurable);
|
|
|
|
|
|
|
|
// Test that redefinition with the same property descriptor is possible
|
|
|
|
Object.defineProperty(obj5, 'bar', dataNoConfigurable);
|
|
|
|
|
|
|
|
// Different value.
|
|
|
|
descDifferent = {
|
|
|
|
configurable:false,
|
|
|
|
enumerable:false,
|
|
|
|
writable: false,
|
|
|
|
value: 1999
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj5, 'bar', descDifferent);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Different writable.
|
|
|
|
descDifferent = {
|
|
|
|
configurable:false,
|
|
|
|
enumerable:false,
|
|
|
|
writable: true,
|
|
|
|
value: 2000
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj5, 'bar', descDifferent);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Different enumerable.
|
|
|
|
descDifferent = {
|
|
|
|
configurable:false,
|
|
|
|
enumerable:true ,
|
|
|
|
writable:false,
|
|
|
|
value: 2000
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj5, 'bar', descDifferent);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Different configurable.
|
|
|
|
descDifferent = {
|
|
|
|
configurable:true,
|
|
|
|
enumerable:false,
|
|
|
|
writable:false,
|
|
|
|
value: 2000
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
Object.defineProperty(obj5, 'bar', descDifferent);
|
|
|
|
assertTrue(false);
|
|
|
|
} catch (e) {
|
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
// No difference.
|
|
|
|
descDifferent = {
|
|
|
|
configurable:false,
|
|
|
|
enumerable:false,
|
|
|
|
writable:false,
|
|
|
|
value:2000
|
|
|
|
};
|
|
|
|
// Make sure we can still redefine if all properties are the same.
|
|
|
|
Object.defineProperty(obj5, 'bar', descDifferent);
|
|
|
|
|
|
|
|
// Make sure that obj5 still holds the original values.
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj5, 'bar');
|
|
|
|
assertEquals(desc.value, 2000);
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure that we can't overwrite +0 with -0 and vice versa.
|
|
|
|
var descMinusZero = {value: -0, configurable: false};
|
|
|
|
var descPlusZero = {value: +0, configurable: false};
|
|
|
|
|
|
|
|
Object.defineProperty(obj5, 'minuszero', descMinusZero);
|
|
|
|
|
|
|
|
// Make sure we can redefine with -0.
|
|
|
|
Object.defineProperty(obj5, 'minuszero', descMinusZero);
|
|
|
|
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = false;
|
2010-05-25 06:25:27 +00:00
|
|
|
try {
|
|
|
|
Object.defineProperty(obj5, 'minuszero', descPlusZero);
|
|
|
|
} catch (e) {
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = true;
|
2010-05-25 06:25:27 +00:00
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
2011-05-31 08:08:42 +00:00
|
|
|
assertTrue(exception);
|
2010-05-25 06:25:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(obj5, 'pluszero', descPlusZero);
|
|
|
|
|
|
|
|
// Make sure we can redefine with +0.
|
|
|
|
Object.defineProperty(obj5, 'pluszero', descPlusZero);
|
|
|
|
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = false;
|
2010-05-25 06:25:27 +00:00
|
|
|
try {
|
|
|
|
Object.defineProperty(obj5, 'pluszero', descMinusZero);
|
|
|
|
} catch (e) {
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = true;
|
2010-05-25 06:25:27 +00:00
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
2011-05-31 08:08:42 +00:00
|
|
|
assertTrue(exception);
|
2010-06-14 13:55:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
var obj6 = {};
|
|
|
|
obj6[1] = 'foo';
|
|
|
|
obj6[2] = 'bar';
|
|
|
|
obj6[3] = '42';
|
|
|
|
obj6[4] = '43';
|
|
|
|
obj6[5] = '44';
|
|
|
|
|
|
|
|
var descElement = { value: 'foobar' };
|
|
|
|
var descElementNonConfigurable = { value: 'barfoo', configurable: false };
|
|
|
|
var descElementNonWritable = { value: 'foofoo', writable: false };
|
|
|
|
var descElementNonEnumerable = { value: 'barbar', enumerable: false };
|
2010-12-16 12:21:08 +00:00
|
|
|
var descElementAllFalse = { value: 'foofalse',
|
2010-06-14 13:55:38 +00:00
|
|
|
configurable: false,
|
|
|
|
writable: false,
|
|
|
|
enumerable: false };
|
|
|
|
|
|
|
|
|
|
|
|
// Redefine existing property.
|
|
|
|
Object.defineProperty(obj6, '1', descElement);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj6, '1');
|
|
|
|
assertEquals(desc.value, 'foobar');
|
|
|
|
assertTrue(desc.writable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
|
|
|
|
// Redefine existing property with configurable: false.
|
|
|
|
Object.defineProperty(obj6, '2', descElementNonConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj6, '2');
|
|
|
|
assertEquals(desc.value, 'barfoo');
|
|
|
|
assertTrue(desc.writable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
|
2011-02-03 19:29:10 +00:00
|
|
|
// Can use defineProperty to change the value of a non
|
|
|
|
// configurable property.
|
2010-06-14 13:55:38 +00:00
|
|
|
try {
|
|
|
|
Object.defineProperty(obj6, '2', descElement);
|
2011-02-03 19:29:10 +00:00
|
|
|
desc = Object.getOwnPropertyDescriptor(obj6, '2');
|
|
|
|
assertEquals(desc.value, 'foobar');
|
|
|
|
} catch (e) {
|
|
|
|
assertUnreachable();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that we can't change the descriptor of a
|
|
|
|
// non configurable property.
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = false;
|
2011-02-03 19:29:10 +00:00
|
|
|
try {
|
|
|
|
var descAccessor = { get: function() { return 0; } };
|
|
|
|
Object.defineProperty(obj6, '2', descAccessor);
|
2010-06-14 13:55:38 +00:00
|
|
|
} catch (e) {
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = true;
|
2010-06-14 13:55:38 +00:00
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
2011-05-31 08:08:42 +00:00
|
|
|
assertTrue(exception);
|
2010-06-14 13:55:38 +00:00
|
|
|
|
2011-02-03 19:29:10 +00:00
|
|
|
Object.defineProperty(obj6, '2', descElementNonWritable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj6, '2');
|
|
|
|
assertEquals(desc.value, 'foofoo');
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
|
2010-06-14 13:55:38 +00:00
|
|
|
Object.defineProperty(obj6, '3', descElementNonWritable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj6, '3');
|
|
|
|
assertEquals(desc.value, 'foofoo');
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
|
|
|
|
// Redefine existing property with configurable: false.
|
|
|
|
Object.defineProperty(obj6, '4', descElementNonEnumerable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj6, '4');
|
|
|
|
assertEquals(desc.value, 'barbar');
|
|
|
|
assertTrue(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
|
|
|
|
// Redefine existing property with configurable: false.
|
|
|
|
Object.defineProperty(obj6, '5', descElementAllFalse);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj6, '5');
|
|
|
|
assertEquals(desc.value, 'foofalse');
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
|
|
|
|
// Define non existing property - all attributes should default to false.
|
|
|
|
Object.defineProperty(obj6, '15', descElement);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj6, '15');
|
|
|
|
assertEquals(desc.value, 'foobar');
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
|
|
|
|
// Make sure that we can't redefine using direct access.
|
|
|
|
obj6[15] ='overwrite';
|
2010-12-16 12:21:08 +00:00
|
|
|
assertEquals(obj6[15],'foobar');
|
2010-06-14 13:55:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Repeat the above tests on an array.
|
|
|
|
var arr = new Array();
|
|
|
|
arr[1] = 'foo';
|
|
|
|
arr[2] = 'bar';
|
|
|
|
arr[3] = '42';
|
|
|
|
arr[4] = '43';
|
|
|
|
arr[5] = '44';
|
|
|
|
|
|
|
|
var descElement = { value: 'foobar' };
|
|
|
|
var descElementNonConfigurable = { value: 'barfoo', configurable: false };
|
|
|
|
var descElementNonWritable = { value: 'foofoo', writable: false };
|
|
|
|
var descElementNonEnumerable = { value: 'barbar', enumerable: false };
|
2010-12-16 12:21:08 +00:00
|
|
|
var descElementAllFalse = { value: 'foofalse',
|
2010-06-14 13:55:38 +00:00
|
|
|
configurable: false,
|
|
|
|
writable: false,
|
|
|
|
enumerable: false };
|
|
|
|
|
|
|
|
|
|
|
|
// Redefine existing property.
|
|
|
|
Object.defineProperty(arr, '1', descElement);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(arr, '1');
|
|
|
|
assertEquals(desc.value, 'foobar');
|
|
|
|
assertTrue(desc.writable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
|
|
|
|
// Redefine existing property with configurable: false.
|
|
|
|
Object.defineProperty(arr, '2', descElementNonConfigurable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(arr, '2');
|
|
|
|
assertEquals(desc.value, 'barfoo');
|
|
|
|
assertTrue(desc.writable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
|
2011-02-03 19:29:10 +00:00
|
|
|
// Can use defineProperty to change the value of a non
|
|
|
|
// configurable property of an array.
|
2010-06-14 13:55:38 +00:00
|
|
|
try {
|
|
|
|
Object.defineProperty(arr, '2', descElement);
|
2011-02-03 19:29:10 +00:00
|
|
|
desc = Object.getOwnPropertyDescriptor(arr, '2');
|
|
|
|
assertEquals(desc.value, 'foobar');
|
|
|
|
} catch (e) {
|
|
|
|
assertUnreachable();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that we can't change the descriptor of a
|
|
|
|
// non configurable property.
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = false;
|
2011-02-03 19:29:10 +00:00
|
|
|
try {
|
|
|
|
var descAccessor = { get: function() { return 0; } };
|
|
|
|
Object.defineProperty(arr, '2', descAccessor);
|
2010-06-14 13:55:38 +00:00
|
|
|
} catch (e) {
|
2011-05-31 08:08:42 +00:00
|
|
|
exception = true;
|
2010-06-14 13:55:38 +00:00
|
|
|
assertTrue(/Cannot redefine property/.test(e));
|
|
|
|
}
|
2011-05-31 08:08:42 +00:00
|
|
|
assertTrue(exception);
|
2010-06-14 13:55:38 +00:00
|
|
|
|
2011-02-03 19:29:10 +00:00
|
|
|
Object.defineProperty(arr, '2', descElementNonWritable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(arr, '2');
|
|
|
|
assertEquals(desc.value, 'foofoo');
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
|
2010-06-14 13:55:38 +00:00
|
|
|
Object.defineProperty(arr, '3', descElementNonWritable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(arr, '3');
|
|
|
|
assertEquals(desc.value, 'foofoo');
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertTrue(desc.enumerable);
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
|
|
|
|
// Redefine existing property with configurable: false.
|
|
|
|
Object.defineProperty(arr, '4', descElementNonEnumerable);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(arr, '4');
|
|
|
|
assertEquals(desc.value, 'barbar');
|
|
|
|
assertTrue(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertTrue(desc.configurable);
|
|
|
|
|
|
|
|
// Redefine existing property with configurable: false.
|
|
|
|
Object.defineProperty(arr, '5', descElementAllFalse);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(arr, '5');
|
|
|
|
assertEquals(desc.value, 'foofalse');
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
|
|
|
|
// Define non existing property - all attributes should default to false.
|
|
|
|
Object.defineProperty(arr, '15', descElement);
|
|
|
|
desc = Object.getOwnPropertyDescriptor(arr, '15');
|
|
|
|
assertEquals(desc.value, 'foobar');
|
|
|
|
assertFalse(desc.writable);
|
|
|
|
assertFalse(desc.enumerable);
|
|
|
|
assertFalse(desc.configurable);
|
|
|
|
|
2010-12-10 11:27:15 +00:00
|
|
|
// See issue 968: http://code.google.com/p/v8/issues/detail?id=968
|
|
|
|
var o = { x : 42 };
|
|
|
|
Object.defineProperty(o, "x", { writable: false });
|
|
|
|
assertEquals(42, o.x);
|
|
|
|
o.x = 37;
|
|
|
|
assertEquals(42, o.x);
|
|
|
|
|
|
|
|
o = { x : 42 };
|
|
|
|
Object.defineProperty(o, "x", {});
|
|
|
|
assertEquals(42, o.x);
|
|
|
|
o.x = 37;
|
|
|
|
// Writability is preserved.
|
|
|
|
assertEquals(37, o.x);
|
|
|
|
|
|
|
|
var o = { };
|
|
|
|
Object.defineProperty(o, "x", { writable: false });
|
|
|
|
assertEquals(undefined, o.x);
|
|
|
|
o.x = 37;
|
|
|
|
assertEquals(undefined, o.x);
|
|
|
|
|
|
|
|
o = { get x() { return 87; } };
|
|
|
|
Object.defineProperty(o, "x", { writable: false });
|
|
|
|
assertEquals(undefined, o.x);
|
|
|
|
o.x = 37;
|
|
|
|
assertEquals(undefined, o.x);
|
|
|
|
|
|
|
|
// Ignore inherited properties.
|
|
|
|
o = { __proto__ : { x : 87 } };
|
|
|
|
Object.defineProperty(o, "x", { writable: false });
|
|
|
|
assertEquals(undefined, o.x);
|
|
|
|
o.x = 37;
|
|
|
|
assertEquals(undefined, o.x);
|
2011-02-03 19:29:10 +00:00
|
|
|
|
|
|
|
function testDefineProperty(obj, propertyName, desc, resultDesc) {
|
|
|
|
Object.defineProperty(obj, propertyName, desc);
|
|
|
|
var actualDesc = Object.getOwnPropertyDescriptor(obj, propertyName);
|
|
|
|
assertEquals(resultDesc.enumerable, actualDesc.enumerable);
|
|
|
|
assertEquals(resultDesc.configurable, actualDesc.configurable);
|
|
|
|
if (resultDesc.hasOwnProperty('value')) {
|
|
|
|
assertEquals(resultDesc.value, actualDesc.value);
|
|
|
|
assertEquals(resultDesc.writable, actualDesc.writable);
|
|
|
|
assertFalse(resultDesc.hasOwnProperty('get'));
|
|
|
|
assertFalse(resultDesc.hasOwnProperty('set'));
|
|
|
|
} else {
|
|
|
|
assertEquals(resultDesc.get, actualDesc.get);
|
|
|
|
assertEquals(resultDesc.set, actualDesc.set);
|
|
|
|
assertFalse(resultDesc.hasOwnProperty('value'));
|
|
|
|
assertFalse(resultDesc.hasOwnProperty('writable'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// tests redefining existing property with a generic descriptor
|
|
|
|
o = { p : 42 };
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ },
|
|
|
|
{ value : 42, writable : true, enumerable : true, configurable : true });
|
|
|
|
|
|
|
|
o = { p : 42 };
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ enumerable : true },
|
|
|
|
{ value : 42, writable : true, enumerable : true, configurable : true });
|
|
|
|
|
|
|
|
o = { p : 42 };
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ configurable : true },
|
|
|
|
{ value : 42, writable : true, enumerable : true, configurable : true });
|
|
|
|
|
|
|
|
o = { p : 42 };
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ enumerable : false },
|
|
|
|
{ value : 42, writable : true, enumerable : false, configurable : true });
|
|
|
|
|
|
|
|
o = { p : 42 };
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ configurable : false },
|
|
|
|
{ value : 42, writable : true, enumerable : true, configurable : false });
|
|
|
|
|
|
|
|
o = { p : 42 };
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ enumerable : true, configurable : true },
|
|
|
|
{ value : 42, writable : true, enumerable : true, configurable : true });
|
|
|
|
|
|
|
|
o = { p : 42 };
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ enumerable : false, configurable : true },
|
|
|
|
{ value : 42, writable : true, enumerable : false, configurable : true });
|
|
|
|
|
|
|
|
o = { p : 42 };
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ enumerable : true, configurable : false },
|
|
|
|
{ value : 42, writable : true, enumerable : true, configurable : false });
|
|
|
|
|
|
|
|
o = { p : 42 };
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ enumerable : false, configurable : false },
|
|
|
|
{ value : 42, writable : true, enumerable : false, configurable : false });
|
|
|
|
|
|
|
|
// can make a writable, non-configurable field non-writable
|
|
|
|
o = { p : 42 };
|
|
|
|
Object.defineProperty(o, 'p', { configurable: false });
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ writable: false },
|
|
|
|
{ value : 42, writable : false, enumerable : true, configurable : false });
|
|
|
|
|
|
|
|
// redefine of get only property with generic descriptor
|
|
|
|
o = {};
|
|
|
|
Object.defineProperty(o, 'p',
|
|
|
|
{ get : getter1, enumerable: true, configurable: true });
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ enumerable : false, configurable : false },
|
|
|
|
{ get: getter1, set: undefined, enumerable : false, configurable : false });
|
|
|
|
|
|
|
|
// redefine of get/set only property with generic descriptor
|
|
|
|
o = {};
|
|
|
|
Object.defineProperty(o, 'p',
|
|
|
|
{ get: getter1, set: setter1, enumerable: true, configurable: true });
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ enumerable : false, configurable : false },
|
|
|
|
{ get: getter1, set: setter1, enumerable : false, configurable : false });
|
|
|
|
|
|
|
|
// redefine of set only property with generic descriptor
|
|
|
|
o = {};
|
|
|
|
Object.defineProperty(o, 'p',
|
|
|
|
{ set : setter1, enumerable: true, configurable: true });
|
|
|
|
testDefineProperty(o, 'p',
|
|
|
|
{ enumerable : false, configurable : false },
|
|
|
|
{ get: undefined, set: setter1, enumerable : false, configurable : false });
|
2011-06-08 08:13:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Regression test: Ensure that growing dictionaries are not ignored.
|
|
|
|
o = {};
|
|
|
|
for (var i = 0; i < 1000; i++) {
|
|
|
|
// Non-enumerable property forces dictionary mode.
|
|
|
|
Object.defineProperty(o, i, {value: i, enumerable: false});
|
|
|
|
}
|
2012-02-24 14:34:01 +00:00
|
|
|
assertEquals(999, o[999]);
|
|
|
|
|
|
|
|
|
|
|
|
// Regression test: Bizzare behavior on non-strict arguments object.
|
2012-09-20 11:28:33 +00:00
|
|
|
// TODO(yangguo): Tests disabled, needs investigation!
|
|
|
|
/*
|
2012-02-24 14:34:01 +00:00
|
|
|
(function test(arg0) {
|
|
|
|
// Here arguments[0] is a fast alias on arg0.
|
|
|
|
Object.defineProperty(arguments, "0", {
|
|
|
|
value:1,
|
|
|
|
enumerable:false
|
|
|
|
});
|
|
|
|
// Here arguments[0] is a slow alias on arg0.
|
|
|
|
Object.defineProperty(arguments, "0", {
|
|
|
|
value:2,
|
|
|
|
writable:false
|
|
|
|
});
|
|
|
|
// Here arguments[0] is no alias at all.
|
|
|
|
Object.defineProperty(arguments, "0", {
|
|
|
|
value:3
|
|
|
|
});
|
|
|
|
assertEquals(2, arg0);
|
|
|
|
assertEquals(3, arguments[0]);
|
|
|
|
})(0);
|
2012-09-20 11:28:33 +00:00
|
|
|
*/
|
2012-03-07 10:03:32 +00:00
|
|
|
|
|
|
|
// Regression test: We should never observe the hole value.
|
|
|
|
var objectWithGetter = {};
|
|
|
|
objectWithGetter.__defineGetter__('foo', function() {});
|
|
|
|
assertEquals(undefined, objectWithGetter.__lookupSetter__('foo'));
|
|
|
|
|
|
|
|
var objectWithSetter = {};
|
|
|
|
objectWithSetter.__defineSetter__('foo', function(x) {});
|
|
|
|
assertEquals(undefined, objectWithSetter.__lookupGetter__('foo'));
|
2012-07-04 11:40:51 +00:00
|
|
|
|
|
|
|
// An object with a getter on the prototype chain.
|
|
|
|
function getter() { return 111; }
|
|
|
|
function anotherGetter() { return 222; }
|
|
|
|
|
|
|
|
function testGetterOnProto(expected, o) {
|
|
|
|
assertEquals(expected, o.quebec);
|
|
|
|
}
|
|
|
|
|
|
|
|
obj1 = {};
|
|
|
|
Object.defineProperty(obj1, "quebec", { get: getter, configurable: true });
|
|
|
|
obj2 = Object.create(obj1);
|
|
|
|
obj3 = Object.create(obj2);
|
|
|
|
|
|
|
|
testGetterOnProto(111, obj3);
|
|
|
|
testGetterOnProto(111, obj3);
|
|
|
|
%OptimizeFunctionOnNextCall(testGetterOnProto);
|
|
|
|
testGetterOnProto(111, obj3);
|
|
|
|
testGetterOnProto(111, obj3);
|
|
|
|
|
|
|
|
Object.defineProperty(obj1, "quebec", { get: anotherGetter });
|
|
|
|
|
|
|
|
testGetterOnProto(222, obj3);
|
|
|
|
testGetterOnProto(222, obj3);
|
|
|
|
%OptimizeFunctionOnNextCall(testGetterOnProto);
|
|
|
|
testGetterOnProto(222, obj3);
|
|
|
|
testGetterOnProto(222, obj3);
|
|
|
|
|
|
|
|
// An object with a setter on the prototype chain.
|
|
|
|
var modifyMe;
|
|
|
|
function setter(x) { modifyMe = x+1; }
|
|
|
|
function anotherSetter(x) { modifyMe = x+2; }
|
|
|
|
|
|
|
|
function testSetterOnProto(expected, o) {
|
|
|
|
modifyMe = 333;
|
|
|
|
o.romeo = 444;
|
|
|
|
assertEquals(expected, modifyMe);
|
|
|
|
}
|
|
|
|
|
|
|
|
obj1 = {};
|
|
|
|
Object.defineProperty(obj1, "romeo", { set: setter, configurable: true });
|
|
|
|
obj2 = Object.create(obj1);
|
|
|
|
obj3 = Object.create(obj2);
|
|
|
|
|
|
|
|
testSetterOnProto(445, obj3);
|
|
|
|
testSetterOnProto(445, obj3);
|
|
|
|
%OptimizeFunctionOnNextCall(testSetterOnProto);
|
|
|
|
testSetterOnProto(445, obj3);
|
|
|
|
testSetterOnProto(445, obj3);
|
|
|
|
|
|
|
|
Object.defineProperty(obj1, "romeo", { set: anotherSetter });
|
|
|
|
|
|
|
|
testSetterOnProto(446, obj3);
|
|
|
|
testSetterOnProto(446, obj3);
|
|
|
|
%OptimizeFunctionOnNextCall(testSetterOnProto);
|
|
|
|
testSetterOnProto(446, obj3);
|
|
|
|
testSetterOnProto(446, obj3);
|
|
|
|
|
|
|
|
// Removing a setter on the prototype chain.
|
|
|
|
function testSetterOnProtoStrict(o) {
|
|
|
|
"use strict";
|
|
|
|
o.sierra = 12345;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj1 = {};
|
|
|
|
Object.defineProperty(obj1, "sierra",
|
|
|
|
{ get: getter, set: setter, configurable: true });
|
|
|
|
obj2 = Object.create(obj1);
|
|
|
|
obj3 = Object.create(obj2);
|
|
|
|
|
|
|
|
testSetterOnProtoStrict(obj3);
|
|
|
|
testSetterOnProtoStrict(obj3);
|
|
|
|
%OptimizeFunctionOnNextCall(testSetterOnProtoStrict);
|
|
|
|
testSetterOnProtoStrict(obj3);
|
|
|
|
testSetterOnProtoStrict(obj3);
|
|
|
|
|
|
|
|
Object.defineProperty(obj1, "sierra",
|
|
|
|
{ get: getter, set: undefined, configurable: true });
|
|
|
|
|
|
|
|
exception = false;
|
|
|
|
try {
|
|
|
|
testSetterOnProtoStrict(obj3);
|
|
|
|
} catch (e) {
|
|
|
|
exception = true;
|
|
|
|
assertTrue(/which has only a getter/.test(e));
|
|
|
|
}
|
|
|
|
assertTrue(exception);
|
2012-08-16 10:35:39 +00:00
|
|
|
|
|
|
|
// Test assignment to a getter-only property on the prototype chain. This makes
|
|
|
|
// sure that crankshaft re-checks its assumptions and doesn't rely only on type
|
|
|
|
// feedback (which would be monomorphic here).
|
|
|
|
|
|
|
|
function Assign(o) {
|
|
|
|
o.blubb = 123;
|
|
|
|
}
|
|
|
|
|
|
|
|
function C() {}
|
|
|
|
|
|
|
|
Assign(new C);
|
|
|
|
Assign(new C);
|
|
|
|
%OptimizeFunctionOnNextCall(Assign);
|
|
|
|
Object.defineProperty(C.prototype, "blubb", {get: function() { return -42; }});
|
|
|
|
Assign(new C);
|