Bug 1475669 - Return proper result for wrapped reactions. r=jorendorff
authorAndré Bargull <andre.bargull@gmail.com>
Thu, 19 Jul 2018 12:41:34 -0700
changeset 490445 cb2f928fff562ff38b66f474ab8acd628535d985
parent 490444 2b175e8c52636117d0e635c4cf9d14d16fcefbea
child 490446 6e87ad3335ba0e7310e485d48254706cb8c21d47
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersjorendorff
bugs1475669
milestone64.0a1
Bug 1475669 - Return proper result for wrapped reactions. r=jorendorff
js/src/builtin/Promise.cpp
js/src/jit-test/tests/debug/Object-promiseDependentPromises-realms.js
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -4345,41 +4345,65 @@ PromiseObject::dependentPromises(JSConte
 
     RootedValue reactionsVal(cx, reactions());
 
     // If no reactions are pending, we don't have list and are done.
     if (reactionsVal.isNullOrUndefined()) {
         return true;
     }
 
-    RootedNativeObject reactions(cx, &reactionsVal.toObject().as<NativeObject>());
-
-    // If only a single reaction is pending, it's stored directly.
-    if (reactions->is<PromiseReactionRecord>()) {
+    RootedObject reactionsObj(cx, &reactionsVal.toObject());
+
+    // If only a single reaction exists, it's stored directly instead of in a
+    // list. In that case, `reactionsObj` might be a wrapper, which we can
+    // always safely unwrap.
+    if (IsProxy(reactionsObj)) {
+        reactionsObj = UncheckedUnwrap(reactionsObj);
+        if (JS_IsDeadWrapper(reactionsObj)) {
+            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
+            return false;
+        }
+        MOZ_RELEASE_ASSERT(reactionsObj->is<PromiseReactionRecord>());
+    }
+
+    if (reactionsObj->is<PromiseReactionRecord>()) {
         // Not all reactions have a Promise on them.
-        RootedObject promiseObj(cx, reactions->as<PromiseReactionRecord>().promise());
+        RootedObject promiseObj(cx, reactionsObj->as<PromiseReactionRecord>().promise());
         if (!promiseObj) {
             return true;
         }
 
         if (!values.growBy(1)) {
             return false;
         }
 
         values[0].setObject(*promiseObj);
         return true;
     }
 
+    MOZ_RELEASE_ASSERT(reactionsObj->is<NativeObject>());
+    HandleNativeObject reactions = reactionsObj.as<NativeObject>();
+
     uint32_t len = reactions->getDenseInitializedLength();
     MOZ_ASSERT(len >= 2);
 
     uint32_t valuesIndex = 0;
+    Rooted<PromiseReactionRecord*> reaction(cx);
     for (uint32_t i = 0; i < len; i++) {
-        const Value& element = reactions->getDenseElement(i);
-        PromiseReactionRecord* reaction = &element.toObject().as<PromiseReactionRecord>();
+        JSObject* element = &reactions->getDenseElement(i).toObject();
+        if (IsProxy(element)) {
+            element = UncheckedUnwrap(element);
+            if (JS_IsDeadWrapper(element)) {
+                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
+                return false;
+            }
+        }
+
+        MOZ_RELEASE_ASSERT(element->is<PromiseReactionRecord>());
+        reaction = &element->as<PromiseReactionRecord>();
 
         // Not all reactions have a Promise on them.
         RootedObject promiseObj(cx, reaction->promise());
         if (!promiseObj) {
             continue;
         }
         if (!values.growBy(1)) {
             return false;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Object-promiseDependentPromises-realms.js
@@ -0,0 +1,17 @@
+// Bug 1475669 - Cross-compartment dependent promises.
+
+// Create a promise in realm 1.
+let g1 = newGlobal();
+let p1 = new g1.Promise((_resolve, _reject) => {});
+
+// Add a dependent promise in realm 2.
+let g2 = newGlobal();
+let p2 = g2.Promise.prototype.then.call(p1, g2.eval(`value => {}`));
+
+// Use debugger to find p2 from p1.
+let dbg = new Debugger;
+let g1w = dbg.addDebuggee(g1);
+let g2w = dbg.addDebuggee(g2);
+let dependents = g1w.makeDebuggeeValue(p1).promiseDependentPromises;
+assertEq(dependents.length, 1);
+assertEq(dependents[0], g2w.makeDebuggeeValue(p2));