Bug 1313810 - Properly handle Promise-subclassing with a cross-compartment Promise superclass. r=arai
authorTill Schneidereit <till@tillschneidereit.net>
Sat, 29 Oct 2016 13:44:09 +0200
changeset 320873 0cc058bf7a5b6742ba61782e83b2ad0c1d0136b6
parent 320872 5ec89b4b058e4a57e670b3a18ed43801ac591c28
child 320874 507bd72e0ff29e46e77d7650ec02516af42c644d
push id30905
push userphilringnalda@gmail.com
push dateFri, 04 Nov 2016 02:33:06 +0000
treeherdermozilla-central@4f09d9469e73 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1313810
milestone52.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 1313810 - Properly handle Promise-subclassing with a cross-compartment Promise superclass. r=arai MozReview-Commit-ID: 8F0b5SHfIen
js/src/builtin/Promise.cpp
js/src/builtin/Promise.h
js/src/jit-test/tests/promise/promise-cross-compartment-subclassing.js
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -1185,72 +1185,72 @@ PromiseConstructor(JSContext* cx, unsign
             // we only need to do the complex wrapping and unwrapping scheme
             // described above for instances of Promise itself.
             if (newTarget == promiseCtor)
                 needsWrapping = true;
         }
     }
 
     RootedObject proto(cx);
-    if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
+    if (!GetPrototypeFromConstructor(cx, needsWrapping ? newTarget : originalNewTarget, &proto))
         return false;
     if (needsWrapping && !cx->compartment()->wrap(cx, &proto))
         return false;
-    Rooted<PromiseObject*> promise(cx, PromiseObject::create(cx, executor, proto));
+    Rooted<PromiseObject*> promise(cx, PromiseObject::create(cx, executor, proto, needsWrapping));
     if (!promise)
         return false;
 
     // Step 11.
     args.rval().setObject(*promise);
     if (needsWrapping)
         return cx->compartment()->wrap(cx, args.rval());
     return true;
 }
 
 // ES2016, 25.4.3.1. steps 3-11.
 /* static */ PromiseObject*
-PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */)
+PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */,
+                      bool needsWrapping /* = false */)
 {
     MOZ_ASSERT(executor->isCallable());
 
     RootedObject usedProto(cx, proto);
-    bool wrappedProto = false;
     // If the proto is wrapped, that means the current function is running
     // with a different compartment active from the one the Promise instance
     // is to be created in.
     // See the comment in PromiseConstructor for details.
-    if (proto && IsWrapper(proto)) {
-        wrappedProto = true;
+    if (needsWrapping) {
+        MOZ_ASSERT(proto);
         usedProto = CheckedUnwrap(proto);
         if (!usedProto)
             return nullptr;
     }
 
 
     // Steps 3-7.
-    Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx, usedProto, wrappedProto,
+    Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx, usedProto, needsWrapping,
                                                                    false));
     if (!promise)
         return nullptr;
 
     RootedValue promiseVal(cx, ObjectValue(*promise));
-    if (wrappedProto && !cx->compartment()->wrap(cx, &promiseVal))
+    if (needsWrapping && !cx->compartment()->wrap(cx, &promiseVal))
         return nullptr;
 
     // Step 8.
     // The resolving functions are created in the compartment active when the
     // (maybe wrapped) Promise constructor was called. They contain checks and
     // can unwrap the Promise if required.
     RootedValue resolveVal(cx);
     RootedValue rejectVal(cx);
     if (!CreateResolvingFunctions(cx, promiseVal, &resolveVal, &rejectVal))
         return nullptr;
 
     // Need to wrap the resolution functions before storing them on the Promise.
-    if (wrappedProto) {
+    if (needsWrapping) {
         AutoCompartment ac(cx, promise);
         RootedValue wrappedRejectVal(cx, rejectVal);
         if (!cx->compartment()->wrap(cx, &wrappedRejectVal))
             return nullptr;
         promise->setFixedSlot(PromiseSlot_RejectFunction, wrappedRejectVal);
     } else {
         promise->setFixedSlot(PromiseSlot_RejectFunction, rejectVal);
     }
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -35,17 +35,17 @@ class AutoSetNewObjectMetadata;
 
 class PromiseObject : public NativeObject
 {
   public:
     static const unsigned RESERVED_SLOTS = PromiseSlots;
     static const Class class_;
     static const Class protoClass_;
     static PromiseObject* create(JSContext* cx, HandleObject executor,
-                                 HandleObject proto = nullptr);
+                                 HandleObject proto = nullptr, bool needsWrapping = false);
 
     static JSObject* unforgeableResolve(JSContext* cx, HandleValue value);
     static JSObject* unforgeableReject(JSContext* cx, HandleValue value);
 
     JS::PromiseState state() {
         int32_t flags = getFixedSlot(PromiseSlot_Flags).toInt32();
         if (!(flags & PROMISE_FLAG_RESOLVED)) {
             MOZ_ASSERT(!(flags & PROMISE_FLAG_FULFILLED));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/promise/promise-cross-compartment-subclassing.js
@@ -0,0 +1,7 @@
+global = newGlobal();
+OtherPromise = global.Promise;
+class SubPromise extends OtherPromise {}
+assertEq(true, new SubPromise(()=>{}) instanceof OtherPromise);
+assertEq(true, SubPromise.resolve({}) instanceof OtherPromise);
+assertEq(true, SubPromise.reject({}) instanceof OtherPromise);
+assertEq(true, SubPromise.resolve({}).then(()=>{}, ()=>{}) instanceof OtherPromise);