Bug 987111 - Implement defineProperty for Object Xrays. r=gabor
☠☠ backed out by fe14647a422d ☠ ☠
authorBobby Holley <bobbyholley@gmail.com>
Wed, 04 Jun 2014 15:12:27 -0700
changeset 205925 7883150e5471699a27121eebf3a69e0f08ef9f7e
parent 205924 407c7ca82ada87a5eea4df3e7530d7ff61cabb90
child 205926 cdfd24ddf448cfda953b1810077158c8c6bcd5c5
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgabor
bugs987111
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 987111 - Implement defineProperty for Object Xrays. r=gabor
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -204,19 +204,19 @@ public:
     static const XrayType Type = XrayForWrappedNative;
 
     virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper,
                                        HandleObject holder, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
     virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper,
                                     HandleObject holder, HandleId id,
                                     MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
-    static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
-                               MutableHandle<JSPropertyDescriptor> desc,
-                               Handle<JSPropertyDescriptor> existingDesc, bool *defined);
+    bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
+                        MutableHandle<JSPropertyDescriptor> desc,
+                        Handle<JSPropertyDescriptor> existingDesc, bool *defined);
     virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags,
                                 AutoIdVector &props);
     static bool call(JSContext *cx, HandleObject wrapper,
                      const JS::CallArgs &args, js::Wrapper& baseInstance);
     static bool construct(JSContext *cx, HandleObject wrapper,
                           const JS::CallArgs &args, js::Wrapper& baseInstance);
 
     static bool isResolving(JSContext *cx, JSObject *holder, jsid id);
@@ -255,19 +255,19 @@ public:
     static const XrayType Type = XrayForDOMObject;
 
     virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper,
                                        HandleObject holder, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
     virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper,
                                     HandleObject holder, HandleId id,
                                     MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
-    static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
-                               MutableHandle<JSPropertyDescriptor> desc,
-                               Handle<JSPropertyDescriptor> existingDesc, bool *defined);
+    bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
+                        MutableHandle<JSPropertyDescriptor> desc,
+                        Handle<JSPropertyDescriptor> existingDesc, bool *defined);
     static bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id,
                     bool strict, MutableHandleValue vp);
     virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags,
                                 AutoIdVector &props);
     static bool call(JSContext *cx, HandleObject wrapper,
                      const JS::CallArgs &args, js::Wrapper& baseInstance);
     static bool construct(JSContext *cx, HandleObject wrapper,
                           const JS::CallArgs &args, js::Wrapper& baseInstance);
@@ -300,24 +300,19 @@ public:
     {
         MOZ_ASSUME_UNREACHABLE("resolveNativeProperty hook should never be called with HasPrototype = 1");
     }
 
     virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper,
                                     HandleObject holder, HandleId id,
                                     MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
 
-    static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
-                               MutableHandle<JSPropertyDescriptor> desc,
-                               Handle<JSPropertyDescriptor> existingDesc, bool *defined)
-    {
-        // There's no useful per-trait work to do here. Punt back up to the common code.
-        *defined = false;
-        return true;
-    }
+    bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
+                        MutableHandle<JSPropertyDescriptor> desc,
+                        Handle<JSPropertyDescriptor> existingDesc, bool *defined);
 
     virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags,
                                 AutoIdVector &props);
 
     static bool call(JSContext *cx, HandleObject wrapper,
                      const JS::CallArgs &args, js::Wrapper& baseInstance)
     {
         // We'll handle this when we start supporting Functions.
@@ -651,16 +646,73 @@ JSXrayTraits::resolveOwnProperty(JSConte
                                      desc.getter(), desc.setter()) &&
                JS_GetPropertyDescriptorById(cx, holder, id, desc);
     }
 
     return true;
 }
 
 bool
+JSXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
+                               MutableHandle<JSPropertyDescriptor> desc,
+                               Handle<JSPropertyDescriptor> existingDesc,
+                               bool *defined)
+{
+    *defined = false;
+    RootedObject holder(cx, ensureHolder(cx, wrapper));
+    if (!holder)
+        return false;
+
+
+    // Object instances are special. For that case, we forward property
+    // definitions to the underlying object if the following conditions are met:
+    // * The property being defined is a value-prop.
+    // * The property being defined is either a primitive or subsumed by the target.
+    // * As seen from the Xray, any existing property that we would overwrite is an
+    //   |own| value-prop.
+    //
+    // To avoid confusion, we disallow expandos on Object instances, and
+    // therefore raise an exception here if the above conditions aren't met.
+    bool isObjectInstance = getProtoKey(holder) == JSProto_Object && !isPrototype(holder);
+    if (isObjectInstance) {
+        RootedObject target(cx, getTargetObject(wrapper));
+        if (desc.hasGetterOrSetter()) {
+            JS_ReportError(cx, "Not allowed to define accessor property on [Object] XrayWrapper");
+            return false;
+        }
+        if (desc.value().isObject() &&
+            !AccessCheck::subsumes(target, js::UncheckedUnwrap(&desc.value().toObject())))
+        {
+            JS_ReportError(cx, "Not allowed to define cross-origin object as property on [Object] XrayWrapper");
+            return false;
+        }
+        if (existingDesc.hasGetterOrSetter()) {
+            JS_ReportError(cx, "Not allowed to overwrite accessor property on [Object] XrayWrapper");
+            return false;
+        }
+        if (existingDesc.object() && existingDesc.object() != wrapper) {
+            JS_ReportError(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] XrayWrapper");
+            return false;
+        }
+
+        JSAutoCompartment ac(cx, target);
+        if (!JS_WrapPropertyDescriptor(cx, desc) ||
+            !JS_DefinePropertyById(cx, target, id, desc.value(), desc.attributes(),
+                                   JS_PropertyStub, JS_StrictPropertyStub))
+        {
+            return false;
+        }
+        *defined = true;
+        return true;
+    }
+
+    return true;
+}
+
+bool
 JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags,
                              AutoIdVector &props)
 {
     RootedObject target(cx, getTargetObject(wrapper));
     RootedObject holder(cx, ensureHolder(cx, wrapper));
     if (!holder)
         return false;
 
@@ -2204,17 +2256,17 @@ XrayWrapper<Base, Traits>::definePropert
     // properties. At present, this may not always do the right thing because
     // we often lie (sloppily) about where we found properties and set
     // desc.object() to |wrapper|. Once we fully fix our Xray prototype semantics,
     // this should work as intended.
     if (existing_desc.object() == wrapper && existing_desc.isPermanent())
         return true; // silently ignore attempt to overwrite native property
 
     bool defined = false;
-    if (!Traits::defineProperty(cx, wrapper, id, desc, existing_desc, &defined))
+    if (!Traits::singleton.defineProperty(cx, wrapper, id, desc, existing_desc, &defined))
         return false;
     if (defined)
         return true;
 
     // We're placing an expando. The expando objects live in the target
     // compartment, so we need to enter it.
     RootedObject target(cx, Traits::singleton.getTargetObject(wrapper));
     JSAutoCompartment ac(cx, target);