Bug 1298597 - Properly handle the target Promise having been nuked in resolve/reject functions for xray'd Promises. r?jandem draft
authorTill Schneidereit <till@tillschneidereit.net>
Fri, 02 Sep 2016 16:57:52 +0200
changeset 409214 66dce0b8ce8f6d01303215441698a70a1b95f336
parent 409205 11af4e9270d3b113f9521e9714341c5cb6063e73
child 530305 fd44999b0343c0976ef87719a266e7a9f417dd81
push id28432
push userbmo:till@tillschneidereit.net
push dateFri, 02 Sep 2016 15:05:54 +0000
reviewersjandem
bugs1298597
milestone51.0a1
Bug 1298597 - Properly handle the target Promise having been nuked in resolve/reject functions for xray'd Promises. r?jandem MozReview-Commit-ID: EBAwzff9vOM
js/src/builtin/Promise.cpp
js/xpconnect/tests/unit/test_resolve_dead_promise.js
js/xpconnect/tests/unit/xpcshell.ini
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -164,19 +164,23 @@ ResolvePromise(JSContext* cx, Handle<Pro
 
 // ES2016, 25.4.1.4.
 static MOZ_MUST_USE bool
 FulfillMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue value_) {
     Rooted<PromiseObject*> promise(cx);
     RootedValue value(cx, value_);
 
     mozilla::Maybe<AutoCompartment> ac;
-    if (!IsWrapper(promiseObj)) {
+    if (!IsProxy(promiseObj)) {
         promise = &promiseObj->as<PromiseObject>();
     } else {
+        if (JS_IsDeadWrapper(promiseObj)) {
+            JS_ReportErrorNumber(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;
     }
 
     MOZ_ASSERT(promise->state() == JS::PromiseState::Pending);
 
@@ -185,19 +189,23 @@ FulfillMaybeWrappedPromise(JSContext *cx
 
 // ES2016, 25.4.1.7.
 static MOZ_MUST_USE bool
 RejectMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue reason_) {
     Rooted<PromiseObject*> promise(cx);
     RootedValue reason(cx, reason_);
 
     mozilla::Maybe<AutoCompartment> ac;
-    if (!IsWrapper(promiseObj)) {
+    if (!IsProxy(promiseObj)) {
         promise = &promiseObj->as<PromiseObject>();
     } else {
+        if (JS_IsDeadWrapper(promiseObj)) {
+            JS_ReportErrorNumber(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
         // values might be wrapped into a wrapper that throws whenever the
         // Promise's reaction handler wants to do anything useful with it. To
         // avoid that situation, we synthesize a generic error that doesn't
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_resolve_dead_promise.js
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=1298597 */
+
+function run_test()
+{
+  var sb = Components.utils.Sandbox("http://www.blah.com");
+  var resolveFun;
+  var p1 = new sb.Promise((res, rej) => {resolveFun = res});
+  var rejectFun;
+  var p2 = new sb.Promise((res, rej) => {rejectFun = rej});
+  Components.utils.nukeSandbox(sb);
+  do_check_true(Components.utils.isDeadWrapper(sb), "sb should be dead");
+  do_check_true(Components.utils.isDeadWrapper(p1), "p1 should be dead");
+  do_check_true(Components.utils.isDeadWrapper(p2), "p2 should be dead");
+
+  var exception;
+
+  try{
+    resolveFun(1);
+    do_check_true(false);
+  } catch (e) {
+    exception = e;
+  }
+  do_check_true(exception.toString().includes("can't access dead object"),
+                "Resolving dead wrapped promise should throw");
+
+  exception = undefined;
+  try{
+    rejectFun(1);
+    do_check_true(false);
+  } catch (e) {
+    exception = e;
+  }
+  do_check_true(exception.toString().includes("can't access dead object"),
+                "Rejecting dead wrapped promise should throw");
+}
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -128,8 +128,9 @@ head = head_watchdog.js
 [test_watchdog_hibernate.js]
 head = head_watchdog.js
 [test_weak_keys.js]
 [test_writeToGlobalPrototype.js]
 [test_xpcwn_tamperproof.js]
 [test_xrayed_iterator.js]
 [test_xray_SavedFrame.js]
 [test_xray_SavedFrame-02.js]
+[test_resolve_dead_promise.js]