Bug 976148 - Support the 'prototype' property for standard constructors. r=gabor
authorBobby Holley <bobbyholley@gmail.com>
Thu, 19 Jun 2014 09:57:06 -0700
changeset 203415 a471415834ae635da94c5e8a2344bf31c44118ed
parent 203414 fa086a70cfbfc508f298fdbfeeb5066ca801c5d3
child 203416 206d7f502e142587002b10f96f49e76f0cacf594
push id6561
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 21:23:20 +0000
treeherdermozilla-aurora@428d4d3c8588 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgabor
bugs976148
milestone33.0a1
Bug 976148 - Support the 'prototype' property for standard constructors. r=gabor
js/xpconnect/tests/chrome/test_xrayToJS.xul
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -48,18 +48,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     // Test constructors that can be instantiated with zero arguments.
     for (var c of simpleConstructors) {
       ok(iwin[c], "Constructors appear: " + c);
       is(iwin[c], Cu.unwaiveXrays(iwin.wrappedJSObject[c]),
          "we end up with the appropriate constructor: " + c);
       is(Cu.unwaiveXrays(Cu.waiveXrays(new iwin[c]).constructor), iwin[c],
          "constructor property is set up right: " + c);
-      is(Object.getPrototypeOf(new iwin[c]),
-         Cu.unwaiveXrays(Cu.waiveXrays(iwin[c]).prototype),
+      is(Object.getPrototypeOf(new iwin[c]), iwin[c].prototype,
          "prototype is correct: " + c);
       is(global(new iwin[c]), iwin, "Got the right global: " + c);
     }
 
     // Test Object in more detail.
     var num = new iwin.Object(4);
     is(num.valueOf(), 4, "primitive object construction works");
     is(global(num), iwin, "correct global for num");
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -412,29 +412,35 @@ public:
         // the target is the canonical representation of state. If it gets
         // collected, then expandos and such should be collected too. So there's
         // nothing to do here.
     }
 
     enum {
         SLOT_PROTOKEY = 0,
         SLOT_ISPROTOTYPE,
+        SLOT_CONSTRUCTOR_FOR,
         SLOT_COUNT
     };
     virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
 
     static JSProtoKey getProtoKey(JSObject *holder) {
         int32_t key = js::GetReservedSlot(holder, SLOT_PROTOKEY).toInt32();
         return static_cast<JSProtoKey>(key);
     }
 
     static bool isPrototype(JSObject *holder) {
         return js::GetReservedSlot(holder, SLOT_ISPROTOTYPE).toBoolean();
     }
 
+    static JSProtoKey constructorFor(JSObject *holder) {
+        int32_t key = js::GetReservedSlot(holder, SLOT_CONSTRUCTOR_FOR).toInt32();
+        return static_cast<JSProtoKey>(key);
+    }
+
     static bool getOwnPropertyFromTargetIfSafe(JSContext *cx,
                                                HandleObject target,
                                                HandleObject wrapper,
                                                HandleId id,
                                                MutableHandle<JSPropertyDescriptor> desc);
 
     static const JSClass HolderClass;
     static JSXrayTraits singleton;
@@ -561,16 +567,35 @@ JSXrayTraits::resolveOwnProperty(JSConte
             return JS_WrapPropertyDescriptor(cx, desc);
         } else if (IsTypedArrayKey(key)) {
             if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
                 JS_ReportError(cx, "Accessing TypedArray data over Xrays is slow, and forbidden "
                                    "in order to encourage performant code. To copy TypedArrays "
                                    "across origin boundaries, consider using Components.utils.cloneInto().");
                 return false;
             }
+        } else if (key == JSProto_Function) {
+            // Handle the 'prototype' property to make xrayedGlobal.StandardClass.prototype work.
+            if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE)) {
+                JSProtoKey standardConstructor = constructorFor(holder);
+                if (standardConstructor != JSProto_Null) {
+                    RootedObject standardProto(cx);
+                    {
+                        JSAutoCompartment ac(cx, target);
+                        if (!JS_GetClassPrototype(cx, standardConstructor, &standardProto))
+                            return false;
+                        MOZ_ASSERT(standardProto);
+                    }
+                    if (!JS_WrapObject(cx, &standardProto))
+                        return false;
+                    FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
+                                           ObjectValue(*standardProto));
+                    return true;
+                }
+            }
         }
 
         // The rest of this function applies only to prototypes.
         return true;
     }
 
     // The non-HasPrototypes semantics implemented by traditional Xrays are kind
     // of broken with respect to |own|-ness and the holder. The common code
@@ -814,16 +839,22 @@ JSXrayTraits::enumerateNames(JSContext *
         } else if (IsTypedArrayKey(key)) {
             uint32_t length = JS_GetTypedArrayLength(target);
             // TypedArrays enumerate every indexed property in range, but
             // |length| is a getter that lives on the proto, like it should be.
             if (!props.reserve(length))
                 return false;
             for (int32_t i = 0; i <= int32_t(length - 1); ++i)
                 props.infallibleAppend(INT_TO_JSID(i));
+        } else if (key == JSProto_Function) {
+            // Handle the .prototype property on standard constructors.
+            if (constructorFor(holder) != JSProto_Null) {
+                if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE)))
+                    return false;
+            }
         }
 
         // The rest of this function applies only to prototypes.
         return true;
     }
 
     // Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
     const js::Class *clasp = js::GetObjectClass(target);
@@ -878,16 +909,23 @@ JSXrayTraits::createHolder(JSContext *cx
 
     // Store it on the holder.
     RootedValue v(cx);
     v.setNumber(static_cast<uint32_t>(key));
     js::SetReservedSlot(holder, SLOT_PROTOKEY, v);
     v.setBoolean(isPrototype);
     js::SetReservedSlot(holder, SLOT_ISPROTOTYPE, v);
 
+    // If this is a function, also compute whether it serves as a constructor
+    // for a standard class.
+    if (key == JSProto_Function) {
+        v.setNumber(static_cast<uint32_t>(IdentifyStandardConstructor(target)));
+        js::SetReservedSlot(holder, SLOT_CONSTRUCTOR_FOR, v);
+    }
+
     return holder;
 }
 
 XPCWrappedNativeXrayTraits XPCWrappedNativeXrayTraits::singleton;
 DOMXrayTraits DOMXrayTraits::singleton;
 JSXrayTraits JSXrayTraits::singleton;
 
 XrayTraits*