blob: c3b1e8600b099549a42793ca77fa9e8afc06d9f6 [file] [log] [blame]
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/ */
// Reflect.defineProperty defines properties.
var obj = {};
assertEq(Reflect.defineProperty(obj, "x", {value: 7}), true);
assertEq(obj.x, 7);
var desc = Reflect.getOwnPropertyDescriptor(obj, "x");
assertDeepEq(desc, {value: 7,
writable: false,
enumerable: false,
configurable: false});
// Reflect.defineProperty can define a symbol-keyed property.
var key = Symbol(":o)");
assertEq(Reflect.defineProperty(obj, key, {value: 8}), true);
assertEq(obj[key], 8);
// array .length property
obj = [1, 2, 3, 4, 5];
assertEq(Reflect.defineProperty(obj, "length", {value: 4}), true);
assertDeepEq(obj, [1, 2, 3, 4]);
// The target can be a proxy.
obj = {};
var proxy = new Proxy(obj, {
defineProperty(t, id, desc) {
t[id] = 1;
return true;
}
});
assertEq(Reflect.defineProperty(proxy, "prop", {value: 7}), true);
assertEq(obj.prop, 1);
assertEq(delete obj.prop, true);
assertEq("prop" in obj, false);
// The attributes object is re-parsed, not passed through to the
// handler.defineProperty method.
obj = {};
var attributes = {
configurable: 17,
enumerable: undefined,
value: null
};
proxy = new Proxy(obj, {
defineProperty(t, id, desc) {
assertEq(desc !== attributes, true);
assertEq(desc.configurable, true);
assertEq(desc.enumerable, false);
assertEq(desc.value, null);
assertEq("writable" in desc, false);
return 15; // and the return value here is coerced to boolean
}
});
assertEq(Reflect.defineProperty(proxy, "prop", attributes), true);
// === Failure and error cases
//
// Reflect.defineProperty behaves much like Object.defineProperty, which has
// extremely thorough tests elsewhere, and the implementation is largely
// shared. Duplicating those tests with Reflect.defineProperty would be a
// big waste.
//
// However, certain failures cause Reflect.defineProperty to return false
// without throwing a TypeError (unlike Object.defineProperty). So here we test
// many error cases to check that behavior.
// missing attributes argument
assertThrowsInstanceOf(() => Reflect.defineProperty(obj, "y"),
TypeError);
// non-object attributes argument
for (var attributes of SOME_PRIMITIVE_VALUES) {
assertThrowsInstanceOf(() => Reflect.defineProperty(obj, "y", attributes),
TypeError);
}
// inextensible object
obj = Object.preventExtensions({});
assertEq(Reflect.defineProperty(obj, "prop", {value: 4}), false);
// inextensible object with irrelevant inherited property
obj = Object.preventExtensions(Object.create({"prop": 3}));
assertEq(Reflect.defineProperty(obj, "prop", {value: 4}), false);
// redefine nonconfigurable to configurable
obj = Object.freeze({prop: 1});
assertEq(Reflect.defineProperty(obj, "prop", {configurable: true}), false);
// redefine enumerability of nonconfigurable property
obj = Object.freeze(Object.defineProperties({}, {
x: {enumerable: true, configurable: false, value: 0},
y: {enumerable: false, configurable: false, value: 0},
}));
assertEq(Reflect.defineProperty(obj, "x", {enumerable: false}), false);
assertEq(Reflect.defineProperty(obj, "y", {enumerable: true}), false);
// redefine nonconfigurable data to accessor property, or vice versa
obj = Object.seal({x: 1, get y() { return 2; }});
assertEq(Reflect.defineProperty(obj, "x", {get() { return 2; }}), false);
assertEq(Reflect.defineProperty(obj, "y", {value: 1}), false);
// redefine nonwritable, nonconfigurable property as writable
obj = Object.freeze({prop: 0});
assertEq(Reflect.defineProperty(obj, "prop", {writable: true}), false);
assertEq(Reflect.defineProperty(obj, "prop", {writable: false}), true); // no-op
// change value of nonconfigurable nonwritable property
obj = Object.freeze({prop: 0});
assertEq(Reflect.defineProperty(obj, "prop", {value: -0}), false);
assertEq(Reflect.defineProperty(obj, "prop", {value: +0}), true); // no-op
// change getter or setter
function g() {}
function s(x) {}
obj = {};
Object.defineProperty(obj, "prop", {get: g, set: s, configurable: false});
assertEq(Reflect.defineProperty(obj, "prop", {get: s}), false);
assertEq(Reflect.defineProperty(obj, "prop", {get: g}), true); // no-op
assertEq(Reflect.defineProperty(obj, "prop", {set: g}), false);
assertEq(Reflect.defineProperty(obj, "prop", {set: s}), true); // no-op
// Proxy defineProperty handler method that returns false
var falseValues = [false, 0, -0, "", NaN, null, undefined];
if (typeof objectEmulatingUndefined === "function")
falseValues.push(objectEmulatingUndefined());
var value;
proxy = new Proxy({}, {
defineProperty(t, id, desc) {
return value;
}
});
for (value of falseValues) {
assertEq(Reflect.defineProperty(proxy, "prop", {value: 1}), false);
}
// Proxy defineProperty handler method returns true, in violation of invariants.
// Per spec, this is a TypeError, not a false return.
obj = Object.freeze({x: 1});
proxy = new Proxy(obj, {
defineProperty(t, id, desc) {
return true;
}
});
assertThrowsInstanceOf(() => Reflect.defineProperty(proxy, "x", {value: 2}), TypeError);
assertThrowsInstanceOf(() => Reflect.defineProperty(proxy, "y", {value: 0}), TypeError);
assertEq(Reflect.defineProperty(proxy, "x", {value: 1}), true);
// The second argument is converted ToPropertyKey before any internal methods
// are called on the first argument.
var poison =
(counter => new Proxy({}, new Proxy({}, { get() { throw counter++; } })))(42);
assertThrowsValue(() => {
Reflect.defineProperty(poison, {
toString() { throw 17; },
valueOf() { throw 8675309; }
}, poison);
}, 17);
// For more Reflect.defineProperty tests, see target.js and propertyKeys.js.
reportCompare(0, 0);