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 189623 a471415834ae635da94c5e8a2344bf31c44118ed
parent 189622 fa086a70cfbfc508f298fdbfeeb5066ca801c5d3
child 189624 206d7f502e142587002b10f96f49e76f0cacf594
push id26992
push userkwierso@gmail.com
push dateFri, 20 Jun 2014 01:07:53 +0000
treeherdermozilla-central@bdac18bd6c74 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgabor
bugs976148
milestone33.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 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*