Bug 1130537 - Reimplement js::SetPropertyIgnoringNamedGetter to follow ES6 draft rev 32 9.1.9 [[Set]]. r=efaust.

This commit is contained in:
Jason Orendorff 2015-02-06 16:55:49 -06:00
parent 02b07f6efa
commit 89bb3ca00b
5 changed files with 63 additions and 62 deletions

View File

@ -239,7 +239,6 @@ DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<JSObject*> r
&desc)) {
return false;
}
bool descIsOwn = desc.object() != nullptr;
if (!desc.object()) {
// Don't just use getPropertyDescriptor, unlike BaseProxyHandler::set,
// because that would call getOwnPropertyDescriptor on ourselves. Instead,
@ -253,8 +252,7 @@ DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<JSObject*> r
}
}
return js::SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id,
&desc, descIsOwn, vp, result);
return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, vp, receiver, &desc, result);
}
bool

View File

@ -33,8 +33,7 @@ class CustomProxyHandler : public DirectProxyHandler {
Rooted<JSPropertyDescriptor> desc(cx);
if (!DirectProxyHandler::getPropertyDescriptor(cx, proxy, id, &desc))
return false;
return SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, &desc,
desc.object() == proxy, vp, result);
return SetPropertyIgnoringNamedGetter(cx, proxy, id, vp, receiver, &desc, result);
}
private:

View File

@ -2625,17 +2625,15 @@ ForwardToNative(JSContext *cx, JSNative native, const JS::CallArgs &args);
*
* SetPropertyIgnoringNamedGetter is exposed to make it easier to override
* set() in this way. It carries out all the steps of BaseProxyHandler::set()
* except the initial getOwnPropertyDescriptor()/getPropertyDescriptor() calls.
* The caller must supply those results as the 'desc' and 'descIsOwn'
* parameters.
* except the initial getOwnPropertyDescriptor() call. The caller must supply
* that descriptor as the 'ownDesc' parameter.
*
* Implemented in jsproxy.cpp.
* Implemented in proxy/BaseProxyHandler.cpp.
*/
JS_FRIEND_API(bool)
SetPropertyIgnoringNamedGetter(JSContext *cx, const BaseProxyHandler *handler,
JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, JS::MutableHandle<JSPropertyDescriptor> desc,
bool descIsOwn, JS::MutableHandleValue vp,
SetPropertyIgnoringNamedGetter(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
JS::MutableHandleValue vp, JS::HandleObject receiver,
JS::MutableHandle<JSPropertyDescriptor> ownDesc,
JS::ObjectOpResult &result);
JS_FRIEND_API(void)

View File

@ -86,12 +86,23 @@ BaseProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
if (!getOwnPropertyDescriptor(cx, proxy, id, &ownDesc))
return false;
// The rest is factored out into a separate function with a weird name.
// This algorithm continues just below.
return SetPropertyIgnoringNamedGetter(cx, proxy, id, vp, receiver, &ownDesc, result);
}
bool
js::SetPropertyIgnoringNamedGetter(JSContext *cx, HandleObject obj, HandleId id,
MutableHandleValue vp, HandleObject receiver,
MutableHandle<PropertyDescriptor> ownDesc,
ObjectOpResult &result)
{
// Step 4.
if (!ownDesc.object()) {
// The spec calls this variable "parent", but that word has weird
// connotations in SpiderMonkey, so let's go with "proto".
RootedObject proto(cx);
if (!GetPrototype(cx, proxy, &proto))
if (!GetPrototype(cx, obj, &proto))
return false;
if (proto)
return SetProperty(cx, proto, receiver, id, vp, result);
@ -149,52 +160,6 @@ BaseProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
return result.succeed();
}
bool
js::SetPropertyIgnoringNamedGetter(JSContext *cx, const BaseProxyHandler *handler,
HandleObject proxy, HandleObject receiver,
HandleId id, MutableHandle<PropertyDescriptor> desc,
bool descIsOwn, MutableHandleValue vp, ObjectOpResult &result)
{
/* The control-flow here differs from ::get() because of the fall-through case below. */
MOZ_ASSERT_IF(descIsOwn, desc.object());
if (desc.object()) {
MOZ_ASSERT(desc.getter() != JS_PropertyStub);
MOZ_ASSERT(desc.setter() != JS_StrictPropertyStub);
// Check for read-only properties.
if (desc.isReadonly())
return result.fail(descIsOwn ? JSMSG_READ_ONLY : JSMSG_CANT_REDEFINE_PROP);
if (desc.hasSetterObject() || desc.setter()) {
if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), vp, result))
return false;
if (!result)
return true;
if (!proxy->is<ProxyObject>() ||
proxy->as<ProxyObject>().handler() != handler ||
desc.isShared())
{
return result.succeed();
}
}
desc.value().set(vp.get());
if (descIsOwn) {
MOZ_ASSERT(desc.object() == proxy);
return handler->defineProperty(cx, proxy, id, desc, result);
}
return DefineProperty(cx, receiver, id, desc.value(), desc.getter(), desc.setter(),
desc.attributes(), result);
}
desc.object().set(receiver);
desc.value().set(vp.get());
desc.setAttributes(JSPROP_ENUMERATE);
desc.setGetter(nullptr);
desc.setSetter(nullptr); // Pick up the class getter/setter.
return DefineProperty(cx, receiver, id, desc.value(), nullptr, nullptr, JSPROP_ENUMERATE,
result);
}
bool
BaseProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
AutoIdVector &props) const

View File

@ -332,6 +332,12 @@ ScriptedIndirectProxyHandler::derivedSet(JSContext *cx, HandleObject proxy, Hand
// Find an own or inherited property. The code here is strange for maximum
// backward compatibility with earlier code written before ES6 and before
// SetPropertyIgnoringNamedGetter.
//
// As of March 2015, testing/specialpowers/content/specialpowersAPI.js
// depends on the call to getPropertyDescriptor below, because it does
// support inherited setters but makes no attempt to provide a meaningful
// prototype chain.
Rooted<PropertyDescriptor> desc(cx);
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
return false;
@ -341,7 +347,42 @@ ScriptedIndirectProxyHandler::derivedSet(JSContext *cx, HandleObject proxy, Hand
return false;
}
return SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, &desc, descIsOwn, vp,
MOZ_ASSERT_IF(descIsOwn, desc.object());
if (desc.object()) {
MOZ_ASSERT(desc.getter() != JS_PropertyStub);
MOZ_ASSERT(desc.setter() != JS_StrictPropertyStub);
// Check for read-only properties.
if (desc.isReadonly())
return result.fail(descIsOwn ? JSMSG_READ_ONLY : JSMSG_CANT_REDEFINE_PROP);
if (desc.hasSetterObject() || desc.setter()) {
if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), vp, result))
return false;
if (!result)
return true;
if (!proxy->is<ProxyObject>() ||
proxy->as<ProxyObject>().handler() != this ||
desc.isShared())
{
return result.succeed();
}
}
desc.value().set(vp.get());
if (descIsOwn) {
MOZ_ASSERT(desc.object() == proxy);
return this->defineProperty(cx, proxy, id, &desc, result);
}
return DefineProperty(cx, receiver, id, desc.value(), desc.getter(), desc.setter(),
desc.attributes(), result);
}
desc.object().set(receiver);
desc.value().set(vp.get());
desc.setAttributes(JSPROP_ENUMERATE);
desc.setGetter(nullptr);
desc.setSetter(nullptr); // Pick up the class getter/setter.
return DefineProperty(cx, receiver, id, desc.value(), nullptr, nullptr, JSPROP_ENUMERATE,
result);
}