From 1d8954ace90bb05eddeda84a73e8b10a2382e89b Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Sat, 22 Dec 2018 21:42:21 +0000 Subject: [PATCH] Bug 1515754 - Enter the reaction's realm in EnqueuePromiseReactionJob before creating the job. r=arai This is consistent with what we do for cross-compartment wrappers and avoids problems with running jobs against a dying global (Gecko drops such jobs). I added a globalOfFirstJobInQueue() shell function so I could write a test that checks both the compartment-per-global and single-compartment cases. Differential Revision: https://phabricator.services.mozilla.com/D15176 --HG-- extra : moz-landing-system : lando --- js/src/builtin/Promise.cpp | 7 +++++ .../tests/realms/promise-job-global.js | 31 +++++++++++++++++++ js/src/shell/js.cpp | 23 ++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 js/src/jit-test/tests/realms/promise-job-global.js diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 6f5738ea222a..0839e2099586 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -959,6 +959,13 @@ MOZ_MUST_USE static bool EnqueuePromiseReactionJob( if (!IsProxy(reactionObj)) { MOZ_RELEASE_ASSERT(reactionObj->is()); reaction = &reactionObj->as(); + if (cx->realm() != reaction->realm()) { + // If the compartment has multiple realms, create the job in the + // reaction's realm. This is consistent with the code in the else-branch + // and avoids problems with running jobs against a dying global (Gecko + // drops such jobs). + ar.emplace(cx, reaction); + } } else { JSObject* unwrappedReactionObj = UncheckedUnwrap(reactionObj); if (JS_IsDeadWrapper(unwrappedReactionObj)) { diff --git a/js/src/jit-test/tests/realms/promise-job-global.js b/js/src/jit-test/tests/realms/promise-job-global.js new file mode 100644 index 000000000000..246ce171a2a9 --- /dev/null +++ b/js/src/jit-test/tests/realms/promise-job-global.js @@ -0,0 +1,31 @@ +// Test that jobs added to the promise job queue have a global that's consistent +// with and without same-compartment realms. + +var g1 = newGlobal(); +var g2 = newGlobal({sameCompartmentAs: this}); + +// EnqueuePromiseReactionJob, handler is a primitive. +// Job's global is the reaction's global. +function test1(g) { + var resolve; + var p = new Promise(res => { resolve = res; }); + g.Promise.prototype.then.call(p, 1); + resolve(); + assertEq(globalOfFirstJobInQueue(), g); + drainJobQueue(); +} +test1(g1); +test1(g2); + +// EnqueuePromiseReactionJob, handler is an object. +// Job's global is the handler's global. +function test2(g) { + var resolve; + var p = new Promise(res => { resolve = res; }); + p.then(new g.Function()); + resolve(); + assertEq(globalOfFirstJobInQueue(), g); + drainJobQueue(); +} +test2(g1); +test2(g2); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 4a119c5bc1f0..f294004852b0 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1036,6 +1036,24 @@ static bool DrainJobQueue(JSContext* cx, unsigned argc, Value* vp) { return true; } +static bool GlobalOfFirstJobInQueue(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + if (cx->jobQueue->empty()) { + JS_ReportErrorASCII(cx, "Job queue is empty"); + return false; + } + + RootedObject job(cx, cx->jobQueue->front()); + RootedObject global(cx, &job->nonCCWGlobal()); + if (!cx->compartment()->wrap(cx, &global)) { + return false; + } + + args.rval().setObject(*global); + return true; +} + static void ForwardingPromiseRejectionTrackerCallback( JSContext* cx, JS::HandleObject promise, JS::PromiseRejectionHandlingState state, void* data) { @@ -8617,6 +8635,11 @@ JS_FN_HELP("parseBin", BinParse, 1, 0, "enqueueJob(fn)", " Enqueue 'fn' on the shell's job queue."), + JS_FN_HELP("globalOfFirstJobInQueue", GlobalOfFirstJobInQueue, 0, 0, +"globalOfFirstJobInQueue()", +" Returns the global of the first item in the job queue. Throws an exception\n" +" if the queue is empty.\n"), + JS_FN_HELP("drainJobQueue", DrainJobQueue, 0, 0, "drainJobQueue()", "Take jobs from the shell's job queue in FIFO order and run them until the\n"