Bug 1254968 - Add support for running JS builtins' constructors over Xray wrappers without unwrapping the newTarget. r=bholley,f=bz
authorTill Schneidereit <till@tillschneidereit.net>
Wed, 10 Feb 2016 23:09:13 +0100
changeset 287879 f271fb4ecec8a0daff460b2dee581da53d100780
parent 287878 07505a1c513fe633b9386a1dddb805635627700f
child 287880 3c12aa6e977e4e7259fa06ba8a7f0d66a786c79c
push id30071
push usercbook@mozilla.com
push dateThu, 10 Mar 2016 10:51:55 +0000
treeherdermozilla-central@dd1abe874252 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1254968
milestone48.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 1254968 - Add support for running JS builtins' constructors over Xray wrappers without unwrapping the newTarget. r=bholley,f=bz
js/public/Class.h
js/xpconnect/wrappers/XrayWrapper.cpp
js/xpconnect/wrappers/XrayWrapper.h
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -660,17 +660,29 @@ struct JSClass {
 };
 
 #define JSCLASS_HAS_PRIVATE             (1<<0)  // objects have private slot
 #define JSCLASS_DELAY_METADATA_CALLBACK (1<<1)  // class's initialization code
                                                 // will call
                                                 // SetNewObjectMetadata itself
 #define JSCLASS_PRIVATE_IS_NSISUPPORTS  (1<<3)  // private is (nsISupports*)
 #define JSCLASS_IS_DOMJSCLASS           (1<<4)  // objects are DOM
-// Bit 5 is unused.
+#define JSCLASS_HAS_XRAYED_CONSTRUCTOR  (1<<5)  // if wrapped by an xray
+                                                // wrapper, the builtin
+                                                // class's constructor won't
+                                                // be unwrapped and invoked.
+                                                // Instead, the constructor is
+                                                // resolved in the caller's
+                                                // compartment and invoked
+                                                // with a wrapped newTarget.
+                                                // The constructor has to
+                                                // detect and handle this
+                                                // situation.
+                                                // See PromiseConstructor for
+                                                // details.
 #define JSCLASS_EMULATES_UNDEFINED      (1<<6)  // objects of this class act
                                                 // like the value undefined,
                                                 // in some contexts
 #define JSCLASS_USERBIT1                (1<<7)  // Reserved for embeddings.
 
 // To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or
 // JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where
 // n is a constant in [1, 255].  Reserved slots are indexed from 0 to n-1.
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -906,16 +906,58 @@ JSXrayTraits::enumerateNames(JSContext* 
     const js::Class* clasp = js::GetObjectClass(target);
     MOZ_ASSERT(clasp->spec.defined());
 
     return AppendNamesFromFunctionAndPropertySpecs(
         cx, clasp->spec.prototypeFunctions(),
         clasp->spec.prototypeProperties(), flags, props);
 }
 
+bool
+JSXrayTraits::construct(JSContext* cx, HandleObject wrapper,
+                        const JS::CallArgs& args, const js::Wrapper& baseInstance)
+{
+    JSXrayTraits& self = JSXrayTraits::singleton;
+    JS::RootedObject holder(cx, self.ensureHolder(cx, wrapper));
+    if (self.getProtoKey(holder) == JSProto_Function) {
+        JSProtoKey standardConstructor = constructorFor(holder);
+        if (standardConstructor == JSProto_Null)
+            return baseInstance.construct(cx, wrapper, args);
+
+        const js::Class* clasp = js::ProtoKeyToClass(standardConstructor);
+        MOZ_ASSERT(clasp);
+        if (!(clasp->flags & JSCLASS_HAS_XRAYED_CONSTRUCTOR))
+            return baseInstance.construct(cx, wrapper, args);
+
+        // If the JSCLASS_HAS_XRAYED_CONSTRUCTOR flag is set on the Class,
+        // we don't use the constructor at hand. Instead, we retrieve the
+        // equivalent standard constructor in the xray compartment and run
+        // it in that compartment. The newTarget isn't unwrapped, and the
+        // constructor has to be able to detect and handle this situation.
+        // See the comments in js/public/Class.h and PromiseConstructor for
+        // details and an example.
+        RootedObject ctor(cx);
+        if (!JS_GetClassObject(cx, standardConstructor, &ctor))
+            return false;
+
+        RootedValue ctorVal(cx, ObjectValue(*ctor));
+        HandleValueArray vals(args);
+        RootedObject result(cx);
+        if (!JS::Construct(cx, ctorVal, wrapper, vals, &result))
+            return false;
+        AssertSameCompartment(cx, result);
+        args.rval().setObject(*result);
+        return true;
+    }
+
+    JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
+    js::ReportIsNotFunction(cx, v);
+    return false;
+}
+
 JSObject*
 JSXrayTraits::createHolder(JSContext* cx, JSObject* wrapper)
 {
     RootedObject target(cx, getTargetObject(wrapper));
     RootedObject holder(cx, JS_NewObjectWithGivenProto(cx, &HolderClass,
                                                        nullptr));
     if (!holder)
         return nullptr;
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -246,27 +246,17 @@ public:
             return baseInstance.call(cx, wrapper, args);
 
         JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
         js::ReportIsNotFunction(cx, v);
         return false;
     }
 
     static bool construct(JSContext* cx, JS::HandleObject wrapper,
-                          const JS::CallArgs& args, const js::Wrapper& baseInstance)
-    {
-        JSXrayTraits& self = JSXrayTraits::singleton;
-        JS::RootedObject holder(cx, self.ensureHolder(cx, wrapper));
-        if (self.getProtoKey(holder) == JSProto_Function)
-            return baseInstance.construct(cx, wrapper, args);
-
-        JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
-        js::ReportIsNotFunction(cx, v);
-        return false;
-    }
+                          const JS::CallArgs& args, const js::Wrapper& baseInstance);
 
     bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
                       JS::HandleObject target,
                       JS::MutableHandleObject protop)
     {
         JS::RootedObject holder(cx, ensureHolder(cx, wrapper));
         JSProtoKey key = getProtoKey(holder);
         if (isPrototype(holder)) {