Bug 1346012 - Handle dead object wrappers in more places in Promise code. r=shu a=gchang
authorTill Schneidereit <till@tillschneidereit.net>
Fri, 24 Mar 2017 22:49:38 -0700
changeset 375963 4b43e1c02d4f
parent 375962 b7cdc8cfc61f
child 375964 665b9de58dee
push id11067
push usercbook@mozilla.com
push date2017-04-18 08:49 +0000
treeherdermozilla-aurora@4b43e1c02d4f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu, gchang
bugs1346012
milestone54.0a2
Bug 1346012 - Handle dead object wrappers in more places in Promise code. r=shu a=gchang MozReview-Commit-ID: HlmKwoMub9D
js/src/builtin/Promise.cpp
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -455,26 +455,29 @@ EnqueuePromiseReactionJob(JSContext* cx,
 {
     // The reaction might have been stored on a Promise from another
     // compartment, which means it would've been wrapped in a CCW.
     // To properly handle that case here, unwrap it and enter its
     // compartment, where the job creation should take place anyway.
     Rooted<PromiseReactionRecord*> reaction(cx);
     RootedValue handlerArg(cx, handlerArg_);
     mozilla::Maybe<AutoCompartment> ac;
-    if (IsWrapper(reactionObj)) {
-        RootedObject unwrappedReactionObj(cx, UncheckedUnwrap(reactionObj));
-        if (!unwrappedReactionObj)
+    if (!IsProxy(reactionObj)) {
+        MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>());
+        reaction = &reactionObj->as<PromiseReactionRecord>();
+    } else {
+        if (JS_IsDeadWrapper(UncheckedUnwrap(reactionObj))) {
+            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
             return false;
-        ac.emplace(cx, unwrappedReactionObj);
-        reaction = &unwrappedReactionObj->as<PromiseReactionRecord>();
-        if (!cx->compartment()->wrap(cx, &handlerArg))
+        }
+        reaction = &UncheckedUnwrap(reactionObj)->as<PromiseReactionRecord>();
+        MOZ_RELEASE_ASSERT(reaction->is<PromiseReactionRecord>());
+        ac.emplace(cx, reaction);
+        if (!reaction->compartment()->wrap(cx, &handlerArg))
             return false;
-    } else {
-        reaction = &reactionObj->as<PromiseReactionRecord>();
     }
 
     // Must not enqueue a reaction job more than once.
     MOZ_ASSERT(reaction->targetState() == JS::PromiseState::Pending);
 
     assertSameCompartment(cx, handlerArg);
     reaction->setHandlerArg(handlerArg.get());
 
@@ -605,17 +608,17 @@ FulfillMaybeWrappedPromise(JSContext *cx
 {
     Rooted<PromiseObject*> promise(cx);
     RootedValue value(cx, value_);
 
     mozilla::Maybe<AutoCompartment> ac;
     if (!IsProxy(promiseObj)) {
         promise = &promiseObj->as<PromiseObject>();
     } else {
-        if (JS_IsDeadWrapper(promiseObj)) {
+        if (JS_IsDeadWrapper(UncheckedUnwrap(promiseObj))) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
             return false;
         }
         promise = &UncheckedUnwrap(promiseObj)->as<PromiseObject>();
         ac.emplace(cx, promise);
         if (!promise->compartment()->wrap(cx, &value))
             return false;
     }
@@ -755,17 +758,17 @@ RejectMaybeWrappedPromise(JSContext *cx,
 {
     Rooted<PromiseObject*> promise(cx);
     RootedValue reason(cx, reason_);
 
     mozilla::Maybe<AutoCompartment> ac;
     if (!IsProxy(promiseObj)) {
         promise = &promiseObj->as<PromiseObject>();
     } else {
-        if (JS_IsDeadWrapper(promiseObj)) {
+        if (JS_IsDeadWrapper(UncheckedUnwrap(promiseObj))) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
             return false;
         }
         promise = &UncheckedUnwrap(promiseObj)->as<PromiseObject>();
         ac.emplace(cx, promise);
 
         // The rejection reason might've been created in a compartment with higher
         // privileges than the Promise's. In that case, object-type rejection
@@ -868,18 +871,25 @@ PromiseReactionJob(JSContext* cx, unsign
     // To ensure that the embedding ends up with the right entry global, we're
     // guaranteeing that the reaction job function gets created in the same
     // compartment as the handler function. That's not necessarily the global
     // that the job was triggered from, though.
     // We can find the triggering global via the job's reaction record. To go
     // back, we check if the reaction is a wrapper and if so, unwrap it and
     // enter its compartment.
     mozilla::Maybe<AutoCompartment> ac;
-    if (IsWrapper(reactionObj)) {
+    if (!IsProxy(reactionObj)) {
+        MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>());
+    } else {
         reactionObj = UncheckedUnwrap(reactionObj);
+        if (JS_IsDeadWrapper(reactionObj)) {
+            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
+            return false;
+        }
+        MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>());
         ac.emplace(cx, reactionObj);
     }
 
     // Steps 1-2.
     Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>());
     if (reaction->isAsyncFunctionAwait())
         return AsyncFunctionAwaitPromiseReactionJob(cx, reaction, args.rval());
 
@@ -2397,20 +2407,24 @@ BlockOnPromise(JSContext* cx, HandleValu
     // other than the original value of |Promise| was used to create it).
     // To have both that object and |blockedPromise| show up as dependent
     // promises in the debugger, add a dummy reaction to the list of reject
     // reactions that contains |blockedPromise|, but otherwise does nothing.
     RootedObject unwrappedPromiseObj(cx, promiseObj);
     RootedObject blockedPromise(cx, blockedPromise_);
 
     mozilla::Maybe<AutoCompartment> ac;
-    if (IsWrapper(promiseObj)) {
+    if (IsProxy(promiseObj)) {
         unwrappedPromiseObj = CheckedUnwrap(promiseObj);
         if (!unwrappedPromiseObj)
             return false;
+        if (JS_IsDeadWrapper(unwrappedPromiseObj)) {
+            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
+            return false;
+        }
         ac.emplace(cx, unwrappedPromiseObj);
         if (!cx->compartment()->wrap(cx, &blockedPromise))
             return false;
     }
 
     // If the object to depend on isn't a, maybe-wrapped, Promise instance,
     // we ignore it. All this does is lose some small amount of debug
     // information in scenarios that are highly unlikely to occur in useful
@@ -2451,18 +2465,22 @@ AddPromiseReaction(JSContext* cx, Handle
         return true;
     }
 
     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 (IsWrapper(reactionsObj)) {
+    if (IsProxy(reactionsObj)) {
         reactionsObj = UncheckedUnwrap(reactionsObj);
+        if (JS_IsDeadWrapper(reactionsObj)) {
+            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
+            return false;
+        }
         MOZ_ASSERT(reactionsObj->is<PromiseReactionRecord>());
     }
 
     if (reactionsObj->is<PromiseReactionRecord>()) {
         // If a single reaction existed so far, create a list and store the
         // old and the new reaction in it.
         reactions = NewDenseFullyAllocatedArray(cx, 2);
         if (!reactions)