mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 00:55:37 +00:00
Bug 978236 - Implement Proxy.[[DefineProperty]] to ES6 standard. (r=jorendorff)
This commit is contained in:
parent
8ae9f3fb4a
commit
e349c0faa8
@ -1,3 +1,5 @@
|
|||||||
|
// |jit-test| error: TypeError
|
||||||
|
|
||||||
var f = (function () {}).bind({});
|
var f = (function () {}).bind({});
|
||||||
var p = new Proxy(f, {});
|
var p = new Proxy(f, {});
|
||||||
Object.defineProperty(p, "caller", {get: function(){}});
|
Object.defineProperty(p, "caller", {get: function(){}});
|
||||||
|
@ -1049,10 +1049,6 @@ js::DefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (obj->getOps()->lookupGeneric) {
|
if (obj->getOps()->lookupGeneric) {
|
||||||
/*
|
|
||||||
* FIXME: Once ScriptedIndirectProxies are removed, this code should call
|
|
||||||
* TrapDefineOwnProperty directly
|
|
||||||
*/
|
|
||||||
if (obj->is<ProxyObject>()) {
|
if (obj->is<ProxyObject>()) {
|
||||||
RootedValue pd(cx, desc.descriptorValue());
|
RootedValue pd(cx, desc.descriptorValue());
|
||||||
return Proxy::defineProperty(cx, obj, id, pd);
|
return Proxy::defineProperty(cx, obj, id, pd);
|
||||||
@ -1135,10 +1131,6 @@ js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (obj->getOps()->lookupGeneric) {
|
if (obj->getOps()->lookupGeneric) {
|
||||||
/*
|
|
||||||
* FIXME: Once ScriptedIndirectProxies are removed, this code should call
|
|
||||||
* TrapDefineOwnProperty directly
|
|
||||||
*/
|
|
||||||
if (obj->is<ProxyObject>()) {
|
if (obj->is<ProxyObject>()) {
|
||||||
for (size_t i = 0, len = ids.length(); i < len; i++) {
|
for (size_t i = 0, len = ids.length(); i < len; i++) {
|
||||||
RootedValue pd(cx, descs[i].descriptorValue());
|
RootedValue pd(cx, descs[i].descriptorValue());
|
||||||
|
@ -1199,22 +1199,20 @@ IsAccessorDescriptor(const PropertyDescriptor &desc)
|
|||||||
return desc.obj && desc.attrs & (JSPROP_GETTER | JSPROP_SETTER);
|
return desc.obj && desc.attrs & (JSPROP_GETTER | JSPROP_SETTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aux.5 ValidateProperty(O, P, Desc)
|
// ES6 (5 April 2014) ValidateAndApplyPropertyDescriptor(O, P, Extensible, Desc, Current)
|
||||||
|
// Since we are actually performing 9.1.6.2 IsCompatiblePropertyDescriptor(Extensible, Desc,
|
||||||
|
// Current), some parameters are omitted.
|
||||||
static bool
|
static bool
|
||||||
ValidateProperty(JSContext *cx, HandleObject obj, HandleId id, Handle<PropDesc> desc, bool *bp)
|
ValidatePropertyDescriptor(JSContext *cx, Handle<PropDesc> desc, Handle<PropertyDescriptor> current,
|
||||||
|
bool *bp)
|
||||||
{
|
{
|
||||||
// step 1
|
|
||||||
Rooted<PropertyDescriptor> current(cx);
|
|
||||||
if (!GetOwnPropertyDescriptor(cx, obj, id, ¤t))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* steps 2-4 are redundant since ValidateProperty is never called unless
|
* step 2 is redundant since ValidatePropertyDescriptor is never called unless
|
||||||
* target.[[HasOwn]](P) is true
|
* target.[[HasOwn]](P) is true
|
||||||
*/
|
*/
|
||||||
JS_ASSERT(current.object());
|
JS_ASSERT(current.object());
|
||||||
|
|
||||||
// step 5
|
// step 3
|
||||||
if (!desc.hasValue() && !desc.hasWritable() && !desc.hasGet() && !desc.hasSet() &&
|
if (!desc.hasValue() && !desc.hasWritable() && !desc.hasGet() && !desc.hasSet() &&
|
||||||
!desc.hasEnumerable() && !desc.hasConfigurable())
|
!desc.hasEnumerable() && !desc.hasConfigurable())
|
||||||
{
|
{
|
||||||
@ -1222,7 +1220,7 @@ ValidateProperty(JSContext *cx, HandleObject obj, HandleId id, Handle<PropDesc>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 6
|
// step 4
|
||||||
if ((!desc.hasWritable() || desc.writable() == !current.isReadonly()) &&
|
if ((!desc.hasWritable() || desc.writable() == !current.isReadonly()) &&
|
||||||
(!desc.hasGet() || desc.getter() == current.getter()) &&
|
(!desc.hasGet() || desc.getter() == current.getter()) &&
|
||||||
(!desc.hasSet() || desc.setter() == current.setter()) &&
|
(!desc.hasSet() || desc.setter() == current.setter()) &&
|
||||||
@ -1242,7 +1240,7 @@ ValidateProperty(JSContext *cx, HandleObject obj, HandleId id, Handle<PropDesc>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 7
|
// step 5
|
||||||
if (current.isPermanent()) {
|
if (current.isPermanent()) {
|
||||||
if (desc.hasConfigurable() && desc.configurable()) {
|
if (desc.hasConfigurable() && desc.configurable()) {
|
||||||
*bp = false;
|
*bp = false;
|
||||||
@ -1257,21 +1255,21 @@ ValidateProperty(JSContext *cx, HandleObject obj, HandleId id, Handle<PropDesc>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 8
|
// step 6
|
||||||
if (desc.isGenericDescriptor()) {
|
if (desc.isGenericDescriptor()) {
|
||||||
*bp = true;
|
*bp = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 9
|
// step 7a
|
||||||
if (IsDataDescriptor(current) != desc.isDataDescriptor()) {
|
if (IsDataDescriptor(current) != desc.isDataDescriptor()) {
|
||||||
*bp = !current.isPermanent();
|
*bp = !current.isPermanent();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 10
|
// step 8
|
||||||
if (IsDataDescriptor(current)) {
|
if (IsDataDescriptor(current)) {
|
||||||
JS_ASSERT(desc.isDataDescriptor()); // by step 9
|
JS_ASSERT(desc.isDataDescriptor()); // by step 7a
|
||||||
if (current.isPermanent() && current.isReadonly()) {
|
if (current.isPermanent() && current.isReadonly()) {
|
||||||
if (desc.hasWritable() && desc.writable()) {
|
if (desc.hasWritable() && desc.writable()) {
|
||||||
*bp = false;
|
*bp = false;
|
||||||
@ -1293,15 +1291,27 @@ ValidateProperty(JSContext *cx, HandleObject obj, HandleId id, Handle<PropDesc>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// steps 11-12
|
// step 9
|
||||||
JS_ASSERT(IsAccessorDescriptor(current)); // by step 10
|
JS_ASSERT(IsAccessorDescriptor(current)); // by step 8
|
||||||
JS_ASSERT(desc.isAccessorDescriptor()); // by step 9
|
JS_ASSERT(desc.isAccessorDescriptor()); // by step 7a
|
||||||
*bp = (!current.isPermanent() ||
|
*bp = (!current.isPermanent() ||
|
||||||
((!desc.hasSet() || desc.setter() == current.setter()) &&
|
((!desc.hasSet() || desc.setter() == current.setter()) &&
|
||||||
(!desc.hasGet() || desc.getter() == current.getter())));
|
(!desc.hasGet() || desc.getter() == current.getter())));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ValidatePropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, Handle<PropDesc> desc, bool *bp)
|
||||||
|
{
|
||||||
|
// step 1
|
||||||
|
Rooted<PropertyDescriptor> current(cx);
|
||||||
|
if (!GetOwnPropertyDescriptor(cx, obj, id, ¤t))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ValidatePropertyDescriptor(cx, desc, current, bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Aux.6 IsSealed(O, P)
|
// Aux.6 IsSealed(O, P)
|
||||||
static bool
|
static bool
|
||||||
IsSealed(JSContext* cx, HandleObject obj, HandleId id, bool *bp)
|
IsSealed(JSContext* cx, HandleObject obj, HandleId id, bool *bp)
|
||||||
@ -1434,7 +1444,7 @@ TrapGetOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle
|
|||||||
/* step 10 */
|
/* step 10 */
|
||||||
if (isFixed) {
|
if (isFixed) {
|
||||||
bool valid;
|
bool valid;
|
||||||
if (!ValidateProperty(cx, target, id, desc, &valid))
|
if (!ValidatePropertyDescriptor(cx, target, id, desc, &valid))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
@ -1454,91 +1464,6 @@ TrapGetOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrapDefineOwnProperty(O, P, DescObj, Throw)
|
|
||||||
static bool
|
|
||||||
TrapDefineOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
|
|
||||||
{
|
|
||||||
// step 1
|
|
||||||
RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
|
|
||||||
|
|
||||||
// step 2
|
|
||||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
|
||||||
|
|
||||||
// step 3
|
|
||||||
RootedValue trap(cx);
|
|
||||||
if (!JSObject::getProperty(cx, handler, handler, cx->names().defineProperty, &trap))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// step 4
|
|
||||||
if (trap.isUndefined()) {
|
|
||||||
Rooted<PropertyDescriptor> desc(cx);
|
|
||||||
if (!ParsePropertyDescriptorObject(cx, proxy, vp, &desc))
|
|
||||||
return false;
|
|
||||||
return JS_DefinePropertyById(cx, target, id, desc.value(), desc.attributes(),
|
|
||||||
desc.getter(), desc.setter());
|
|
||||||
}
|
|
||||||
|
|
||||||
// step 5
|
|
||||||
RootedValue normalizedDesc(cx, vp);
|
|
||||||
if (!NormalizePropertyDescriptor(cx, &normalizedDesc))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// step 6
|
|
||||||
RootedValue value(cx);
|
|
||||||
if (!IdToExposableValue(cx, id, &value))
|
|
||||||
return false;
|
|
||||||
Value argv[] = {
|
|
||||||
ObjectValue(*target),
|
|
||||||
value,
|
|
||||||
normalizedDesc
|
|
||||||
};
|
|
||||||
RootedValue trapResult(cx);
|
|
||||||
if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// steps 7-8
|
|
||||||
if (ToBoolean(trapResult)) {
|
|
||||||
bool isFixed;
|
|
||||||
if (!HasOwn(cx, target, id, &isFixed))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool extensible;
|
|
||||||
if (!JSObject::isExtensible(cx, target, &extensible))
|
|
||||||
return false;
|
|
||||||
if (!extensible && !isFixed) {
|
|
||||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rooted<PropDesc> desc(cx);
|
|
||||||
if (!desc.initialize(cx, normalizedDesc))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (isFixed) {
|
|
||||||
bool valid;
|
|
||||||
if (!ValidateProperty(cx, target, id, desc, &valid))
|
|
||||||
return false;
|
|
||||||
if (!valid) {
|
|
||||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!desc.configurable() && !isFixed) {
|
|
||||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
vp.set(BooleanValue(true));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// step 9
|
|
||||||
// FIXME: API does not include a Throw parameter
|
|
||||||
vp.set(BooleanValue(false));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
ReportInvalidTrapResult(JSContext *cx, JSObject *proxy, JSAtom *atom)
|
ReportInvalidTrapResult(JSContext *cx, JSObject *proxy, JSAtom *atom)
|
||||||
{
|
{
|
||||||
@ -1744,19 +1669,90 @@ ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject
|
|||||||
return ParsePropertyDescriptorObject(cx, proxy, v, desc, true);
|
return ParsePropertyDescriptorObject(cx, proxy, v, desc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ES6 (5 April 2014) Proxy.[[DefineOwnProperty]](O,P)
|
||||||
bool
|
bool
|
||||||
ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
||||||
MutableHandle<PropertyDescriptor> desc)
|
MutableHandle<PropertyDescriptor> desc)
|
||||||
{
|
{
|
||||||
// step 1
|
// step 2
|
||||||
Rooted<PropDesc> d(cx);
|
RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
|
||||||
d.initFromPropertyDescriptor(desc);
|
|
||||||
RootedValue v(cx);
|
// TODO: step 3: Implement revocation semantics. See bug 978279.
|
||||||
if (!FromGenericPropertyDescriptor(cx, &d, &v))
|
|
||||||
|
// step 4
|
||||||
|
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||||
|
|
||||||
|
// step 5-6
|
||||||
|
RootedValue trap(cx);
|
||||||
|
if (!JSObject::getProperty(cx, handler, handler, cx->names().defineProperty, &trap))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// step 7
|
||||||
|
if (trap.isUndefined())
|
||||||
|
return DirectProxyHandler::defineProperty(cx, proxy, id, desc);
|
||||||
|
|
||||||
|
// step 8-9, with 9 blatantly defied.
|
||||||
|
// FIXME: This is incorrect with respect to [[Origin]]. See bug 601379.
|
||||||
|
RootedValue descObj(cx);
|
||||||
|
if (!NewPropertyDescriptorObject(cx, desc, &descObj))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// step 2
|
// step 10, 12
|
||||||
return TrapDefineOwnProperty(cx, proxy, id, &v);
|
RootedValue propKey(cx);
|
||||||
|
if (!IdToExposableValue(cx, id, &propKey))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Value argv[] = {
|
||||||
|
ObjectValue(*target),
|
||||||
|
propKey,
|
||||||
|
descObj
|
||||||
|
};
|
||||||
|
RootedValue trapResult(cx);
|
||||||
|
if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// step 11, 13
|
||||||
|
if (ToBoolean(trapResult)) {
|
||||||
|
// step 14-15
|
||||||
|
Rooted<PropertyDescriptor> targetDesc(cx);
|
||||||
|
if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// step 16-17
|
||||||
|
bool extensibleTarget;
|
||||||
|
if (!JSObject::isExtensible(cx, target, &extensibleTarget))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// step 18-19
|
||||||
|
bool settingConfigFalse = desc.isPermanent();
|
||||||
|
if (!targetDesc.object()) {
|
||||||
|
// step 20a
|
||||||
|
if (!extensibleTarget) {
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// step 20b
|
||||||
|
if (settingConfigFalse) {
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// step 21
|
||||||
|
bool valid;
|
||||||
|
Rooted<PropDesc> pd(cx);
|
||||||
|
pd.initFromPropertyDescriptor(desc);
|
||||||
|
if (!ValidatePropertyDescriptor(cx, pd, targetDesc, &valid))
|
||||||
|
return false;
|
||||||
|
if (!valid || (settingConfigFalse && !targetDesc.isPermanent())) {
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// [[DefineProperty]] should return a boolean value, which is used to do things like
|
||||||
|
// strict-mode throwing. At present, the engine is not prepared to do that. See bug 826587.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -18,14 +18,23 @@ print(BUGNUMBER + ": " + summary);
|
|||||||
var arr = [];
|
var arr = [];
|
||||||
var p = new Proxy(arr, {});
|
var p = new Proxy(arr, {});
|
||||||
|
|
||||||
// This really should throw a TypeError, but we're buggy just yet, and this
|
function assertThrowsTypeError(f)
|
||||||
// silently does nothing.
|
{
|
||||||
Object.defineProperty(p, "length", { value: 17, configurable: true });
|
try {
|
||||||
|
f();
|
||||||
|
assertEq(false, true, "Must have thrown");
|
||||||
|
} catch (e) {
|
||||||
|
assertEq(e instanceof TypeError, true, "Must have thrown TypeError");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redefining non-configurable length should throw a TypeError
|
||||||
|
assertThrowsTypeError(function () { Object.defineProperty(p, "length", { value: 17, configurable: true }); });
|
||||||
|
|
||||||
// Same here.
|
// Same here.
|
||||||
Object.defineProperty(p, "length", { value: 42, enumerable: true });
|
assertThrowsTypeError(function () { Object.defineProperty(p, "length", { value: 42, enumerable: true }); });
|
||||||
|
|
||||||
// But at least we can check the property went unchanged.
|
// Check the property went unchanged.
|
||||||
var pd = Object.getOwnPropertyDescriptor(p, "length");
|
var pd = Object.getOwnPropertyDescriptor(p, "length");
|
||||||
assertEq(pd.value, 0);
|
assertEq(pd.value, 0);
|
||||||
assertEq(pd.writable, true);
|
assertEq(pd.writable, true);
|
||||||
|
Loading…
Reference in New Issue
Block a user