mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Merge mozilla-inbound to mozilla-central. a=merge
This commit is contained in:
commit
00f3836a87
@ -1475,6 +1475,15 @@ pref("media.gmp-widevinecdm.visible", true);
|
||||
pref("media.gmp-widevinecdm.enabled", true);
|
||||
#endif
|
||||
|
||||
#if defined(_ARM64_) && defined(XP_WIN)
|
||||
// Windows on ARM64, OpenH264 not available yet.
|
||||
pref("media.gmp-gmpopenh264.visible", false);
|
||||
pref("media.gmp-gmpopenh264.enabled", false);
|
||||
#else
|
||||
// Not Windows on ARM64
|
||||
pref("media.gmp-gmpopenh264.visible", true);
|
||||
pref("media.gmp-gmpopenh264.enabled", true);
|
||||
#endif
|
||||
// Switch block autoplay logic to v2, and enable UI.
|
||||
pref("media.autoplay.enabled.user-gestures-needed", true);
|
||||
|
||||
|
@ -35,15 +35,14 @@ export async function onConnect(connection: any, actions: Object) {
|
||||
supportsWasm
|
||||
});
|
||||
|
||||
if (actions) {
|
||||
setupEvents({ threadClient, actions, supportsWasm });
|
||||
}
|
||||
setupEvents({ threadClient, tabTarget, actions, supportsWasm });
|
||||
|
||||
tabTarget.on("will-navigate", actions.willNavigate);
|
||||
tabTarget.on("navigate", actions.navigated);
|
||||
|
||||
await threadClient.reconfigure({
|
||||
observeAsmJS: true,
|
||||
pauseWorkersUntilAttach: true,
|
||||
wasmBinarySource: supportsWasm,
|
||||
skipBreakpoints: prefs.skipPausing
|
||||
});
|
||||
|
@ -357,11 +357,16 @@ function getSourceForActor(actor: ActorId) {
|
||||
|
||||
async function fetchWorkers(): Promise<Worker[]> {
|
||||
if (features.windowlessWorkers) {
|
||||
const options = {
|
||||
breakpoints
|
||||
};
|
||||
|
||||
const newWorkerClients = await updateWorkerClients({
|
||||
tabTarget,
|
||||
debuggerClient,
|
||||
threadClient,
|
||||
workerClients
|
||||
workerClients,
|
||||
options
|
||||
});
|
||||
|
||||
// Fetch the sources and install breakpoints on any new workers.
|
||||
@ -370,9 +375,6 @@ async function fetchWorkers(): Promise<Worker[]> {
|
||||
if (!workerClients[actor]) {
|
||||
const client = newWorkerClients[actor].thread;
|
||||
createSources(client);
|
||||
for (const { location, options } of (Object.values(breakpoints): any)) {
|
||||
client.setBreakpoint(location, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,8 @@ import type {
|
||||
ResumedPacket,
|
||||
PausedPacket,
|
||||
ThreadClient,
|
||||
Actions
|
||||
Actions,
|
||||
TabTarget
|
||||
} from "./types";
|
||||
|
||||
import { createPause, createSource } from "./create";
|
||||
@ -19,6 +20,7 @@ const CALL_STACK_PAGE_SIZE = 1000;
|
||||
|
||||
type Dependencies = {
|
||||
threadClient: ThreadClient,
|
||||
tabTarget: TabTarget,
|
||||
actions: Actions,
|
||||
supportsWasm: boolean
|
||||
};
|
||||
@ -35,26 +37,13 @@ function addThreadEventListeners(client: ThreadClient) {
|
||||
|
||||
function setupEvents(dependencies: Dependencies) {
|
||||
const threadClient = dependencies.threadClient;
|
||||
const tabTarget = dependencies.tabTarget;
|
||||
actions = dependencies.actions;
|
||||
supportsWasm = dependencies.supportsWasm;
|
||||
sourceQueue.initialize(actions);
|
||||
|
||||
if (threadClient) {
|
||||
addThreadEventListeners(threadClient);
|
||||
|
||||
if (threadClient._parent) {
|
||||
// Parent may be BrowsingContextTargetFront/WorkerTargetFront and
|
||||
// be protocol.js. Or DebuggerClient and still be old fashion actor.
|
||||
if (threadClient._parent.on) {
|
||||
threadClient._parent.on("workerListChanged", workerListChanged);
|
||||
} else {
|
||||
threadClient._parent.addListener(
|
||||
"workerListChanged",
|
||||
workerListChanged
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
addThreadEventListeners(threadClient);
|
||||
tabTarget.on("workerListChanged", workerListChanged);
|
||||
}
|
||||
|
||||
async function paused(
|
||||
|
@ -15,7 +15,8 @@ export async function updateWorkerClients({
|
||||
tabTarget,
|
||||
debuggerClient,
|
||||
threadClient,
|
||||
workerClients
|
||||
workerClients,
|
||||
options
|
||||
}: Object) {
|
||||
if (!supportsWorkers(tabTarget)) {
|
||||
return {};
|
||||
@ -26,7 +27,7 @@ export async function updateWorkerClients({
|
||||
const { workers } = await tabTarget.listWorkers();
|
||||
for (const workerTargetFront of workers) {
|
||||
await workerTargetFront.attach();
|
||||
const [, workerThread] = await workerTargetFront.attachThread();
|
||||
const [, workerThread] = await workerTargetFront.attachThread(options);
|
||||
|
||||
if (workerClients[workerThread.actor]) {
|
||||
if (workerClients[workerThread.actor].thread != workerThread) {
|
||||
|
@ -653,6 +653,7 @@ support-files =
|
||||
examples/script-switching-01.js
|
||||
examples/times2.js
|
||||
examples/doc-windowless-workers.html
|
||||
examples/doc-windowless-workers-early-breakpoint.html
|
||||
examples/simple-worker.js
|
||||
examples/doc-event-handler.html
|
||||
examples/doc-eval-throw.html
|
||||
@ -770,5 +771,6 @@ skip-if = os == "win"
|
||||
[browser_dbg-wasm-sourcemaps.js]
|
||||
skip-if = true
|
||||
[browser_dbg-windowless-workers.js]
|
||||
[browser_dbg-windowless-workers-early-breakpoint.js]
|
||||
[browser_dbg-event-handler.js]
|
||||
[browser_dbg-eval-throw.js]
|
||||
|
@ -0,0 +1,30 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that breakpoints at worker startup are hit when using windowless workers.
|
||||
add_task(async function() {
|
||||
const dbg = await initDebugger("doc-windowless-workers-early-breakpoint.html", "simple-worker.js");
|
||||
|
||||
const workerSource = findSource(dbg, "simple-worker.js");
|
||||
|
||||
await addBreakpoint(dbg, workerSource, 1);
|
||||
invokeInTab("startWorker");
|
||||
await waitForPaused(dbg, "simple-worker.js");
|
||||
|
||||
// We should be paused at the first line of simple-worker.js
|
||||
assertPausedAtSourceAndLine(dbg, workerSource.id, 1);
|
||||
await removeBreakpoint(dbg, workerSource.id, 1);
|
||||
await resume(dbg);
|
||||
|
||||
// Make sure that suspending activity in the worker when attaching does not
|
||||
// interfere with sending messages to the worker.
|
||||
await addBreakpoint(dbg, workerSource, 10);
|
||||
invokeInTab("startWorkerWithMessage");
|
||||
await waitForPaused(dbg, "simple-worker.js");
|
||||
|
||||
// We should be paused in the message listener in simple-worker.js
|
||||
assertPausedAtSourceAndLine(dbg, workerSource.id, 10);
|
||||
await removeBreakpoint(dbg, workerSource.id, 10);
|
||||
});
|
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<script>
|
||||
startWorker();
|
||||
|
||||
function startWorker() {
|
||||
new Worker("simple-worker.js");
|
||||
}
|
||||
function startWorkerWithMessage() {
|
||||
var w = new Worker("simple-worker.js");
|
||||
w.postMessage({yo: 'yo'})
|
||||
}
|
||||
</script>
|
||||
|
||||
<body>
|
||||
Hello World!
|
||||
</body>
|
||||
</html>
|
@ -8,4 +8,4 @@ setInterval(timer, 1000);
|
||||
|
||||
self.onmessage = () => {
|
||||
console.log('hi')
|
||||
}
|
||||
}
|
||||
|
@ -627,19 +627,26 @@ const browsingContextTargetPrototype = {
|
||||
return { frames: windows };
|
||||
},
|
||||
|
||||
listWorkers(request) {
|
||||
if (!this.attached) {
|
||||
return { error: "wrongState" };
|
||||
}
|
||||
|
||||
ensureWorkerTargetActorList() {
|
||||
if (this._workerTargetActorList === null) {
|
||||
this._workerTargetActorList = new WorkerTargetActorList(this.conn, {
|
||||
type: Ci.nsIWorkerDebugger.TYPE_DEDICATED,
|
||||
window: this.window,
|
||||
});
|
||||
}
|
||||
return this._workerTargetActorList;
|
||||
},
|
||||
|
||||
return this._workerTargetActorList.getList().then((actors) => {
|
||||
pauseWorkersUntilAttach(shouldPause) {
|
||||
this.ensureWorkerTargetActorList().setPauseMatchingWorkers(shouldPause);
|
||||
},
|
||||
|
||||
listWorkers(request) {
|
||||
if (!this.attached) {
|
||||
return { error: "wrongState" };
|
||||
}
|
||||
|
||||
return this.ensureWorkerTargetActorList().getList().then((actors) => {
|
||||
const pool = new Pool(this.conn);
|
||||
for (const actor of actors) {
|
||||
pool.manage(actor);
|
||||
@ -886,6 +893,7 @@ const browsingContextTargetPrototype = {
|
||||
// Make sure that no more workerListChanged notifications are sent.
|
||||
if (this._workerTargetActorList !== null) {
|
||||
this._workerTargetActorList.onListChanged = null;
|
||||
this._workerTargetActorList.setPauseMatchingWorkers(false);
|
||||
this._workerTargetActorList = null;
|
||||
}
|
||||
|
||||
|
@ -270,8 +270,22 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
thread: this,
|
||||
});
|
||||
|
||||
if (request.options.breakpoints) {
|
||||
for (const { location, options } of Object.values(request.options.breakpoints)) {
|
||||
this.setBreakpoint(location, options);
|
||||
}
|
||||
}
|
||||
|
||||
this.dbg.addDebuggees();
|
||||
this.dbg.enabled = true;
|
||||
|
||||
// Notify the parent that we've finished attaching. If this is a worker
|
||||
// thread which was paused until attaching, this will allow content to
|
||||
// begin executing.
|
||||
if (this._parent.onThreadAttached) {
|
||||
this._parent.onThreadAttached();
|
||||
}
|
||||
|
||||
try {
|
||||
// Put ourselves in the paused state.
|
||||
const packet = this._paused();
|
||||
@ -449,6 +463,12 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
this.skipBreakpoints = options.skipBreakpoints;
|
||||
}
|
||||
|
||||
if ("pauseWorkersUntilAttach" in options) {
|
||||
if (this._parent.pauseWorkersUntilAttach) {
|
||||
this._parent.pauseWorkersUntilAttach(options.pauseWorkersUntilAttach);
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(this._options, options);
|
||||
|
||||
// Update the global source store
|
||||
@ -1104,6 +1124,8 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
},
|
||||
|
||||
onSources: function(request) {
|
||||
// FIXME bug 1530699 we should make sure that existing breakpoints are
|
||||
// applied to any sources we find here.
|
||||
for (const source of this.dbg.findSources()) {
|
||||
this.sources.createSourceActor(source);
|
||||
}
|
||||
|
@ -31,11 +31,40 @@ function matchWorkerDebugger(dbg, options) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// When a new worker appears, in some cases (i.e. the debugger is running) we
|
||||
// want it to pause during registration until a later time (i.e. the debugger
|
||||
// finishes attaching to the worker). This is an optional WorkderDebuggerManager
|
||||
// listener that can be installed in addition to the WorkerTargetActorList
|
||||
// listener. It always listens to new workers and pauses any which are matched
|
||||
// by the WorkerTargetActorList.
|
||||
function PauseMatchingWorkers(options) {
|
||||
this._options = options;
|
||||
this.onRegister = this._onRegister.bind(this);
|
||||
this.onUnregister = () => {};
|
||||
|
||||
wdm.addListener(this);
|
||||
}
|
||||
|
||||
PauseMatchingWorkers.prototype = {
|
||||
destroy() {
|
||||
wdm.removeListener(this);
|
||||
},
|
||||
|
||||
_onRegister(dbg) {
|
||||
if (matchWorkerDebugger(dbg, this._options)) {
|
||||
// Prevent the debuggee from executing in this worker until the debugger
|
||||
// has finished attaching to it.
|
||||
dbg.setDebuggerReady(false);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function WorkerTargetActorList(conn, options) {
|
||||
this._conn = conn;
|
||||
this._options = options;
|
||||
this._actors = new Map();
|
||||
this._onListChanged = null;
|
||||
this._pauseMatchingWorkers = null;
|
||||
this._mustNotify = false;
|
||||
this.onRegister = this.onRegister.bind(this);
|
||||
this.onUnregister = this.onUnregister.bind(this);
|
||||
@ -123,6 +152,17 @@ WorkerTargetActorList.prototype = {
|
||||
this._notifyListChanged();
|
||||
}
|
||||
},
|
||||
|
||||
setPauseMatchingWorkers(shouldPause) {
|
||||
if (shouldPause != !!this._pauseMatchingWorkers) {
|
||||
if (shouldPause) {
|
||||
this._pauseMatchingWorkers = new PauseMatchingWorkers(this._options);
|
||||
} else {
|
||||
this._pauseMatchingWorkers.destroy();
|
||||
this._pauseMatchingWorkers = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
exports.WorkerTargetActorList = WorkerTargetActorList;
|
||||
|
@ -398,6 +398,11 @@ var DebuggerServer = {
|
||||
onMessage: (message) => {
|
||||
message = JSON.parse(message);
|
||||
if (message.type !== "rpc") {
|
||||
if (message.type == "attached") {
|
||||
// The thread actor has finished attaching and can hit installed
|
||||
// breakpoints. Allow content to begin executing in the worker.
|
||||
dbg.setDebuggerReady(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,10 @@ this.addEventListener("message", function(event) {
|
||||
},
|
||||
|
||||
window: global,
|
||||
|
||||
onThreadAttached() {
|
||||
postMessage(JSON.stringify({ type: "attached" }));
|
||||
},
|
||||
};
|
||||
|
||||
const threadActor = new ThreadActor(parent, global);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "mozilla/Algorithm.h"
|
||||
#include "mozilla/ipc/CrashReporterClient.h"
|
||||
#include "mozilla/ipc/ProcessChild.h"
|
||||
#include "mozilla/TextUtils.h"
|
||||
#include "GMPUtils.h"
|
||||
#include "prio.h"
|
||||
#include "base/task.h"
|
||||
@ -264,7 +265,8 @@ mozilla::ipc::IPCResult GMPChild::RecvPreloadLibs(const nsCString& aLibs) {
|
||||
u"msmpeg2vdec.dll", // H.264 decoder
|
||||
u"psapi.dll", // For GetMappedFileNameW, see bug 1383611
|
||||
};
|
||||
constexpr static bool (*IsASCII)(const char16_t*) = NS_IsAscii;
|
||||
constexpr static bool (*IsASCII)(const char16_t*) =
|
||||
IsAsciiNullTerminated<char16_t>;
|
||||
static_assert(AllOf(std::begin(whitelist), std::end(whitelist), IsASCII),
|
||||
"Items in the whitelist must not contain non-ASCII "
|
||||
"characters!");
|
||||
|
@ -4,6 +4,8 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/TextUtils.h"
|
||||
|
||||
#include "FunctionHook.h"
|
||||
#include "FunctionBroker.h"
|
||||
#include "nsClassHashtable.h"
|
||||
@ -65,7 +67,7 @@ WindowsDllInterceptor* FunctionHook::GetDllInterceptorFor(
|
||||
sDllInterceptorCache = new DllInterceptors();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_IsAscii(aModuleName),
|
||||
MOZ_ASSERT(IsAsciiNullTerminated(aModuleName),
|
||||
"Non-ASCII module names are not supported");
|
||||
NS_ConvertASCIItoUTF16 moduleName(aModuleName);
|
||||
|
||||
|
@ -138,7 +138,9 @@ add_task(async function test() {
|
||||
Assert.ok(isTopLevel, "example.com as a top level window");
|
||||
Assert.ok(aboutMemoryFound, "about:memory");
|
||||
Assert.ok(heapUsage > 0, "got some memory value reported");
|
||||
Assert.ok(sharedWorker, "We got some info from a shared worker");
|
||||
// FIXME bug 1522246. the worker performance improvements in this bug cause
|
||||
// the shared worker to shut down too quickly to report any info.
|
||||
// Assert.ok(sharedWorker, "We got some info from a shared worker");
|
||||
let numCounters = counterIds.length;
|
||||
Assert.ok(numCounters > 5, "This test generated at least " + numCounters + " unique counters");
|
||||
|
||||
|
@ -88,6 +88,17 @@ class CompileDebuggerScriptRunnable final : public WorkerDebuggerRunnable {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize performance state which might be used on the main thread, as
|
||||
// in CompileScriptRunnable. This runnable might execute first.
|
||||
aWorkerPrivate->EnsurePerformanceStorage();
|
||||
if (mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) {
|
||||
aWorkerPrivate->EnsurePerformanceCounter();
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
|
||||
|
||||
ErrorResult rv;
|
||||
@ -368,6 +379,12 @@ WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebugger::SetDebuggerReady(bool aReady)
|
||||
{
|
||||
return mWorkerPrivate->SetIsDebuggerReady(aReady);
|
||||
}
|
||||
|
||||
void WorkerDebugger::Close() {
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate = nullptr;
|
||||
|
@ -299,13 +299,14 @@ class ModifyBusyCountRunnable final : public WorkerControlRunnable {
|
||||
}
|
||||
};
|
||||
|
||||
class CompileScriptRunnable final : public WorkerRunnable {
|
||||
class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
|
||||
nsString mScriptURL;
|
||||
|
||||
public:
|
||||
explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
const nsAString& aScriptURL)
|
||||
: WorkerRunnable(aWorkerPrivate), mScriptURL(aScriptURL) {}
|
||||
: WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
|
||||
mScriptURL(aScriptURL) {}
|
||||
|
||||
private:
|
||||
// We can't implement PreRun effectively, because at the point when that would
|
||||
@ -1332,51 +1333,60 @@ void WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb) {
|
||||
nsresult WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
|
||||
nsIEventTarget* aSyncLoopTarget) {
|
||||
// May be called on any thread!
|
||||
MutexAutoLock lock(mMutex);
|
||||
return DispatchLockHeld(std::move(aRunnable), aSyncLoopTarget, lock);
|
||||
}
|
||||
|
||||
nsresult WorkerPrivate::DispatchLockHeld(already_AddRefed<WorkerRunnable> aRunnable,
|
||||
nsIEventTarget* aSyncLoopTarget,
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
// May be called on any thread!
|
||||
RefPtr<WorkerRunnable> runnable(aRunnable);
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
|
||||
|
||||
MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
|
||||
|
||||
if (!mThread) {
|
||||
if (ParentStatus() == Pending || mStatus == Pending) {
|
||||
mPreStartRunnables.AppendElement(runnable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_WARNING(
|
||||
"Using a worker event target after the thread has already"
|
||||
"been released!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Running)) {
|
||||
NS_WARNING(
|
||||
"A runnable was posted to a worker that is already shutting "
|
||||
"down!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (aSyncLoopTarget) {
|
||||
rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
// WorkerDebuggeeRunnables don't need any special treatment here. True,
|
||||
// they should not be delivered to a frozen worker. But frozen workers
|
||||
// aren't drawing from the thread's main event queue anyway, only from
|
||||
// mControlQueue.
|
||||
rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(),
|
||||
runnable.forget());
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mCondVar.Notify();
|
||||
if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Running)) {
|
||||
NS_WARNING(
|
||||
"A runnable was posted to a worker that is already shutting "
|
||||
"down!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (runnable->IsDebuggeeRunnable() && !mDebuggerReady) {
|
||||
MOZ_RELEASE_ASSERT(!aSyncLoopTarget);
|
||||
mDelayedDebuggeeRunnables.AppendElement(runnable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mThread) {
|
||||
if (ParentStatus() == Pending || mStatus == Pending) {
|
||||
mPreStartRunnables.AppendElement(runnable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_WARNING(
|
||||
"Using a worker event target after the thread has already"
|
||||
"been released!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (aSyncLoopTarget) {
|
||||
rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
// WorkerDebuggeeRunnables don't need any special treatment here. True,
|
||||
// they should not be delivered to a frozen worker. But frozen workers
|
||||
// aren't drawing from the thread's main event queue anyway, only from
|
||||
// mControlQueue.
|
||||
rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(),
|
||||
runnable.forget());
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mCondVar.Notify();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2061,6 +2071,7 @@ WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
|
||||
mIsSecureContext(
|
||||
IsNewWorkerSecureContext(mParent, mWorkerType, mLoadInfo)),
|
||||
mDebuggerRegistered(false),
|
||||
mDebuggerReady(true),
|
||||
mIsInAutomation(false),
|
||||
mPerformanceCounter(nullptr) {
|
||||
MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
|
||||
@ -2243,6 +2254,39 @@ already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor(
|
||||
return worker.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
WorkerPrivate::SetIsDebuggerReady(bool aReady)
|
||||
{
|
||||
AssertIsOnParentThread();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mDebuggerReady == aReady) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!aReady && mDebuggerRegistered) {
|
||||
// The debugger can only be marked as not ready during registration.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mDebuggerReady = aReady;
|
||||
|
||||
if (aReady && mDebuggerRegistered) {
|
||||
// Dispatch all the delayed runnables without releasing the lock, to ensure
|
||||
// that the order in which debuggee runnables execute is the same as the
|
||||
// order in which they were originally dispatched.
|
||||
auto pending = std::move(mDelayedDebuggeeRunnables);
|
||||
for (uint32_t i = 0; i < pending.Length(); i++) {
|
||||
RefPtr<WorkerRunnable> runnable = pending[i].forget();
|
||||
nsresult rv = DispatchLockHeld(runnable.forget(), nullptr, lock);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
||||
WorkerPrivate* aParent,
|
||||
@ -2572,9 +2616,15 @@ void WorkerPrivate::DoRunLoop(JSContext* aCx) {
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
// Wait for a runnable to arrive that we can execute, or for it to be okay
|
||||
// to shutdown this worker once all holders have been removed.
|
||||
// Holders may be removed from inside normal runnables, but we don't check
|
||||
// for that after processing normal runnables, so we need to let control
|
||||
// flow to the shutdown logic without blocking.
|
||||
while (mControlQueue.IsEmpty() &&
|
||||
!(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
|
||||
!(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
|
||||
!(normalRunnablesPending = NS_HasPendingEvents(mThread)) &&
|
||||
!(mStatus != Running && !HasActiveHolders())) {
|
||||
WaitForWorkerEvents();
|
||||
}
|
||||
|
||||
|
@ -170,6 +170,8 @@ class WorkerPrivate : public RelativeTimeline {
|
||||
}
|
||||
}
|
||||
|
||||
nsresult SetIsDebuggerReady(bool aReady);
|
||||
|
||||
WorkerDebugger* Debugger() const {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
@ -907,6 +909,12 @@ class WorkerPrivate : public RelativeTimeline {
|
||||
data->mHolders.IsEmpty());
|
||||
}
|
||||
|
||||
// Internal logic to dispatch a runnable. This is separate from Dispatch()
|
||||
// to allow runnables to be atomically dispatched in bulk.
|
||||
nsresult DispatchLockHeld(already_AddRefed<WorkerRunnable> aRunnable,
|
||||
nsIEventTarget* aSyncLoopTarget,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
|
||||
class EventTarget;
|
||||
friend class EventTarget;
|
||||
friend class mozilla::dom::WorkerHolder;
|
||||
@ -1074,6 +1082,13 @@ class WorkerPrivate : public RelativeTimeline {
|
||||
|
||||
bool mDebuggerRegistered;
|
||||
|
||||
// During registration, this worker may be marked as not being ready to
|
||||
// execute debuggee runnables or content.
|
||||
//
|
||||
// Protected by mMutex.
|
||||
bool mDebuggerReady;
|
||||
nsTArray<RefPtr<WorkerRunnable>> mDelayedDebuggeeRunnables;
|
||||
|
||||
// mIsInAutomation is true when we're running in test automation.
|
||||
// We expose some extra testing functions in that case.
|
||||
bool mIsInAutomation;
|
||||
|
@ -47,4 +47,17 @@ interface nsIWorkerDebugger : nsISupports
|
||||
void addListener(in nsIWorkerDebuggerListener listener);
|
||||
|
||||
void removeListener(in nsIWorkerDebuggerListener listener);
|
||||
|
||||
// Indicate whether the debugger has finished initializing. By default the
|
||||
// debugger will be considered initialized when the onRegister hooks in all
|
||||
// nsIWorkerDebuggerManagerListener have been called.
|
||||
//
|
||||
// setDebuggerReady(false) can be called during an onRegister hook to mark
|
||||
// the debugger as not being ready yet. This will prevent all content from
|
||||
// running in the worker, including the worker's main script and any messages
|
||||
// posted to it. Other runnables will still execute in the worker as normal.
|
||||
//
|
||||
// When the debugger is ready, setDebuggerReady(true) should then be called
|
||||
// to allow the worker to begin executing content.
|
||||
void setDebuggerReady(in boolean ready);
|
||||
};
|
||||
|
@ -193,7 +193,8 @@ void RotatedBuffer::DrawBufferWithRotation(
|
||||
aMaskTransform);
|
||||
}
|
||||
|
||||
bool IsClippingCheap(gfx::DrawTarget* aTarget, const nsIntRegion& aRegion) {
|
||||
static bool IsClippingCheap(gfx::DrawTarget* aTarget,
|
||||
const nsIntRegion& aRegion) {
|
||||
// Assume clipping is cheap if the draw target just has an integer
|
||||
// translation, and the visible region is simple.
|
||||
return !aTarget->GetTransform().HasNonIntegerTranslation() &&
|
||||
|
@ -53,7 +53,7 @@ struct WaitForTexturesRequest {
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
std::unordered_set<uint64_t>* GetLockedTextureIdsForProcess(pid_t pid) {
|
||||
static std::unordered_set<uint64_t>* GetLockedTextureIdsForProcess(pid_t pid) {
|
||||
gTextureLockMonitor.AssertCurrentThreadOwns();
|
||||
|
||||
if (gProcessTextureIds.find(pid) == gProcessTextureIds.end()) {
|
||||
@ -63,8 +63,8 @@ std::unordered_set<uint64_t>* GetLockedTextureIdsForProcess(pid_t pid) {
|
||||
return &gProcessTextureIds.at(pid);
|
||||
}
|
||||
|
||||
bool WaitForTextureIdsToUnlock(pid_t pid,
|
||||
const Span<const uint64_t>& textureIds) {
|
||||
static bool WaitForTextureIdsToUnlock(pid_t pid,
|
||||
const Span<const uint64_t>& textureIds) {
|
||||
{
|
||||
StaticMonitorAutoLock lock(gTextureLockMonitor);
|
||||
std::unordered_set<uint64_t>* freedTextureIds =
|
||||
@ -99,7 +99,7 @@ bool WaitForTextureIdsToUnlock(pid_t pid,
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTexturesForUnlock() {
|
||||
static void CheckTexturesForUnlock() {
|
||||
if (gTextureSourceProviders) {
|
||||
for (auto it = gTextureSourceProviders->begin();
|
||||
it != gTextureSourceProviders->end(); ++it) {
|
||||
|
@ -1766,7 +1766,7 @@ nsEventStatus APZCTreeManager::ProcessTouchInput(
|
||||
return result;
|
||||
}
|
||||
|
||||
MouseInput::MouseType MultiTouchTypeToMouseType(
|
||||
static MouseInput::MouseType MultiTouchTypeToMouseType(
|
||||
MultiTouchInput::MultiTouchType aType) {
|
||||
switch (aType) {
|
||||
case MultiTouchInput::MULTITOUCH_START:
|
||||
|
@ -3719,7 +3719,7 @@ void AsyncPanZoomController::RequestContentRepaint(
|
||||
RequestContentRepaint(Metrics(), velocity, aUpdateType);
|
||||
}
|
||||
|
||||
/*static*/ CSSRect GetDisplayPortRect(const FrameMetrics& aFrameMetrics) {
|
||||
static CSSRect GetDisplayPortRect(const FrameMetrics& aFrameMetrics) {
|
||||
// This computation is based on what happens in CalculatePendingDisplayPort.
|
||||
// If that changes then this might need to change too
|
||||
CSSRect baseRect(aFrameMetrics.GetScrollOffset(),
|
||||
|
@ -38,13 +38,13 @@ static const float ONE_TOUCH_PINCH_SPEED = 0.005f;
|
||||
|
||||
static bool sLongTapEnabled = true;
|
||||
|
||||
ScreenPoint GetCurrentFocus(const MultiTouchInput& aEvent) {
|
||||
static ScreenPoint GetCurrentFocus(const MultiTouchInput& aEvent) {
|
||||
const ScreenPoint& firstTouch = aEvent.mTouches[0].mScreenPoint;
|
||||
const ScreenPoint& secondTouch = aEvent.mTouches[1].mScreenPoint;
|
||||
return (firstTouch + secondTouch) / 2;
|
||||
}
|
||||
|
||||
ScreenCoord GetCurrentSpan(const MultiTouchInput& aEvent) {
|
||||
static ScreenCoord GetCurrentSpan(const MultiTouchInput& aEvent) {
|
||||
const ScreenPoint& firstTouch = aEvent.mTouches[0].mScreenPoint;
|
||||
const ScreenPoint& secondTouch = aEvent.mTouches[1].mScreenPoint;
|
||||
ScreenPoint delta = secondTouch - firstTouch;
|
||||
@ -59,8 +59,8 @@ ScreenCoord GestureEventListener::GetYSpanFromGestureStartPoint() {
|
||||
return current.y - start.y;
|
||||
}
|
||||
|
||||
TapGestureInput CreateTapEvent(const MultiTouchInput& aTouch,
|
||||
TapGestureInput::TapGestureType aType) {
|
||||
static TapGestureInput CreateTapEvent(const MultiTouchInput& aTouch,
|
||||
TapGestureInput::TapGestureType aType) {
|
||||
return TapGestureInput(aType, aTouch.mTime, aTouch.mTimeStamp,
|
||||
aTouch.mTouches[0].mScreenPoint, aTouch.modifiers);
|
||||
}
|
||||
|
@ -48,16 +48,17 @@ typedef mozilla::layers::GeckoContentController::TapType TapType;
|
||||
// passed either to an APZC (which expects an transformed point), or to an APZTM
|
||||
// (which expects an untransformed point). We handle both cases by setting both
|
||||
// the transformed and untransformed fields to the same value.
|
||||
SingleTouchData CreateSingleTouchData(int32_t aIdentifier,
|
||||
const ScreenIntPoint& aPoint) {
|
||||
inline SingleTouchData CreateSingleTouchData(int32_t aIdentifier,
|
||||
const ScreenIntPoint& aPoint) {
|
||||
SingleTouchData touch(aIdentifier, aPoint, ScreenSize(0, 0), 0, 0);
|
||||
touch.mLocalScreenPoint = ParentLayerPoint(aPoint.x, aPoint.y);
|
||||
return touch;
|
||||
}
|
||||
|
||||
// Convenience wrapper for CreateSingleTouchData() that takes loose coordinates.
|
||||
SingleTouchData CreateSingleTouchData(int32_t aIdentifier, ScreenIntCoord aX,
|
||||
ScreenIntCoord aY) {
|
||||
inline SingleTouchData CreateSingleTouchData(int32_t aIdentifier,
|
||||
ScreenIntCoord aX,
|
||||
ScreenIntCoord aY) {
|
||||
return CreateSingleTouchData(aIdentifier, ScreenIntPoint(aX, aY));
|
||||
}
|
||||
|
||||
@ -805,7 +806,7 @@ AsyncPanZoomController* TestAPZCTreeManager::NewAPZCInstance(
|
||||
aLayersId, mcc, this, AsyncPanZoomController::USE_GESTURE_DETECTOR);
|
||||
}
|
||||
|
||||
FrameMetrics TestFrameMetrics() {
|
||||
inline FrameMetrics TestFrameMetrics() {
|
||||
FrameMetrics fm;
|
||||
|
||||
fm.SetDisplayPort(CSSRect(0, 0, 10, 10));
|
||||
@ -816,7 +817,7 @@ FrameMetrics TestFrameMetrics() {
|
||||
return fm;
|
||||
}
|
||||
|
||||
uint32_t MillisecondsSinceStartup(TimeStamp aTime) {
|
||||
inline uint32_t MillisecondsSinceStartup(TimeStamp aTime) {
|
||||
return (aTime - GetStartupTime()).ToMilliseconds();
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
* code to dispatch input events.
|
||||
*/
|
||||
|
||||
PinchGestureInput CreatePinchGestureInput(
|
||||
inline PinchGestureInput CreatePinchGestureInput(
|
||||
PinchGestureInput::PinchGestureType aType, const ScreenPoint& aFocus,
|
||||
float aCurrentSpan, float aPreviousSpan) {
|
||||
ParentLayerPoint localFocus(aFocus.x, aFocus.y);
|
||||
@ -53,8 +53,8 @@ void SetDefaultAllowedTouchBehavior(const RefPtr<InputReceiver>& aTarget,
|
||||
aTarget->SetAllowedTouchBehavior(aInputBlockId, defaultBehaviors);
|
||||
}
|
||||
|
||||
MultiTouchInput CreateMultiTouchInput(MultiTouchInput::MultiTouchType aType,
|
||||
TimeStamp aTime) {
|
||||
inline MultiTouchInput CreateMultiTouchInput(
|
||||
MultiTouchInput::MultiTouchType aType, TimeStamp aTime) {
|
||||
return MultiTouchInput(aType, MillisecondsSinceStartup(aTime), aTime, 0);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
# The following tests test the async positioning of the scrollbars.
|
||||
# Basic root-frame scrollbar with async scrolling
|
||||
fuzzy-if(Android,1-1,1-2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html # bug 1392259
|
||||
fuzzy-if(Android,1-1,1-2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html
|
||||
fuzzy-if(Android,0-4,0-5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-h.html async-scrollbar-1-h-ref.html
|
||||
fuzzy-if(Android,0-3,0-5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html
|
||||
fuzzy-if(Android,1-1,1-2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html # bug 1392259
|
||||
fuzzy-if(Android,1-1,1-2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html
|
||||
fuzzy-if(Android,0-4,0-5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html
|
||||
fuzzy-if(Android,0-3,0-7) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl-ref.html
|
||||
|
||||
|
@ -15,6 +15,10 @@
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
already_AddRefed<TextureHost> CreateTextureHostBasic(
|
||||
const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
|
||||
LayersBackend aBackend, TextureFlags aFlags);
|
||||
|
||||
/**
|
||||
* A texture source interface that can be used by the software Compositor.
|
||||
*/
|
||||
|
@ -301,9 +301,9 @@ void ContentClient::EndPaint(
|
||||
}
|
||||
}
|
||||
|
||||
nsIntRegion ExpandDrawRegion(ContentClient::PaintState& aPaintState,
|
||||
RotatedBuffer::DrawIterator* aIter,
|
||||
BackendType aBackendType) {
|
||||
static nsIntRegion ExpandDrawRegion(ContentClient::PaintState& aPaintState,
|
||||
RotatedBuffer::DrawIterator* aIter,
|
||||
BackendType aBackendType) {
|
||||
nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
|
||||
if (aIter) {
|
||||
// The iterators draw region currently only contains the bounds of the
|
||||
|
@ -300,8 +300,9 @@ void TextureChild::Destroy(const TextureDeallocParams& aParams) {
|
||||
|
||||
/* static */ Atomic<uint64_t> TextureClient::sSerialCounter(0);
|
||||
|
||||
void DeallocateTextureClientSyncProxy(TextureDeallocParams params,
|
||||
ReentrantMonitor* aBarrier, bool* aDone) {
|
||||
static void DeallocateTextureClientSyncProxy(TextureDeallocParams params,
|
||||
ReentrantMonitor* aBarrier,
|
||||
bool* aDone) {
|
||||
DeallocateTextureClient(params);
|
||||
ReentrantMonitorAutoEnter autoMon(*aBarrier);
|
||||
*aDone = true;
|
||||
@ -791,8 +792,8 @@ void TextureClient::SetAddedToCompositableClient() {
|
||||
}
|
||||
}
|
||||
|
||||
void CancelTextureClientRecycle(uint64_t aTextureId,
|
||||
LayersIPCChannel* aAllocator) {
|
||||
static void CancelTextureClientRecycle(uint64_t aTextureId,
|
||||
LayersIPCChannel* aAllocator) {
|
||||
if (!aAllocator) {
|
||||
return;
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ enum class TilePaintFlags : uint8_t {
|
||||
};
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TilePaintFlags)
|
||||
|
||||
void ShutdownTileCache();
|
||||
|
||||
struct AcquiredBackBuffer {
|
||||
AcquiredBackBuffer(gfx::DrawTarget* aTarget, gfx::DrawTargetCapture* aCapture,
|
||||
gfx::DrawTarget* aBackBuffer,
|
||||
|
@ -25,10 +25,11 @@
|
||||
#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget
|
||||
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
|
||||
#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
|
||||
#include "mozilla/mozalloc.h" // for operator delete, etc
|
||||
#include "mozilla/RefPtr.h" // for nsRefPtr
|
||||
#include "nsDebug.h" // for NS_ASSERTION
|
||||
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
|
||||
#include "mozilla/layers/LayersHelpers.h"
|
||||
#include "mozilla/mozalloc.h" // for operator delete, etc
|
||||
#include "mozilla/RefPtr.h" // for nsRefPtr
|
||||
#include "nsDebug.h" // for NS_ASSERTION
|
||||
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
|
||||
#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
|
||||
#include "nsRegion.h" // for nsIntRegion
|
||||
#include "nsTArray.h" // for AutoTArray
|
||||
|
@ -231,7 +231,7 @@ void LayerManagerComposite::PostProcessLayers(nsIntRegion& aOpaqueRegion) {
|
||||
// intermediate surface. We compute occlusions for leaves and intermediate
|
||||
// surfaces against the layer that they actually composite into so that we can
|
||||
// use the final (snapped) effective transform.
|
||||
bool ShouldProcessLayer(Layer* aLayer) {
|
||||
static bool ShouldProcessLayer(Layer* aLayer) {
|
||||
if (!aLayer->AsContainerLayer()) {
|
||||
return true;
|
||||
}
|
||||
@ -1406,8 +1406,8 @@ static void AddTransformedRegion(LayerIntRegion& aDest,
|
||||
// Async animations can move child layers without updating our visible region.
|
||||
// PostProcessLayers will recompute visible regions for layers with an
|
||||
// intermediate surface, but otherwise we need to do it now.
|
||||
void ComputeVisibleRegionForChildren(ContainerLayer* aContainer,
|
||||
LayerIntRegion& aResult) {
|
||||
static void ComputeVisibleRegionForChildren(ContainerLayer* aContainer,
|
||||
LayerIntRegion& aResult) {
|
||||
for (Layer* l = aContainer->GetFirstChild(); l; l = l->GetNextSibling()) {
|
||||
if (l->Extend3DContext()) {
|
||||
MOZ_ASSERT(l->AsContainerLayer());
|
||||
|
@ -53,6 +53,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
# include "mozilla/layers/TextureD3D11.h"
|
||||
# include "mozilla/layers/TextureDIB.h"
|
||||
#endif
|
||||
|
||||
@ -163,21 +164,6 @@ void TextureHost::SetLastFwdTransactionId(uint64_t aTransactionId) {
|
||||
mFwdTransactionId = aTransactionId;
|
||||
}
|
||||
|
||||
// implemented in TextureHostOGL.cpp
|
||||
already_AddRefed<TextureHost> CreateTextureHostOGL(
|
||||
const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
|
||||
LayersBackend aBackend, TextureFlags aFlags);
|
||||
|
||||
// implemented in TextureHostBasic.cpp
|
||||
already_AddRefed<TextureHost> CreateTextureHostBasic(
|
||||
const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
|
||||
LayersBackend aBackend, TextureFlags aFlags);
|
||||
|
||||
// implemented in TextureD3D11.cpp
|
||||
already_AddRefed<TextureHost> CreateTextureHostD3D11(
|
||||
const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
|
||||
LayersBackend aBackend, TextureFlags aFlags);
|
||||
|
||||
already_AddRefed<TextureHost> TextureHost::Create(
|
||||
const SurfaceDescriptor& aDesc, const ReadLockDescriptor& aReadLock,
|
||||
ISurfaceAllocator* aDeallocator, LayersBackend aBackend,
|
||||
|
@ -153,10 +153,10 @@ bool TiledContentHost::UseTiledLayerBuffer(
|
||||
return true;
|
||||
}
|
||||
|
||||
void UseTileTexture(CompositableTextureHostRef& aTexture,
|
||||
CompositableTextureSourceRef& aTextureSource,
|
||||
const IntRect& aUpdateRect,
|
||||
TextureSourceProvider* aProvider) {
|
||||
static void UseTileTexture(CompositableTextureHostRef& aTexture,
|
||||
CompositableTextureSourceRef& aTextureSource,
|
||||
const IntRect& aUpdateRect,
|
||||
TextureSourceProvider* aProvider) {
|
||||
MOZ_ASSERT(aTexture);
|
||||
if (!aTexture) {
|
||||
return;
|
||||
|
@ -25,6 +25,10 @@ class GLBlitHelper;
|
||||
|
||||
namespace layers {
|
||||
|
||||
already_AddRefed<TextureHost> CreateTextureHostD3D11(
|
||||
const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
|
||||
LayersBackend aBackend, TextureFlags aFlags);
|
||||
|
||||
class MOZ_RAII AutoTextureLock {
|
||||
public:
|
||||
AutoTextureLock(IDXGIKeyedMutex* aMutex, HRESULT& aResult,
|
||||
|
@ -1824,7 +1824,7 @@ Maybe<TimeStamp> CompositorBridgeParent::GetTestingTimeStamp() const {
|
||||
return mTestTime;
|
||||
}
|
||||
|
||||
void EraseLayerState(LayersId aId) {
|
||||
static void EraseLayerState(LayersId aId) {
|
||||
RefPtr<APZUpdater> apz;
|
||||
|
||||
{ // scope lock
|
||||
@ -2136,8 +2136,8 @@ void CompositorBridgeParent::InvalidateRemoteLayers() {
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateIndirectTree(LayersId aId, Layer* aRoot,
|
||||
const TargetConfig& aTargetConfig) {
|
||||
static void UpdateIndirectTree(LayersId aId, Layer* aRoot,
|
||||
const TargetConfig& aTargetConfig) {
|
||||
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
||||
sIndirectLayerTrees[aId].mRoot = aRoot;
|
||||
sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig;
|
||||
|
@ -26,7 +26,7 @@ using namespace std;
|
||||
#define GAUSSIAN_KERNEL_HALF_WIDTH 11
|
||||
#define GAUSSIAN_KERNEL_STEP 0.2
|
||||
|
||||
void AddUniforms(ProgramProfileOGL &aProfile) {
|
||||
static void AddUniforms(ProgramProfileOGL &aProfile) {
|
||||
// This needs to be kept in sync with the KnownUniformName enum
|
||||
static const char *sKnownUniformNames[] = {"uLayerTransform",
|
||||
"uLayerTransformInverse",
|
||||
|
@ -53,6 +53,10 @@ void ApplySamplingFilterToBoundTexture(gl::GLContext* aGL,
|
||||
gfx::SamplingFilter aSamplingFilter,
|
||||
GLuint aTarget = LOCAL_GL_TEXTURE_2D);
|
||||
|
||||
already_AddRefed<TextureHost> CreateTextureHostOGL(
|
||||
const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
|
||||
LayersBackend aBackend, TextureFlags aFlags);
|
||||
|
||||
/*
|
||||
* TextureHost implementations for the OpenGL backend.
|
||||
*
|
||||
|
@ -251,7 +251,7 @@ static bool IsContainerLayerItem(nsDisplayItem* aItem) {
|
||||
|
||||
#include <sstream>
|
||||
|
||||
bool DetectContainerLayerPropertiesBoundsChange(
|
||||
static bool DetectContainerLayerPropertiesBoundsChange(
|
||||
nsDisplayItem* aItem, BlobItemData* aData,
|
||||
nsDisplayItemGeometry& aGeometry) {
|
||||
switch (aItem->GetType()) {
|
||||
|
@ -206,6 +206,18 @@ bool NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fast approximate division by 255. It has the property that
|
||||
// for all 0 <= n <= 255*255, FAST_DIVIDE_BY_255(n) == n/255.
|
||||
// But it only uses two adds and two shifts instead of an
|
||||
// integer division (which is expensive on many processors).
|
||||
//
|
||||
// equivalent to target=v/255
|
||||
#define FAST_DIVIDE_BY_255(target, v) \
|
||||
PR_BEGIN_MACRO \
|
||||
unsigned tmp_ = v; \
|
||||
target = ((tmp_ << 8) + tmp_ + 255) >> 16; \
|
||||
PR_END_MACRO
|
||||
|
||||
// Macro to blend two colors
|
||||
//
|
||||
// equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255
|
||||
@ -238,38 +250,6 @@ nscolor NS_ComposeColors(nscolor aBG, nscolor aFG) {
|
||||
return NS_RGBA(r, g, b, a);
|
||||
}
|
||||
|
||||
// Functions to convert from HSL color space to RGB color space.
|
||||
// This is the algorithm described in the CSS3 specification
|
||||
|
||||
// helper
|
||||
static float HSL_HueToRGB(float m1, float m2, float h) {
|
||||
if (h < 0.0f) h += 1.0f;
|
||||
if (h > 1.0f) h -= 1.0f;
|
||||
if (h < (float)(1.0 / 6.0)) return m1 + (m2 - m1) * h * 6.0f;
|
||||
if (h < (float)(1.0 / 2.0)) return m2;
|
||||
if (h < (float)(2.0 / 3.0))
|
||||
return m1 + (m2 - m1) * ((float)(2.0 / 3.0) - h) * 6.0f;
|
||||
return m1;
|
||||
}
|
||||
|
||||
// The float parameters are all expected to be in the range 0-1
|
||||
nscolor NS_HSL2RGB(float h, float s, float l) {
|
||||
uint8_t r, g, b;
|
||||
float m1, m2;
|
||||
if (l <= 0.5f) {
|
||||
m2 = l * (s + 1);
|
||||
} else {
|
||||
m2 = l + s - l * s;
|
||||
}
|
||||
m1 = l * 2 - m2;
|
||||
// We round, not floor, because that's how we handle
|
||||
// percentage RGB values.
|
||||
r = ClampColor(255 * HSL_HueToRGB(m1, m2, h + 1.0f / 3.0f));
|
||||
g = ClampColor(255 * HSL_HueToRGB(m1, m2, h));
|
||||
b = ClampColor(255 * HSL_HueToRGB(m1, m2, h - 1.0f / 3.0f));
|
||||
return NS_RGB(r, g, b);
|
||||
}
|
||||
|
||||
const char* NS_RGBToColorName(nscolor aColor) {
|
||||
for (size_t idx = 0; idx < ArrayLength(kColors); ++idx) {
|
||||
if (kColors[idx] == aColor) {
|
||||
|
@ -48,18 +48,6 @@ inline uint8_t ClampColor(T aColor) {
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
// Fast approximate division by 255. It has the property that
|
||||
// for all 0 <= n <= 255*255, FAST_DIVIDE_BY_255(n) == n/255.
|
||||
// But it only uses two adds and two shifts instead of an
|
||||
// integer division (which is expensive on many processors).
|
||||
//
|
||||
// equivalent to target=v/255
|
||||
#define FAST_DIVIDE_BY_255(target, v) \
|
||||
PR_BEGIN_MACRO \
|
||||
unsigned tmp_ = v; \
|
||||
target = ((tmp_ << 8) + tmp_ + 255) >> 16; \
|
||||
PR_END_MACRO
|
||||
|
||||
enum class nsHexColorType : uint8_t {
|
||||
NoAlpha, // 3 or 6 digit hex colors only
|
||||
AllowAlpha, // 3, 4, 6, or 8 digit hex colors
|
||||
@ -98,10 +86,6 @@ bool NS_LooseHexToRGB(const nsString& aBuf, nscolor* aResult);
|
||||
// otherwise return false.
|
||||
bool NS_ColorNameToRGB(const nsAString& aBuf, nscolor* aResult);
|
||||
|
||||
// function to convert from HSL color space to RGB color space
|
||||
// the float parameters are all expected to be in the range 0-1
|
||||
nscolor NS_HSL2RGB(float h, float s, float l);
|
||||
|
||||
// Return a color name for the given nscolor. If there is no color
|
||||
// name for it, returns null. If there are multiple possible color
|
||||
// names for the given color, the first one in nsColorNameList.h
|
||||
|
@ -64,7 +64,7 @@ struct BigStruct {
|
||||
explicit BigStruct(uint64_t val) : mVal(val) {}
|
||||
};
|
||||
|
||||
void TestArenaAlloc(IterableArena::ArenaType aType) {
|
||||
static void TestArenaAlloc(IterableArena::ArenaType aType) {
|
||||
sDtorItemA = 0;
|
||||
sDtorItemB = 0;
|
||||
IterableArena arena(aType, 256);
|
||||
@ -133,7 +133,8 @@ void TestArenaAlloc(IterableArena::ArenaType aType) {
|
||||
}
|
||||
}
|
||||
|
||||
void TestArenaLimit(IterableArena::ArenaType aType, bool aShouldReachLimit) {
|
||||
static void TestArenaLimit(IterableArena::ArenaType aType,
|
||||
bool aShouldReachLimit) {
|
||||
IterableArena arena(aType, 128);
|
||||
|
||||
// A non-growable arena should return a negative offset when running out
|
||||
|
@ -24,7 +24,7 @@ using mozilla::gfx::SyncObject;
|
||||
|
||||
// Artificially cause threads to yield randomly in an attempt to make racy
|
||||
// things more apparent (if any).
|
||||
void MaybeYieldThread() {
|
||||
static void MaybeYieldThread() {
|
||||
#ifndef WIN32
|
||||
if (rand() % 5 == 0) {
|
||||
sched_yield();
|
||||
@ -112,7 +112,7 @@ class TestJob : public Job {
|
||||
/// The main thread is only blocked when waiting for the completion of the
|
||||
/// entire task stream (it doesn't have to wait at the filter's sync points to
|
||||
/// orchestrate it).
|
||||
void TestSchedulerJoin(uint32_t aNumThreads, uint32_t aNumCmdBuffers) {
|
||||
static void TestSchedulerJoin(uint32_t aNumThreads, uint32_t aNumCmdBuffers) {
|
||||
JoinTestSanityCheck check(aNumCmdBuffers);
|
||||
|
||||
RefPtr<SyncObject> beforeFilter = new SyncObject(aNumCmdBuffers);
|
||||
@ -150,7 +150,7 @@ void TestSchedulerJoin(uint32_t aNumThreads, uint32_t aNumCmdBuffers) {
|
||||
/// executed sequentially, and chains are exectuted in parallel. This simulates
|
||||
/// the typical scenario where we want to process sequences of drawing commands
|
||||
/// for several tiles in parallel.
|
||||
void TestSchedulerChain(uint32_t aNumThreads, uint32_t aNumCmdBuffers) {
|
||||
static void TestSchedulerChain(uint32_t aNumThreads, uint32_t aNumCmdBuffers) {
|
||||
SanityChecker check(aNumCmdBuffers);
|
||||
|
||||
RefPtr<SyncObject> completion = new SyncObject(aNumCmdBuffers);
|
||||
|
@ -44,8 +44,8 @@ TEST(MozPolygon, TriangulatePentagon) {
|
||||
AssertArrayEQ(triangles, expected);
|
||||
}
|
||||
|
||||
void TestClipRect(const MozPolygon& aPolygon, const MozPolygon& aExpected,
|
||||
const Rect& aRect) {
|
||||
static void TestClipRect(const MozPolygon& aPolygon,
|
||||
const MozPolygon& aExpected, const Rect& aRect) {
|
||||
const MozPolygon res = aPolygon.ClipPolygon(MozPolygon::FromRect(aRect));
|
||||
EXPECT_TRUE(res == aExpected);
|
||||
}
|
||||
|
@ -1191,7 +1191,8 @@ struct RegionBitmap {
|
||||
int height;
|
||||
};
|
||||
|
||||
void VisitEdge(void *closure, VisitSide side, int x1, int y1, int x2, int y2) {
|
||||
static void VisitEdge(void *closure, VisitSide side, int x1, int y1,
|
||||
int x2, int y2) {
|
||||
EXPECT_GE(x2, x1);
|
||||
RegionBitmap *visitor = static_cast<RegionBitmap *>(closure);
|
||||
unsigned char *bitmap = visitor->bitmap;
|
||||
@ -1220,7 +1221,7 @@ void VisitEdge(void *closure, VisitSide side, int x1, int y1, int x2, int y2) {
|
||||
}
|
||||
}
|
||||
|
||||
void TestVisit(nsRegion &r) {
|
||||
static void TestVisit(nsRegion &r) {
|
||||
auto reference = mozilla::MakeUnique<unsigned char[]>(600 * 600);
|
||||
auto result = mozilla::MakeUnique<unsigned char[]>(600 * 600);
|
||||
RegionBitmap ref(reference.get(), 600, 600);
|
||||
|
@ -67,7 +67,7 @@ static void GetPlatformBackends(nsTArray<LayersBackend>& aBackends) {
|
||||
/**
|
||||
* This function will return a BasicCompositor to caller.
|
||||
*/
|
||||
already_AddRefed<Compositor> CreateBasicCompositor() {
|
||||
static already_AddRefed<Compositor> CreateBasicCompositor() {
|
||||
RefPtr<Compositor> compositor;
|
||||
// Init the platform.
|
||||
if (gfxPlatform::GetPlatform()) {
|
||||
@ -84,7 +84,7 @@ already_AddRefed<Compositor> CreateBasicCompositor() {
|
||||
* This function checks if the textures react correctly when setting them to
|
||||
* BasicCompositor.
|
||||
*/
|
||||
void CheckCompatibilityWithBasicCompositor(
|
||||
static void CheckCompatibilityWithBasicCompositor(
|
||||
LayersBackend aBackends, nsTArray<RefPtr<TextureHost>>& aTextures) {
|
||||
RefPtr<Compositor> compositor = CreateBasicCompositor();
|
||||
for (uint32_t i = 0; i < aTextures.Length(); i++) {
|
||||
|
@ -41,7 +41,7 @@ namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
// fills the surface with values betwee 0 and 100.
|
||||
void SetupSurface(gfxImageSurface* surface) {
|
||||
static void SetupSurface(gfxImageSurface* surface) {
|
||||
int bpp = gfxASurface::BytePerPixelFromFormat(surface->Format());
|
||||
int stride = surface->Stride();
|
||||
uint8_t val = 0;
|
||||
@ -61,7 +61,8 @@ void SetupSurface(gfxImageSurface* surface) {
|
||||
}
|
||||
|
||||
// return true if two surfaces contain the same data
|
||||
void AssertSurfacesEqual(gfxImageSurface* surface1, gfxImageSurface* surface2) {
|
||||
static void AssertSurfacesEqual(gfxImageSurface* surface1,
|
||||
gfxImageSurface* surface2) {
|
||||
ASSERT_EQ(surface1->GetSize(), surface2->GetSize());
|
||||
ASSERT_EQ(surface1->Format(), surface2->Format());
|
||||
|
||||
@ -81,7 +82,8 @@ void AssertSurfacesEqual(gfxImageSurface* surface1, gfxImageSurface* surface2) {
|
||||
}
|
||||
}
|
||||
|
||||
void AssertSurfacesEqual(SourceSurface* surface1, SourceSurface* surface2) {
|
||||
static void AssertSurfacesEqual(SourceSurface* surface1,
|
||||
SourceSurface* surface2) {
|
||||
ASSERT_EQ(surface1->GetSize(), surface2->GetSize());
|
||||
ASSERT_EQ(surface1->GetFormat(), surface2->GetFormat());
|
||||
|
||||
|
@ -13,12 +13,12 @@
|
||||
|
||||
#include "cairo.h"
|
||||
|
||||
int GetASurfaceRefCount(gfxASurface *s) {
|
||||
static int GetASurfaceRefCount(gfxASurface *s) {
|
||||
NS_ADDREF(s);
|
||||
return s->Release();
|
||||
}
|
||||
|
||||
int CheckInt(int value, int expected) {
|
||||
static int CheckInt(int value, int expected) {
|
||||
if (value != expected) {
|
||||
fprintf(stderr, "Expected %d got %d\n", expected, value);
|
||||
return 1;
|
||||
@ -27,7 +27,7 @@ int CheckInt(int value, int expected) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CheckPointer(void *value, void *expected) {
|
||||
static int CheckPointer(void *value, void *expected) {
|
||||
if (value != expected) {
|
||||
fprintf(stderr, "Expected %p got %p\n", expected, value);
|
||||
return 1;
|
||||
@ -37,9 +37,9 @@ int CheckPointer(void *value, void *expected) {
|
||||
}
|
||||
|
||||
static cairo_user_data_key_t destruction_key;
|
||||
void SurfaceDestroyNotifier(void *data) { *(int *)data = 1; }
|
||||
static void SurfaceDestroyNotifier(void *data) { *(int *)data = 1; }
|
||||
|
||||
int TestNewSurface() {
|
||||
static int TestNewSurface() {
|
||||
int failures = 0;
|
||||
int destroyed = 0;
|
||||
|
||||
@ -87,7 +87,7 @@ int TestNewSurface() {
|
||||
return failures;
|
||||
}
|
||||
|
||||
int TestExistingSurface() {
|
||||
static int TestExistingSurface() {
|
||||
int failures = 0;
|
||||
int destroyed = 0;
|
||||
|
||||
|
@ -441,10 +441,11 @@ static IntSize ComputeMinSizeForShadowShape(const RectCornerRadii* aCornerRadii,
|
||||
return minSize;
|
||||
}
|
||||
|
||||
void CacheBlur(DrawTarget* aDT, const IntSize& aMinSize,
|
||||
const IntSize& aBlurRadius, const RectCornerRadii* aCornerRadii,
|
||||
const Color& aShadowColor, const IntMargin& aBlurMargin,
|
||||
SourceSurface* aBoxShadow) {
|
||||
static void CacheBlur(DrawTarget* aDT, const IntSize& aMinSize,
|
||||
const IntSize& aBlurRadius,
|
||||
const RectCornerRadii* aCornerRadii,
|
||||
const Color& aShadowColor, const IntMargin& aBlurMargin,
|
||||
SourceSurface* aBoxShadow) {
|
||||
BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor,
|
||||
aDT->GetBackendType());
|
||||
BlurCacheData* data =
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layers/ISurfaceAllocator.h" // for GfxMemoryImageReporter
|
||||
#include "mozilla/layers/CompositorBridgeChild.h"
|
||||
#include "mozilla/layers/TiledContentClient.h"
|
||||
#include "mozilla/webrender/RenderThread.h"
|
||||
#include "mozilla/webrender/WebRenderAPI.h"
|
||||
#include "mozilla/webrender/webrender_ffi.h"
|
||||
@ -148,12 +149,6 @@ class mozilla::gl::SkiaGLGlue : public GenericAtomicRefCounted {};
|
||||
#include "mozilla/layers/MemoryReportingMLGPU.h"
|
||||
#include "prsystem.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
void ShutdownTileCache();
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::gl;
|
||||
@ -556,7 +551,7 @@ void RecordingPrefChanged(const char* aPrefName, void* aClosure) {
|
||||
|
||||
#define WR_DEBUG_PREF "gfx.webrender.debug"
|
||||
|
||||
void WebRenderDebugPrefChangeCallback(const char* aPrefName, void*) {
|
||||
static void WebRenderDebugPrefChangeCallback(const char* aPrefName, void*) {
|
||||
int32_t flags = 0;
|
||||
#define GFX_WEBRENDER_DEBUG(suffix, bit) \
|
||||
if (Preferences::GetBool(WR_DEBUG_PREF suffix, false)) { \
|
||||
@ -1084,7 +1079,7 @@ void gfxPlatform::Init() {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsFeatureSupported(long aFeature) {
|
||||
static bool IsFeatureSupported(long aFeature) {
|
||||
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
|
||||
nsCString blockId;
|
||||
int32_t status;
|
||||
@ -1352,7 +1347,7 @@ struct SourceSurfaceUserData {
|
||||
BackendType mBackendType;
|
||||
};
|
||||
|
||||
void SourceBufferDestroy(void* srcSurfUD) {
|
||||
static void SourceBufferDestroy(void* srcSurfUD) {
|
||||
delete static_cast<SourceSurfaceUserData*>(srcSurfUD);
|
||||
}
|
||||
|
||||
@ -1362,7 +1357,7 @@ struct DependentSourceSurfaceUserData {
|
||||
RefPtr<gfxASurface> mSurface;
|
||||
};
|
||||
|
||||
void SourceSurfaceDestroyed(void* aData) {
|
||||
static void SourceSurfaceDestroyed(void* aData) {
|
||||
delete static_cast<DependentSourceSurfaceUserData*>(aData);
|
||||
}
|
||||
|
||||
@ -2432,7 +2427,7 @@ static bool sBufferRotationCheckPref = true;
|
||||
|
||||
static mozilla::Atomic<bool> sLayersAccelerationPrefsInitialized(false);
|
||||
|
||||
void VideoDecodingFailedChangedCallback(const char* aPref, void*) {
|
||||
static void VideoDecodingFailedChangedCallback(const char* aPref, void*) {
|
||||
sLayersHardwareVideoDecodingFailed = Preferences::GetBool(aPref, false);
|
||||
gfxPlatform::GetPlatform()->UpdateCanUseHardwareVideoDecoding();
|
||||
}
|
||||
|
@ -680,6 +680,16 @@ AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
static bool Contains(const std::vector<std::string>& aExtraOpts,
|
||||
const char* aValue) {
|
||||
return std::any_of(aExtraOpts.begin(), aExtraOpts.end(),
|
||||
[&](const std::string arg) {
|
||||
return arg.find(aValue) != std::string::npos;
|
||||
});
|
||||
}
|
||||
#endif // XP_WIN
|
||||
|
||||
bool GeckoChildProcessHost::PerformAsyncLaunch(
|
||||
std::vector<std::string> aExtraOpts) {
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
@ -846,25 +856,26 @@ bool GeckoChildProcessHost::PerformAsyncLaunch(
|
||||
|
||||
childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end());
|
||||
|
||||
if (Omnijar::IsInitialized()) {
|
||||
// Make sure that child processes can find the omnijar
|
||||
// See XRE_InitCommandLine in nsAppRunner.cpp
|
||||
nsAutoCString path;
|
||||
nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
|
||||
if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
|
||||
childArgv.push_back("-greomni");
|
||||
childArgv.push_back(path.get());
|
||||
}
|
||||
file = Omnijar::GetPath(Omnijar::APP);
|
||||
if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
|
||||
childArgv.push_back("-appomni");
|
||||
childArgv.push_back(path.get());
|
||||
if (mProcessType != GeckoProcessType_GMPlugin) {
|
||||
if (Omnijar::IsInitialized()) {
|
||||
// Make sure that child processes can find the omnijar
|
||||
// See XRE_InitCommandLine in nsAppRunner.cpp
|
||||
nsAutoCString path;
|
||||
nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
|
||||
if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
|
||||
childArgv.push_back("-greomni");
|
||||
childArgv.push_back(path.get());
|
||||
}
|
||||
file = Omnijar::GetPath(Omnijar::APP);
|
||||
if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
|
||||
childArgv.push_back("-appomni");
|
||||
childArgv.push_back(path.get());
|
||||
}
|
||||
}
|
||||
// Add the application directory path (-appdir path)
|
||||
AddAppDirToCommandLine(childArgv);
|
||||
}
|
||||
|
||||
// Add the application directory path (-appdir path)
|
||||
AddAppDirToCommandLine(childArgv);
|
||||
|
||||
childArgv.push_back(pidstring);
|
||||
|
||||
if (!CrashReporter::IsDummy()) {
|
||||
@ -1008,6 +1019,18 @@ bool GeckoChildProcessHost::PerformAsyncLaunch(
|
||||
FilePath exePath;
|
||||
BinaryPathType pathType = GetPathToBinary(exePath, mProcessType);
|
||||
|
||||
const bool isGMP = mProcessType == GeckoProcessType_GMPlugin;
|
||||
const bool isWidevine = isGMP && Contains(aExtraOpts, "gmp-widevinecdm");
|
||||
# if defined(_ARM64_) && defined(XP_WIN)
|
||||
const bool isClearKey = isGMP && Contains(aExtraOpts, "gmp-clearkey");
|
||||
if (isGMP && (isClearKey || isWidevine)) {
|
||||
// On Windows on ARM64 for ClearKey and Widevine, we want to run the
|
||||
// x86 plugin-container.exe in the "i686" subdirectory, instead of the
|
||||
// aarch64 plugin-container.exe. So insert "i686" into the exePath.
|
||||
exePath = exePath.DirName().AppendASCII("i686").Append(exePath.BaseName());
|
||||
}
|
||||
# endif
|
||||
|
||||
CommandLine cmdLine(exePath.ToWStringHack());
|
||||
|
||||
if (pathType == BinaryPathType::Self) {
|
||||
@ -1076,10 +1099,6 @@ bool GeckoChildProcessHost::PerformAsyncLaunch(
|
||||
// not at USER_LOCKDOWN. So look in the command line arguments
|
||||
// to see if we're loading the path to the Widevine CDM, and if
|
||||
// so use sandbox level USER_RESTRICTED instead of USER_LOCKDOWN.
|
||||
bool isWidevine = std::any_of(
|
||||
aExtraOpts.begin(), aExtraOpts.end(), [](const std::string arg) {
|
||||
return arg.find("gmp-widevinecdm") != std::string::npos;
|
||||
});
|
||||
auto level =
|
||||
isWidevine ? SandboxBroker::Restricted : SandboxBroker::LockDown;
|
||||
bool ok = mSandboxBroker.SetSecurityLevelForGMPlugin(level);
|
||||
|
@ -3941,6 +3941,8 @@ static bool array_proto_finish(JSContext* cx, JS::HandleObject ctor,
|
||||
!DefineDataProperty(cx, unscopables, cx->names().fill, value) ||
|
||||
!DefineDataProperty(cx, unscopables, cx->names().find, value) ||
|
||||
!DefineDataProperty(cx, unscopables, cx->names().findIndex, value) ||
|
||||
!DefineDataProperty(cx, unscopables, cx->names().flat, value) ||
|
||||
!DefineDataProperty(cx, unscopables, cx->names().flatMap, value) ||
|
||||
!DefineDataProperty(cx, unscopables, cx->names().includes, value) ||
|
||||
!DefineDataProperty(cx, unscopables, cx->names().keys, value) ||
|
||||
!DefineDataProperty(cx, unscopables, cx->names().values, value)) {
|
||||
|
15
js/src/builtin/AsyncFunction.js
Normal file
15
js/src/builtin/AsyncFunction.js
Normal file
@ -0,0 +1,15 @@
|
||||
/* 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/. */
|
||||
|
||||
function AsyncFunctionNext(val) {
|
||||
assert(IsAsyncFunctionGeneratorObject(this),
|
||||
"ThisArgument must be a generator object for async functions");
|
||||
return resumeGenerator(this, val, "next");
|
||||
}
|
||||
|
||||
function AsyncFunctionThrow(val) {
|
||||
assert(IsAsyncFunctionGeneratorObject(this),
|
||||
"ThisArgument must be a generator object for async functions");
|
||||
return resumeGenerator(this, val, "throw");
|
||||
}
|
@ -5,3 +5,22 @@
|
||||
function AsyncIteratorIdentity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
function AsyncGeneratorNext(val) {
|
||||
assert(IsAsyncGeneratorObject(this),
|
||||
"ThisArgument must be a generator object for async generators");
|
||||
return resumeGenerator(this, val, "next");
|
||||
}
|
||||
|
||||
function AsyncGeneratorThrow(val) {
|
||||
assert(IsAsyncGeneratorObject(this),
|
||||
"ThisArgument must be a generator object for async generators");
|
||||
return resumeGenerator(this, val, "throw");
|
||||
}
|
||||
|
||||
function AsyncGeneratorReturn(val) {
|
||||
assert(IsAsyncGeneratorObject(this),
|
||||
"ThisArgument must be a generator object for async generators");
|
||||
var rval = { value: val, done: true };
|
||||
return resumeGenerator(this, rval, "return");
|
||||
}
|
||||
|
@ -9,8 +9,10 @@
|
||||
#include "mozilla/Alignment.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "mozilla/WrappingOperations.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsnum.h"
|
||||
@ -39,6 +41,7 @@ using namespace js;
|
||||
using JS::CanonicalizeNaN;
|
||||
using JS::ToInt32;
|
||||
using mozilla::AssertedCast;
|
||||
using mozilla::WrapToSigned;
|
||||
|
||||
DataViewObject* DataViewObject::create(
|
||||
JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
||||
@ -412,17 +415,24 @@ template <typename NativeType>
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T WrappingConvert(int32_t value) {
|
||||
if (std::is_unsigned<T>::value) {
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
|
||||
return WrapToSigned(static_cast<typename std::make_unsigned<T>::type>(value));
|
||||
}
|
||||
|
||||
template <typename NativeType>
|
||||
static inline bool WebIDLCast(JSContext* cx, HandleValue value,
|
||||
NativeType* out) {
|
||||
int32_t temp;
|
||||
if (!ToInt32(cx, value, &temp)) {
|
||||
int32_t i;
|
||||
if (!ToInt32(cx, value, &i)) {
|
||||
return false;
|
||||
}
|
||||
// Technically, the behavior of assigning an out of range value to a signed
|
||||
// variable is undefined. In practice, compilers seem to do what we want
|
||||
// without issuing any warnings.
|
||||
*out = static_cast<NativeType>(temp);
|
||||
|
||||
*out = WrappingConvert<NativeType>(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1000,18 +1000,6 @@ bool ModuleObject::noteFunctionDeclaration(JSContext* cx, HandleAtom name,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fun->isAsync()) {
|
||||
if (fun->isGenerator()) {
|
||||
obj = WrapAsyncGenerator(cx, obj.as<JSFunction>());
|
||||
} else {
|
||||
obj = WrapAsyncFunction(cx, obj.as<JSFunction>());
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = ObjectValue(*obj);
|
||||
if (!SetProperty(cx, env, funDecl.name->asPropertyName(), value)) {
|
||||
return false;
|
||||
|
@ -368,7 +368,7 @@ JSString* js::ObjectToSource(JSContext* cx, HandleObject obj) {
|
||||
return false;
|
||||
}
|
||||
} else if (kind == PropertyKind::Method && fun) {
|
||||
if (IsWrappedAsyncFunction(fun)) {
|
||||
if (fun->isAsync()) {
|
||||
if (!buf.append("async ")) {
|
||||
return false;
|
||||
}
|
||||
@ -412,7 +412,6 @@ JSString* js::ObjectToSource(JSContext* cx, HandleObject obj) {
|
||||
RootedId id(cx);
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
RootedValue val(cx);
|
||||
RootedFunction fun(cx);
|
||||
for (size_t i = 0; i < idv.length(); ++i) {
|
||||
id = idv[i];
|
||||
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
|
||||
@ -440,17 +439,13 @@ JSString* js::ObjectToSource(JSContext* cx, HandleObject obj) {
|
||||
}
|
||||
|
||||
val.set(desc.value());
|
||||
if (IsFunctionObject(val, fun.address())) {
|
||||
if (IsWrappedAsyncFunction(fun)) {
|
||||
fun = GetUnwrappedAsyncFunction(fun);
|
||||
}
|
||||
|
||||
if (fun->isMethod()) {
|
||||
if (!AddProperty(id, val, PropertyKind::Method)) {
|
||||
return nullptr;
|
||||
}
|
||||
continue;
|
||||
JSFunction* fun;
|
||||
if (IsFunctionObject(val, &fun) && fun->isMethod()) {
|
||||
if (!AddProperty(id, val, PropertyKind::Method)) {
|
||||
return nullptr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!AddProperty(id, val, PropertyKind::Normal)) {
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "vm/AsyncFunction.h"
|
||||
#include "vm/AsyncIteration.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/GeneratorObject.h"
|
||||
#include "vm/Iteration.h"
|
||||
#include "vm/JSContext.h"
|
||||
#include "vm/JSObject.h"
|
||||
@ -535,8 +536,10 @@ enum ReactionRecordSlots {
|
||||
|
||||
// Additional slot to store extra data for specific reaction record types.
|
||||
//
|
||||
// - When the REACTION_FLAG_ASYNC_GENERATOR flag is set, this slot store
|
||||
// the async generator function for this promise reaction.
|
||||
// - When the REACTION_FLAG_ASYNC_FUNCTION flag is set, this slot stores
|
||||
// the (internal) generator object for this promise reaction.
|
||||
// - When the REACTION_FLAG_ASYNC_GENERATOR flag is set, this slot stores
|
||||
// the async generator object for this promise reaction.
|
||||
// - When the REACTION_FLAG_DEFAULT_RESOLVING_HANDLER flag is set, this
|
||||
// slot stores the promise to resolve when conceptually "calling" the
|
||||
// OnFulfilled or OnRejected handlers.
|
||||
@ -619,13 +622,21 @@ class PromiseReactionRecord : public NativeObject {
|
||||
getFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve);
|
||||
return &promiseToResolve.toObject().as<PromiseObject>();
|
||||
}
|
||||
void setIsAsyncFunction() {
|
||||
void setIsAsyncFunction(AsyncFunctionGeneratorObject* genObj) {
|
||||
setFlagOnInitialState(REACTION_FLAG_ASYNC_FUNCTION);
|
||||
setFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve,
|
||||
ObjectValue(*genObj));
|
||||
}
|
||||
bool isAsyncFunction() {
|
||||
int32_t flags = this->flags();
|
||||
return flags & REACTION_FLAG_ASYNC_FUNCTION;
|
||||
}
|
||||
AsyncFunctionGeneratorObject* asyncFunctionGenerator() {
|
||||
MOZ_ASSERT(isAsyncFunction());
|
||||
const Value& generator =
|
||||
getFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve);
|
||||
return &generator.toObject().as<AsyncFunctionGeneratorObject>();
|
||||
}
|
||||
void setIsAsyncGenerator(AsyncGeneratorObject* asyncGenObj) {
|
||||
setFlagOnInitialState(REACTION_FLAG_ASYNC_GENERATOR);
|
||||
setFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve,
|
||||
@ -1469,24 +1480,20 @@ static MOZ_MUST_USE bool AsyncFunctionPromiseReactionJob(
|
||||
|
||||
RootedValue handlerVal(cx, reaction->handler());
|
||||
RootedValue argument(cx, reaction->handlerArg());
|
||||
Rooted<PromiseObject*> resultPromise(
|
||||
cx, &reaction->promise()->as<PromiseObject>());
|
||||
RootedValue generatorVal(
|
||||
cx, resultPromise->getFixedSlot(PromiseSlot_AwaitGenerator));
|
||||
Rooted<AsyncFunctionGeneratorObject*> generator(
|
||||
cx, reaction->asyncFunctionGenerator());
|
||||
|
||||
int32_t handlerNum = handlerVal.toInt32();
|
||||
|
||||
// Await's handlers don't return a value, nor throw exception.
|
||||
// Await's handlers don't return a value, nor throw an exception.
|
||||
// They fail only on OOM.
|
||||
if (handlerNum == PromiseHandlerAsyncFunctionAwaitedFulfilled) {
|
||||
if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal,
|
||||
argument)) {
|
||||
if (!AsyncFunctionAwaitedFulfilled(cx, generator, argument)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitedRejected);
|
||||
if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal,
|
||||
argument)) {
|
||||
if (!AsyncFunctionAwaitedRejected(cx, generator, argument)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -3267,6 +3274,7 @@ static PromiseReactionRecord* NewReactionRecord(
|
||||
} else {
|
||||
// `resultCapability.promise` is null for the following cases:
|
||||
// * resulting Promise is known to be unused
|
||||
// * Async Function
|
||||
// * Async Generator
|
||||
// In any case, other fields are also not used.
|
||||
MOZ_ASSERT(!resultCapability.resolve());
|
||||
@ -3490,8 +3498,7 @@ static MOZ_MUST_USE bool PerformPromiseThenWithReaction(
|
||||
// js/src/builtin/AsyncFunction.cpp, to call Promise internal functions.
|
||||
|
||||
// ES 2018 draft 14.6.11 and 14.7.14 step 1.
|
||||
MOZ_MUST_USE PromiseObject* js::CreatePromiseObjectForAsync(
|
||||
JSContext* cx, HandleValue generatorVal) {
|
||||
MOZ_MUST_USE PromiseObject* js::CreatePromiseObjectForAsync(JSContext* cx) {
|
||||
// Step 1.
|
||||
PromiseObject* promise = CreatePromiseObjectWithoutResolutionFunctions(cx);
|
||||
if (!promise) {
|
||||
@ -3499,7 +3506,6 @@ MOZ_MUST_USE PromiseObject* js::CreatePromiseObjectForAsync(
|
||||
}
|
||||
|
||||
AddPromiseFlags(*promise, PROMISE_FLAG_ASYNC);
|
||||
promise->setFixedSlot(PromiseSlot_AwaitGenerator, generatorVal);
|
||||
return promise;
|
||||
}
|
||||
|
||||
@ -3508,33 +3514,19 @@ bool js::IsPromiseForAsync(JSObject* promise) {
|
||||
PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_ASYNC);
|
||||
}
|
||||
|
||||
// ES 2018 draft 25.5.5.2 steps 3.f, 3.g.
|
||||
MOZ_MUST_USE bool js::AsyncFunctionThrown(
|
||||
JSContext* cx, Handle<PromiseObject*> resultPromise) {
|
||||
// Step 3.f.
|
||||
RootedValue exc(cx);
|
||||
if (!MaybeGetAndClearException(cx, &exc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RejectPromiseInternal(cx, resultPromise, exc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3.g.
|
||||
return true;
|
||||
// ES2019 draft rev 7428c89bef626548084cd4e697a19ece7168f24c
|
||||
// 25.7.5.1 AsyncFunctionStart, steps 3.f-g.
|
||||
MOZ_MUST_USE bool js::AsyncFunctionThrown(JSContext* cx,
|
||||
Handle<PromiseObject*> resultPromise,
|
||||
HandleValue reason) {
|
||||
return RejectPromiseInternal(cx, resultPromise, reason);
|
||||
}
|
||||
|
||||
// ES 2018 draft 25.5.5.2 steps 3.d-e, 3.g.
|
||||
// ES2019 draft rev 7428c89bef626548084cd4e697a19ece7168f24c
|
||||
// 25.7.5.1 AsyncFunctionStart, steps 3.d-e, 3.g.
|
||||
MOZ_MUST_USE bool js::AsyncFunctionReturned(
|
||||
JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value) {
|
||||
// Steps 3.d-e.
|
||||
if (!ResolvePromiseInternal(cx, resultPromise, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3.g.
|
||||
return true;
|
||||
return ResolvePromiseInternal(cx, resultPromise, value);
|
||||
}
|
||||
|
||||
// Helper function that performs the equivalent steps as
|
||||
@ -3577,9 +3569,9 @@ static MOZ_MUST_USE bool InternalAwait(JSContext* cx, HandleValue value,
|
||||
}
|
||||
|
||||
// ES 2018 draft 25.5.5.3 steps 2-10.
|
||||
MOZ_MUST_USE bool js::AsyncFunctionAwait(JSContext* cx,
|
||||
Handle<PromiseObject*> resultPromise,
|
||||
HandleValue value) {
|
||||
MOZ_MUST_USE JSObject* js::AsyncFunctionAwait(
|
||||
JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj,
|
||||
HandleValue value) {
|
||||
// Steps 4-5.
|
||||
RootedValue onFulfilled(
|
||||
cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedFulfilled));
|
||||
@ -3587,11 +3579,13 @@ MOZ_MUST_USE bool js::AsyncFunctionAwait(JSContext* cx,
|
||||
cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedRejected));
|
||||
|
||||
// Steps 2-3, 6-10.
|
||||
auto extra = [](Handle<PromiseReactionRecord*> reaction) {
|
||||
reaction->setIsAsyncFunction();
|
||||
auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
|
||||
reaction->setIsAsyncFunction(genObj);
|
||||
};
|
||||
return InternalAwait(cx, value, resultPromise, onFulfilled, onRejected,
|
||||
extra);
|
||||
if (!InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra)) {
|
||||
return nullptr;
|
||||
}
|
||||
return genObj->promise();
|
||||
}
|
||||
|
||||
// Async Iteration proposal 4.1 Await steps 2-9.
|
||||
@ -4834,19 +4828,34 @@ static MOZ_MUST_USE bool IsTopMostAsyncFunctionCall(JSContext* cx) {
|
||||
}
|
||||
MOZ_ASSERT(iter.calleeTemplate()->isAsync());
|
||||
|
||||
#ifdef DEBUG
|
||||
bool isGenerator = iter.calleeTemplate()->isGenerator();
|
||||
#endif
|
||||
|
||||
++iter;
|
||||
|
||||
// The parent frame should be the `next` function of the generator that is
|
||||
// internally called in AsyncFunctionResume.
|
||||
// internally called in AsyncFunctionResume resp. AsyncGeneratorResume.
|
||||
if (iter.done()) {
|
||||
return false;
|
||||
}
|
||||
// The initial call into an async function can happen from top-level code, so
|
||||
// the parent frame isn't required to be a function frame. Contrary to that,
|
||||
// the parent frame for an async generator function is always a function
|
||||
// frame, because async generators can't directly fall through to an `await`
|
||||
// expression from their initial call.
|
||||
if (!iter.isFunctionFrame()) {
|
||||
MOZ_ASSERT(!isGenerator);
|
||||
return false;
|
||||
}
|
||||
if (!iter.calleeTemplate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsSelfHostedFunctionWithName(iter.calleeTemplate(),
|
||||
cx->names().GeneratorNext)) {
|
||||
cx->names().AsyncFunctionNext) &&
|
||||
!IsSelfHostedFunctionWithName(iter.calleeTemplate(),
|
||||
cx->names().AsyncGeneratorNext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -33,10 +33,7 @@ enum PromiseSlots {
|
||||
// This slot holds only the reject function. The resolve function is
|
||||
// reachable from the reject function's extended slot.
|
||||
// * if this promise is either fulfilled or rejected, undefined
|
||||
// * (special case) if this promise is the return value of an async function
|
||||
// invocation, the generator object for the function's internal generator
|
||||
PromiseSlot_RejectFunction,
|
||||
PromiseSlot_AwaitGenerator = PromiseSlot_RejectFunction,
|
||||
|
||||
// Promise object's debug info, which is created on demand.
|
||||
// * if this promise has no debug info, undefined
|
||||
@ -238,8 +235,7 @@ MOZ_MUST_USE bool RejectPromiseWithPendingError(JSContext* cx,
|
||||
* Create the promise object which will be used as the return value of an async
|
||||
* function.
|
||||
*/
|
||||
MOZ_MUST_USE PromiseObject* CreatePromiseObjectForAsync(
|
||||
JSContext* cx, HandleValue generatorVal);
|
||||
MOZ_MUST_USE PromiseObject* CreatePromiseObjectForAsync(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Returns true if the given object is a promise created by
|
||||
@ -247,16 +243,21 @@ MOZ_MUST_USE PromiseObject* CreatePromiseObjectForAsync(
|
||||
*/
|
||||
MOZ_MUST_USE bool IsPromiseForAsync(JSObject* promise);
|
||||
|
||||
class AsyncFunctionGeneratorObject;
|
||||
|
||||
MOZ_MUST_USE bool AsyncFunctionReturned(JSContext* cx,
|
||||
Handle<PromiseObject*> resultPromise,
|
||||
HandleValue value);
|
||||
|
||||
MOZ_MUST_USE bool AsyncFunctionThrown(JSContext* cx,
|
||||
Handle<PromiseObject*> resultPromise);
|
||||
Handle<PromiseObject*> resultPromise,
|
||||
HandleValue reason);
|
||||
|
||||
MOZ_MUST_USE bool AsyncFunctionAwait(JSContext* cx,
|
||||
Handle<PromiseObject*> resultPromise,
|
||||
HandleValue value);
|
||||
// Start awaiting `value` in an async function (, but doesn't suspend the
|
||||
// async function's execution!). Returns the async function's result promise.
|
||||
MOZ_MUST_USE JSObject* AsyncFunctionAwait(
|
||||
JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj,
|
||||
HandleValue value);
|
||||
|
||||
// If the await operation can be skipped and the resolution value for `val` can
|
||||
// be acquired, stored the resolved value to `resolved` and `true` to
|
||||
|
@ -318,6 +318,97 @@ function MergeSort(array, len, comparefn) {
|
||||
return array;
|
||||
}
|
||||
|
||||
// A helper function for MergeSortTypedArray.
|
||||
//
|
||||
// Merge comparefn-sorted slices list[start..<=mid] and list[mid+1..<=end],
|
||||
// storing the merged sequence in out[start..<=end].
|
||||
function MergeTypedArray(list, out, start, mid, end, comparefn) {
|
||||
// Skip lopsided runs to avoid doing useless work.
|
||||
// Skip calling the comparator if the sub-list is already sorted.
|
||||
if (mid >= end || comparefn(list[mid], list[mid + 1]) <= 0) {
|
||||
for (var i = start; i <= end; i++) {
|
||||
out[i] = list[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var i = start;
|
||||
var j = mid + 1;
|
||||
var k = start;
|
||||
while (i <= mid && j <= end) {
|
||||
var lvalue = list[i];
|
||||
var rvalue = list[j];
|
||||
if (comparefn(lvalue, rvalue) <= 0) {
|
||||
out[k++] = lvalue;
|
||||
i++;
|
||||
} else {
|
||||
out[k++] = rvalue;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
// Empty out any remaining elements.
|
||||
while (i <= mid) {
|
||||
out[k++] = list[i++];
|
||||
}
|
||||
while (j <= end) {
|
||||
out[k++] = list[j++];
|
||||
}
|
||||
}
|
||||
|
||||
// Iterative, bottom up, mergesort. Optimized version for TypedArrays.
|
||||
function MergeSortTypedArray(array, len, comparefn) {
|
||||
assert(IsPossiblyWrappedTypedArray(array),
|
||||
"MergeSortTypedArray works only with typed arrays.");
|
||||
|
||||
// Insertion sort for small arrays, where "small" is defined by performance
|
||||
// testing.
|
||||
if (len < 8) {
|
||||
InsertionSort(array, 0, len - 1, comparefn);
|
||||
return array;
|
||||
}
|
||||
|
||||
// Use the same TypedArray kind for the buffer.
|
||||
var C = _ConstructorForTypedArray(array);
|
||||
|
||||
// We do all of our allocating up front.
|
||||
var lBuffer = array;
|
||||
var rBuffer = new C(len);
|
||||
|
||||
// Use insertion sort for the initial ranges.
|
||||
var windowSize = 4;
|
||||
for (var start = 0; start < len - 1; start += windowSize) {
|
||||
var end = std_Math_min(start + windowSize - 1, len - 1);
|
||||
InsertionSort(lBuffer, start, end, comparefn);
|
||||
}
|
||||
|
||||
for (; windowSize < len; windowSize = 2 * windowSize) {
|
||||
for (var start = 0; start < len; start += 2 * windowSize) {
|
||||
// The midpoint between the two subarrays.
|
||||
var mid = start + windowSize - 1;
|
||||
|
||||
// To keep from going over the edge.
|
||||
var end = std_Math_min(start + 2 * windowSize - 1, len - 1);
|
||||
|
||||
MergeTypedArray(lBuffer, rBuffer, start, mid, end, comparefn);
|
||||
}
|
||||
|
||||
// Swap both lists.
|
||||
var swap = lBuffer;
|
||||
lBuffer = rBuffer;
|
||||
rBuffer = swap;
|
||||
}
|
||||
|
||||
// Move the sorted elements into the array.
|
||||
if (lBuffer !== array) {
|
||||
for (var i = 0; i < len; i++) {
|
||||
array[i] = lBuffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
// Rearranges the elements in array[from:to + 1] and returns an index j such that:
|
||||
// - from < j < to
|
||||
// - each element in array[from:j] is less than or equal to array[j]
|
||||
@ -385,7 +476,6 @@ function QuickSort(array, len, comparefn) {
|
||||
// Calculate the left and right sub-array lengths and save
|
||||
// stack space by directly modifying start/end so that
|
||||
// we sort the longest of the two during the next iteration.
|
||||
// This reduces the maximum stack size to log2(len).
|
||||
leftLen = (pivotIndex - 1) - start;
|
||||
rightLen = end - (pivotIndex + 1);
|
||||
|
||||
|
@ -91,7 +91,7 @@ inline static MOZ_MUST_USE JSFunction* NewHandler(JSContext* cx, Native handler,
|
||||
HandleObject target) {
|
||||
cx->check(target);
|
||||
|
||||
RootedAtom funName(cx, cx->names().empty);
|
||||
HandlePropertyName funName = cx->names().empty;
|
||||
RootedFunction handlerFun(
|
||||
cx, NewNativeFunction(cx, handler, 0, funName,
|
||||
gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
|
||||
@ -1433,9 +1433,10 @@ static MOZ_MUST_USE JSObject* ReadableStreamCancel(
|
||||
|
||||
// Step 6: Return the result of transforming sourceCancelPromise with a
|
||||
// fulfillment handler that returns undefined.
|
||||
RootedAtom funName(cx, cx->names().empty);
|
||||
HandlePropertyName funName = cx->names().empty;
|
||||
RootedFunction returnUndefined(
|
||||
cx, NewNativeFunction(cx, ReturnUndefined, 0, funName));
|
||||
cx, NewNativeFunction(cx, ReturnUndefined, 0, funName,
|
||||
gc::AllocKind::FUNCTION, GenericObject));
|
||||
if (!returnUndefined) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -5439,14 +5439,6 @@ JSScript* js::TestingFunctionArgumentToScript(
|
||||
}
|
||||
}
|
||||
|
||||
// Get unwrapped async function.
|
||||
if (IsWrappedAsyncFunction(fun)) {
|
||||
fun = GetUnwrappedAsyncFunction(fun);
|
||||
}
|
||||
if (IsWrappedAsyncGenerator(fun)) {
|
||||
fun = GetUnwrappedAsyncGenerator(fun);
|
||||
}
|
||||
|
||||
if (!fun->isInterpreted()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_TESTING_SCRIPTS_ONLY);
|
||||
|
@ -1188,7 +1188,7 @@ function TypedArrayCompareInt(x, y) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ES2018 draft rev 3bbc87cd1b9d3bf64c3e68ca2fe9c5a3f2c304c0
|
||||
// ES2019 draft rev 8a16cb8d18660a1106faae693f0f39b9f1a30748
|
||||
// 22.2.3.26 %TypedArray%.prototype.sort ( comparefn )
|
||||
function TypedArraySort(comparefn) {
|
||||
// This function is not generic.
|
||||
@ -1283,13 +1283,15 @@ function TypedArraySort(comparefn) {
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
}
|
||||
|
||||
// Step c. is redundant, see:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1121937#c36
|
||||
// Step c.
|
||||
if (v !== v)
|
||||
return 0;
|
||||
|
||||
// Step d.
|
||||
return v;
|
||||
};
|
||||
|
||||
return QuickSort(obj, len, wrappedCompareFn);
|
||||
return MergeSortTypedArray(obj, len, wrappedCompareFn);
|
||||
}
|
||||
|
||||
// ES2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
/* eslint-disable complexity */
|
||||
// Mappings from complete tags to preferred values.
|
||||
// Derived from IANA Language Subtag Registry, file date 2018-03-30.
|
||||
// Derived from IANA Language Subtag Registry, file date 2019-02-20.
|
||||
// https://www.iana.org/assignments/language-subtag-registry
|
||||
function updateLangTagMappings(tag) {
|
||||
assert(IsObject(tag), "tag is an object");
|
||||
@ -313,7 +313,7 @@ function updateLangTagMappings(tag) {
|
||||
/* eslint-enable complexity */
|
||||
|
||||
// Mappings from grandfathered tags to preferred values.
|
||||
// Derived from IANA Language Subtag Registry, file date 2018-03-30.
|
||||
// Derived from IANA Language Subtag Registry, file date 2019-02-20.
|
||||
// https://www.iana.org/assignments/language-subtag-registry
|
||||
var grandfatheredMappings = {
|
||||
"art-lojban": "jbo",
|
||||
@ -345,7 +345,7 @@ var grandfatheredMappings = {
|
||||
};
|
||||
|
||||
// Mappings from language subtags to preferred values.
|
||||
// Derived from IANA Language Subtag Registry, file date 2018-03-30.
|
||||
// Derived from IANA Language Subtag Registry, file date 2019-02-20.
|
||||
// https://www.iana.org/assignments/language-subtag-registry
|
||||
var languageMappings = {
|
||||
"aam": "aas",
|
||||
@ -429,7 +429,7 @@ var languageMappings = {
|
||||
};
|
||||
|
||||
// Mappings from region subtags to preferred values.
|
||||
// Derived from IANA Language Subtag Registry, file date 2018-03-30.
|
||||
// Derived from IANA Language Subtag Registry, file date 2019-02-20.
|
||||
// https://www.iana.org/assignments/language-subtag-registry
|
||||
var regionMappings = {
|
||||
"BU": "MM",
|
||||
@ -444,7 +444,7 @@ var regionMappings = {
|
||||
// All current deprecated extlang subtags have the form `<prefix>-<extlang>`
|
||||
// and their preferred value is exactly equal to `<extlang>`. So each key in
|
||||
// extlangMappings acts both as the extlang subtag and its preferred value.
|
||||
// Derived from IANA Language Subtag Registry, file date 2018-03-30.
|
||||
// Derived from IANA Language Subtag Registry, file date 2019-02-20.
|
||||
// https://www.iana.org/assignments/language-subtag-registry
|
||||
var extlangMappings = {
|
||||
"aao": "ar",
|
||||
@ -570,6 +570,7 @@ var extlangMappings = {
|
||||
"lcf": "ms",
|
||||
"liw": "ms",
|
||||
"lls": "sgn",
|
||||
"lsg": "sgn",
|
||||
"lsl": "sgn",
|
||||
"lso": "sgn",
|
||||
"lsp": "sgn",
|
||||
@ -623,6 +624,7 @@ var extlangMappings = {
|
||||
"psr": "sgn",
|
||||
"pys": "sgn",
|
||||
"rms": "sgn",
|
||||
"rsi": "sgn",
|
||||
"rsl": "sgn",
|
||||
"rsm": "sgn",
|
||||
"sdl": "sgn",
|
||||
@ -670,6 +672,7 @@ var extlangMappings = {
|
||||
"xml": "sgn",
|
||||
"xmm": "ms",
|
||||
"xms": "sgn",
|
||||
"yds": "sgn",
|
||||
"ygs": "sgn",
|
||||
"yhs": "sgn",
|
||||
"ysl": "sgn",
|
||||
|
@ -39,6 +39,7 @@ import io
|
||||
import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
from collections import namedtuple
|
||||
from contextlib import closing
|
||||
from functools import partial, total_ordering
|
||||
from itertools import chain, groupby, tee
|
||||
@ -106,11 +107,36 @@ def readRegistry(registry):
|
||||
extlangMappings = {}
|
||||
extlangSubtags = []
|
||||
|
||||
# Set of language tags which require special handling.
|
||||
SpecialCase = namedtuple("SpecialCase", ["Type", "Subtag", "Prefix", "Preferred_Value"])
|
||||
knownSpecialCases = {
|
||||
SpecialCase("variant", "arevela", "hy", None): "hy",
|
||||
SpecialCase("variant", "arevmda", "hy", None): "hyw",
|
||||
SpecialCase("variant", "heploc", "ja-Latn-hepburn", "alalc97"): "ja-Latn-alalc97",
|
||||
}
|
||||
|
||||
# The de-facto marker for special cases is a comment of the form
|
||||
# "Preferred tag is <preferred>", where <preferred> denotes the preferred
|
||||
# language tag. This is not specified in RFC 5646.
|
||||
specialCaseRE = re.compile("Preferred tag is (?P<preferred>.+)")
|
||||
|
||||
for record in readRegistryRecord(registry):
|
||||
if "File-Date" in record:
|
||||
fileDate = record["File-Date"]
|
||||
continue
|
||||
|
||||
# Watch out for cases which need special processing.
|
||||
if "Comments" in record:
|
||||
match = specialCaseRE.match(record["Comments"])
|
||||
if match:
|
||||
replacement = knownSpecialCases[record["Type"],
|
||||
record["Subtag"],
|
||||
record["Prefix"],
|
||||
record.get("Preferred-Value")]
|
||||
if replacement != match.group("preferred"):
|
||||
raise Exception("Unexpected replacement value for {}".format(record))
|
||||
record["Preferred-Value"] = replacement
|
||||
|
||||
if record["Type"] == "grandfathered":
|
||||
# Grandfathered tags don't use standard syntax, so
|
||||
# CanonicalizeLanguageTag expects the mapping table to provide
|
||||
@ -181,9 +207,13 @@ def readRegistry(registry):
|
||||
if extlang in languageMappings:
|
||||
raise Exception("Conflict: extlang with lang mapping: " + extlang)
|
||||
|
||||
# Special case for heploc.
|
||||
assert variantMappings["ja-Latn-hepburn-heploc"] == "alalc97"
|
||||
variantMappings["ja-Latn-hepburn-heploc"] = "ja-Latn-alalc97"
|
||||
# Check all known special cases were processed.
|
||||
for elem in knownSpecialCases:
|
||||
tag = "{}-{}".format(elem.Prefix, elem.Subtag)
|
||||
assert elem.Type == "variant", "Unexpected non-variant special case"
|
||||
assert tag in variantMappings, "{} not found in variant mappings".format(tag)
|
||||
assert variantMappings[tag] == knownSpecialCases[elem], \
|
||||
"{} does not map to {}".format(tag, knownSpecialCases[elem])
|
||||
|
||||
# ValidateAndCanonicalizeLanguageTag in CommonFunctions.js expects
|
||||
# redundantMappings contains no 2*3ALPHA.
|
||||
@ -305,7 +335,7 @@ def writeMappingsFunction(println, variantMappings, redundantMappings, extlangMa
|
||||
continue
|
||||
|
||||
if kind == Subtag.ExtLang:
|
||||
assert extlangIndex in [1, 2, 3],\
|
||||
assert extlangIndex in [1, 2, 3], \
|
||||
"Language-Tag permits no more than three extlang subtags"
|
||||
cond.append('tag.extlang{} === "{}"'.format(extlangIndex, subtag))
|
||||
extlangIndex += 1
|
||||
@ -397,7 +427,7 @@ def writeMappingsFunction(println, variantMappings, redundantMappings, extlangMa
|
||||
assert preferred_kind != Subtag.ExtLang
|
||||
extlangIndex = 1
|
||||
while tag_kind == Subtag.ExtLang:
|
||||
assert extlangIndex in [1, 2, 3],\
|
||||
assert extlangIndex in [1, 2, 3], \
|
||||
"Language-Tag permits no more than three extlang subtags"
|
||||
println3(u"tag.extlang{} = undefined;".format(extlangIndex))
|
||||
extlangIndex += 1
|
||||
@ -505,7 +535,7 @@ def writeMappingsFunction(println, variantMappings, redundantMappings, extlangMa
|
||||
println(u' case "{}":'.format(lang))
|
||||
isFirstLanguageTag = True
|
||||
for tag in sorted(tag for tag in langTagMappings if language(tag) == lang):
|
||||
assert not isinstance(langTagMappings[tag], dict),\
|
||||
assert not isinstance(langTagMappings[tag], dict), \
|
||||
"only supports complete language tags"
|
||||
emitCompare(tag, langTagMappings[tag], isFirstLanguageTag)
|
||||
isFirstLanguageTag = False
|
||||
|
@ -340,13 +340,15 @@ JS::Result<FunctionNode*> BinASTParserPerTokenizer<Tok>::buildFunction(
|
||||
BINJS_TRY(usedNames_.noteUse(cx_, dotGenerator, pc_->scriptId(),
|
||||
pc_->innermostScope()->id()));
|
||||
|
||||
BINJS_TRY_DECL(
|
||||
dotGen, handler_.newName(dotGenerator,
|
||||
tokenizer_->pos(tokenizer_->offset()), cx_));
|
||||
if (funbox->isGenerator()) {
|
||||
BINJS_TRY_DECL(
|
||||
dotGen, handler_.newName(dotGenerator,
|
||||
tokenizer_->pos(tokenizer_->offset()), cx_));
|
||||
|
||||
ListNode* stmtList =
|
||||
&body->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
|
||||
BINJS_TRY(handler_.prependInitialYield(stmtList, dotGen));
|
||||
ListNode* stmtList =
|
||||
&body->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
|
||||
BINJS_TRY(handler_.prependInitialYield(stmtList, dotGen));
|
||||
}
|
||||
}
|
||||
|
||||
const bool canSkipLazyClosedOverBindings = false;
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "frontend/TryEmitter.h"
|
||||
#include "frontend/WhileEmitter.h"
|
||||
#include "js/CompileOptions.h"
|
||||
#include "vm/AsyncFunction.h"
|
||||
#include "vm/BytecodeUtil.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/GeneratorObject.h"
|
||||
@ -2211,9 +2212,9 @@ bool BytecodeEmitter::allocateResumeIndex(ptrdiff_t offset,
|
||||
static constexpr uint32_t MaxResumeIndex = JS_BITMASK(24);
|
||||
|
||||
static_assert(
|
||||
MaxResumeIndex < uint32_t(GeneratorObject::RESUME_INDEX_CLOSING),
|
||||
"resumeIndex should not include magic GeneratorObject resumeIndex "
|
||||
"values");
|
||||
MaxResumeIndex < uint32_t(AbstractGeneratorObject::RESUME_INDEX_CLOSING),
|
||||
"resumeIndex should not include magic AbstractGeneratorObject "
|
||||
"resumeIndex values");
|
||||
static_assert(
|
||||
MaxResumeIndex <= INT32_MAX / sizeof(uintptr_t),
|
||||
"resumeIndex * sizeof(uintptr_t) must fit in an int32. JIT code relies "
|
||||
@ -3198,30 +3199,15 @@ bool BytecodeEmitter::emitAnonymousFunctionWithComputedName(
|
||||
|
||||
if (node->is<FunctionNode>()) {
|
||||
if (!emitTree(node)) {
|
||||
// [stack] # !isAsync || !needsHomeObject
|
||||
// [stack] NAME FUN
|
||||
// [stack] # isAsync && needsHomeObject
|
||||
// [stack] NAME UNWRAPPED WRAPPED
|
||||
return false;
|
||||
}
|
||||
unsigned depth = 1;
|
||||
FunctionNode* funNode = &node->as<FunctionNode>();
|
||||
FunctionBox* funbox = funNode->funbox();
|
||||
if (funbox->isAsync() && funbox->needsHomeObject()) {
|
||||
depth = 2;
|
||||
}
|
||||
if (!emitDupAt(depth)) {
|
||||
// [stack] # !isAsync || !needsHomeObject
|
||||
if (!emitDupAt(1)) {
|
||||
// [stack] NAME FUN NAME
|
||||
// [stack] # isAsync && needsHomeObject
|
||||
// [stack] NAME UNWRAPPED WRAPPED NAME
|
||||
return false;
|
||||
}
|
||||
if (!emit2(JSOP_SETFUNNAME, uint8_t(prefixKind))) {
|
||||
// [stack] # !isAsync || !needsHomeObject
|
||||
// [stack] NAME FUN
|
||||
// [stack] # isAsync && needsHomeObject
|
||||
// [stack] NAME UNWRAPPED WRAPPED
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -5807,11 +5793,6 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(FunctionNode* funNode,
|
||||
// JSOP_LAMBDA_ARROW is always preceded by a new.target
|
||||
MOZ_ASSERT(fun->isArrow() ==
|
||||
(funNode->syntaxKind() == FunctionSyntaxKind::Arrow));
|
||||
if (funbox->isAsync()) {
|
||||
MOZ_ASSERT(!needsProto);
|
||||
return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow(),
|
||||
fun->isGenerator());
|
||||
}
|
||||
|
||||
if (fun->isArrow()) {
|
||||
if (sc->allowNewTarget()) {
|
||||
@ -5867,15 +5848,8 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(FunctionNode* funNode,
|
||||
MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext());
|
||||
MOZ_ASSERT(funNode->syntaxKind() == FunctionSyntaxKind::Statement);
|
||||
MOZ_ASSERT(inPrologue());
|
||||
if (funbox->isAsync()) {
|
||||
if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow(),
|
||||
fun->isGenerator())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emitIndex32(JSOP_LAMBDA, index)) {
|
||||
return false;
|
||||
}
|
||||
if (!emitIndex32(JSOP_LAMBDA, index)) {
|
||||
return false;
|
||||
}
|
||||
if (!emit1(JSOP_DEFFUN)) {
|
||||
return false;
|
||||
@ -5889,15 +5863,8 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(FunctionNode* funNode,
|
||||
if (!noe.prepareForRhs()) {
|
||||
return false;
|
||||
}
|
||||
if (funbox->isAsync()) {
|
||||
if (!emitAsyncWrapper(index, /* needsHomeObject = */ false,
|
||||
/* isArrow = */ false, funbox->isGenerator())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emitIndexOp(JSOP_LAMBDA, index)) {
|
||||
return false;
|
||||
}
|
||||
if (!emitIndexOp(JSOP_LAMBDA, index)) {
|
||||
return false;
|
||||
}
|
||||
if (!noe.emitAssignment()) {
|
||||
return false;
|
||||
@ -5910,77 +5877,6 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(FunctionNode* funNode,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BytecodeEmitter::emitAsyncWrapperLambda(unsigned index, bool isArrow) {
|
||||
if (isArrow) {
|
||||
if (sc->allowNewTarget()) {
|
||||
if (!emit1(JSOP_NEWTARGET)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emit1(JSOP_NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!emitIndex32(JSOP_LAMBDA_ARROW, index)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emitIndex32(JSOP_LAMBDA, index)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject,
|
||||
bool isArrow, bool isGenerator) {
|
||||
// needsHomeObject can be true for propertyList for extended class.
|
||||
// In that case push both unwrapped and wrapped function, in order to
|
||||
// initialize home object of unwrapped function, and set wrapped function
|
||||
// as a property.
|
||||
//
|
||||
// lambda // unwrapped
|
||||
// dup // unwrapped unwrapped
|
||||
// toasync // unwrapped wrapped
|
||||
//
|
||||
// Emitted code is surrounded by the following code.
|
||||
//
|
||||
// // classObj classCtor classProto
|
||||
// (emitted code) // classObj classCtor classProto unwrapped wrapped
|
||||
// swap // classObj classCtor classProto wrapped unwrapped
|
||||
// dupat 2 // classObj classCtor classProto wrapped unwrapped
|
||||
// classProto inithomeobject // classObj classCtor classProto wrapped
|
||||
// unwrapped
|
||||
// // initialize the home object of unwrapped
|
||||
// // with classProto here
|
||||
// pop // classObj classCtor classProto wrapped
|
||||
// inithiddenprop // classObj classCtor classProto wrapped
|
||||
// // initialize the property of the classProto
|
||||
// // with wrapped function here
|
||||
// pop // classObj classCtor classProto
|
||||
//
|
||||
// needsHomeObject is false for other cases, push wrapped function only.
|
||||
if (!emitAsyncWrapperLambda(index, isArrow)) {
|
||||
return false;
|
||||
}
|
||||
if (needsHomeObject) {
|
||||
if (!emit1(JSOP_DUP)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (isGenerator) {
|
||||
if (!emit1(JSOP_TOASYNCGEN)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emit1(JSOP_TOASYNC)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BytecodeEmitter::emitDo(BinaryNode* doNode) {
|
||||
ParseNode* bodyNode = doNode->left();
|
||||
|
||||
@ -6181,9 +6077,7 @@ bool BytecodeEmitter::emitReturn(UnaryNode* returnNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isAsyncGenerator =
|
||||
sc->asFunctionBox()->isAsync() && sc->asFunctionBox()->isGenerator();
|
||||
if (isAsyncGenerator) {
|
||||
if (sc->asFunctionBox()->isAsync() && sc->asFunctionBox()->isGenerator()) {
|
||||
if (!emitAwaitInInnermostScope()) {
|
||||
return false;
|
||||
}
|
||||
@ -6251,6 +6145,28 @@ bool BytecodeEmitter::emitReturn(UnaryNode* returnNode) {
|
||||
// all nested scopes.
|
||||
NameLocation loc = *locationOfNameBoundInFunctionScope(
|
||||
cx->names().dotGenerator, varEmitterScope);
|
||||
|
||||
// Resolve the return value before emitting the final yield.
|
||||
if (sc->asFunctionBox()->needsPromiseResult()) {
|
||||
if (!emit1(JSOP_GETRVAL)) {
|
||||
// [stack] RVAL
|
||||
return false;
|
||||
}
|
||||
if (!emitGetNameAtLocation(cx->names().dotGenerator, loc)) {
|
||||
// [stack] RVAL GEN
|
||||
return false;
|
||||
}
|
||||
if (!emit2(JSOP_ASYNCRESOLVE,
|
||||
uint8_t(AsyncFunctionResolveKind::Fulfill))) {
|
||||
// [stack] PROMISE
|
||||
return false;
|
||||
}
|
||||
if (!emit1(JSOP_SETRVAL)) {
|
||||
// [stack]
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!emitGetNameAtLocation(cx->names().dotGenerator, loc)) {
|
||||
return false;
|
||||
}
|
||||
@ -6318,8 +6234,7 @@ bool BytecodeEmitter::emitYield(UnaryNode* yieldNode) {
|
||||
}
|
||||
|
||||
// 11.4.3.7 AsyncGeneratorYield step 5.
|
||||
bool isAsyncGenerator = sc->asFunctionBox()->isAsync();
|
||||
if (isAsyncGenerator) {
|
||||
if (sc->asFunctionBox()->isAsync()) {
|
||||
if (!emitAwaitInInnermostScope()) {
|
||||
// [stack] ITEROBJ RESULT
|
||||
return false;
|
||||
@ -6373,8 +6288,19 @@ bool BytecodeEmitter::emitAwaitInScope(EmitterScope& currentScope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sc->asFunctionBox()->needsPromiseResult()) {
|
||||
if (!emitGetDotGeneratorInScope(currentScope)) {
|
||||
// [stack] VALUE GENERATOR
|
||||
return false;
|
||||
}
|
||||
if (!emit1(JSOP_ASYNCAWAIT)) {
|
||||
// [stack] PROMISE
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!emitGetDotGeneratorInScope(currentScope)) {
|
||||
// [stack] VALUE GENERATOR
|
||||
// [stack] VALUE|PROMISE GENERATOR
|
||||
return false;
|
||||
}
|
||||
if (!emitYieldOp(JSOP_AWAIT)) {
|
||||
@ -7172,8 +7098,8 @@ bool BytecodeEmitter::emitSelfHostedResumeGenerator(BinaryNode* callNode) {
|
||||
|
||||
ParseNode* kindNode = valNode->pn_next;
|
||||
MOZ_ASSERT(kindNode->isKind(ParseNodeKind::StringExpr));
|
||||
uint16_t operand =
|
||||
GeneratorObject::getResumeKind(cx, kindNode->as<NameNode>().atom());
|
||||
uint16_t operand = AbstractGeneratorObject::getResumeKind(
|
||||
cx, kindNode->as<NameNode>().atom());
|
||||
MOZ_ASSERT(!kindNode->pn_next);
|
||||
|
||||
if (!emitCall(JSOP_RESUME, operand)) {
|
||||
@ -7899,10 +7825,12 @@ bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
|
||||
|
||||
if (propVal->is<FunctionNode>() &&
|
||||
propVal->as<FunctionNode>().funbox()->needsHomeObject()) {
|
||||
FunctionBox* funbox = propVal->as<FunctionNode>().funbox();
|
||||
MOZ_ASSERT(funbox->function()->allowSuperProperty());
|
||||
MOZ_ASSERT(propVal->as<FunctionNode>()
|
||||
.funbox()
|
||||
->function()
|
||||
->allowSuperProperty());
|
||||
|
||||
if (!pe.emitInitHomeObject(funbox->asyncKind())) {
|
||||
if (!pe.emitInitHomeObject()) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY? FUN
|
||||
return false;
|
||||
}
|
||||
@ -8477,6 +8405,18 @@ bool BytecodeEmitter::emitFunctionFormalParameters(ListNode* paramsBody) {
|
||||
bool hasParameterExprs = funbox->hasParameterExprs;
|
||||
bool hasRest = funbox->hasRest();
|
||||
|
||||
// Parameters can't reuse the reject try-catch block from the function body,
|
||||
// because the body may have pushed an additional var-environment. This
|
||||
// messes up scope resolution for the |.generator| variable, because we'd
|
||||
// need different hops to reach |.generator| depending on whether the error
|
||||
// was thrown from the parameters or the function body.
|
||||
Maybe<TryEmitter> rejectTryCatch;
|
||||
if (hasParameterExprs && funbox->needsPromiseResult()) {
|
||||
if (!emitAsyncFunctionRejectPrologue(rejectTryCatch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t argSlot = 0;
|
||||
for (ParseNode* arg = paramsBody->head(); arg != funBody;
|
||||
arg = arg->pn_next, argSlot++) {
|
||||
@ -8588,6 +8528,12 @@ bool BytecodeEmitter::emitFunctionFormalParameters(ListNode* paramsBody) {
|
||||
}
|
||||
}
|
||||
|
||||
if (rejectTryCatch) {
|
||||
if (!emitAsyncFunctionRejectEpilogue(*rejectTryCatch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -8635,12 +8581,27 @@ bool BytecodeEmitter::emitInitializeFunctionSpecialNames() {
|
||||
}
|
||||
}
|
||||
|
||||
// Do nothing if the function doesn't implicitly return a promise result.
|
||||
if (funbox->needsPromiseResult()) {
|
||||
if (!emitInitializeFunctionSpecialName(this, cx->names().dotGenerator,
|
||||
JSOP_GENERATOR)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BytecodeEmitter::emitFunctionBody(ParseNode* funBody) {
|
||||
FunctionBox* funbox = sc->asFunctionBox();
|
||||
|
||||
Maybe<TryEmitter> rejectTryCatch;
|
||||
if (funbox->needsPromiseResult()) {
|
||||
if (!emitAsyncFunctionRejectPrologue(rejectTryCatch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (funbox->function()->kind() ==
|
||||
JSFunction::FunctionKind::ClassConstructor) {
|
||||
if (!emitInitializeInstanceFields()) {
|
||||
@ -8671,6 +8632,19 @@ bool BytecodeEmitter::emitFunctionBody(ParseNode* funBody) {
|
||||
}
|
||||
}
|
||||
|
||||
if (funbox->needsPromiseResult()) {
|
||||
if (!emitGetDotGeneratorInInnermostScope()) {
|
||||
// [stack] RVAL GEN
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emit2(JSOP_ASYNCRESOLVE,
|
||||
uint8_t(AsyncFunctionResolveKind::Fulfill))) {
|
||||
// [stack] PROMISE
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!emit1(JSOP_SETRVAL)) {
|
||||
return false;
|
||||
}
|
||||
@ -8705,6 +8679,12 @@ bool BytecodeEmitter::emitFunctionBody(ParseNode* funBody) {
|
||||
}
|
||||
}
|
||||
|
||||
if (rejectTryCatch) {
|
||||
if (!emitAsyncFunctionRejectEpilogue(*rejectTryCatch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -8730,6 +8710,46 @@ bool BytecodeEmitter::emitLexicalInitialization(JSAtom* name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BytecodeEmitter::emitAsyncFunctionRejectPrologue(
|
||||
Maybe<TryEmitter>& tryCatch) {
|
||||
tryCatch.emplace(this, TryEmitter::Kind::TryCatch,
|
||||
TryEmitter::ControlKind::NonSyntactic);
|
||||
return tryCatch->emitTry();
|
||||
}
|
||||
|
||||
bool BytecodeEmitter::emitAsyncFunctionRejectEpilogue(TryEmitter& tryCatch) {
|
||||
if (!tryCatch.emitCatch()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emit1(JSOP_EXCEPTION)) {
|
||||
// [stack] EXC
|
||||
return false;
|
||||
}
|
||||
if (!emitGetDotGeneratorInInnermostScope()) {
|
||||
// [stack] EXC GEN
|
||||
return false;
|
||||
}
|
||||
if (!emit2(JSOP_ASYNCRESOLVE, uint8_t(AsyncFunctionResolveKind::Reject))) {
|
||||
// [stack] PROMISE
|
||||
return false;
|
||||
}
|
||||
if (!emit1(JSOP_SETRVAL)) {
|
||||
// [stack]
|
||||
return false;
|
||||
}
|
||||
if (!emitGetDotGeneratorInInnermostScope()) {
|
||||
// [stack] GEN
|
||||
return false;
|
||||
}
|
||||
if (!emit1(JSOP_FINALYIELDRVAL)) {
|
||||
// [stack]
|
||||
return false;
|
||||
}
|
||||
|
||||
return tryCatch.emitEnd();
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE FunctionNode* FindConstructor(JSContext* cx,
|
||||
ListNode* classMethods) {
|
||||
for (ParseNode* mn : classMethods->contents()) {
|
||||
|
@ -104,6 +104,7 @@ class EmitterScope;
|
||||
class NestableControl;
|
||||
class PropertyEmitter;
|
||||
class TDZCheckCache;
|
||||
class TryEmitter;
|
||||
|
||||
struct MOZ_STACK_CLASS BytecodeEmitter {
|
||||
// Context shared between parsing and bytecode generation.
|
||||
@ -654,10 +655,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
|
||||
MOZ_MUST_USE bool emitPropLHS(PropertyAccess* prop);
|
||||
MOZ_MUST_USE bool emitPropIncDec(UnaryNode* incDec);
|
||||
|
||||
MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow);
|
||||
MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject,
|
||||
bool isArrow, bool isGenerator);
|
||||
|
||||
MOZ_MUST_USE bool emitComputedPropertyName(UnaryNode* computedPropName);
|
||||
|
||||
// Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM
|
||||
@ -851,6 +848,12 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
|
||||
MOZ_MUST_USE bool emitLexicalInitialization(NameNode* name);
|
||||
MOZ_MUST_USE bool emitLexicalInitialization(JSAtom* name);
|
||||
|
||||
// Async functions have implicit try-catch blocks to convert exceptions
|
||||
// into promise rejections.
|
||||
MOZ_MUST_USE bool emitAsyncFunctionRejectPrologue(
|
||||
mozilla::Maybe<TryEmitter>& tryCatch);
|
||||
MOZ_MUST_USE bool emitAsyncFunctionRejectEpilogue(TryEmitter& tryCatch);
|
||||
|
||||
// Emit bytecode for the spread operator.
|
||||
//
|
||||
// emitSpread expects the current index (I) of the array, the array itself
|
||||
|
@ -227,51 +227,34 @@ bool PropertyEmitter::prepareForComputedPropValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitHomeObject(
|
||||
FunctionAsyncKind kind /* = FunctionAsyncKind::SyncFunction */) {
|
||||
bool PropertyEmitter::emitInitHomeObject() {
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
|
||||
propertyState_ == PropertyState::IndexValue ||
|
||||
propertyState_ == PropertyState::ComputedValue);
|
||||
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? FUN
|
||||
|
||||
bool isAsync = kind == FunctionAsyncKind::AsyncFunction;
|
||||
if (isAsync) {
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? UNWRAPPED WRAPPED
|
||||
if (!bce_->emit1(JSOP_SWAP)) {
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED UNWRAPPED
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// There are the following values on the stack conditionally, between
|
||||
// HOMEOBJ and FUN:
|
||||
// * the 2nd CTOR if isStatic_
|
||||
// * KEY if isIndexOrComputed_
|
||||
// * WRAPPED if isAsync
|
||||
//
|
||||
// JSOP_INITHOMEOBJECT uses one of the following:
|
||||
// * HOMEOBJ if !isStatic_
|
||||
// (`super.foo` points the super prototype property)
|
||||
// * the 2nd CTOR if isStatic_
|
||||
// (`super.foo` points the super constructor property)
|
||||
if (!bce_->emitDupAt(1 + isIndexOrComputed_ + isAsync)) {
|
||||
if (!bce_->emitDupAt(1 + isIndexOrComputed_)) {
|
||||
// [stack] # non-static method
|
||||
// [stack] CTOR? HOMEOBJ CTOR KEY? WRAPPED? FUN CTOR
|
||||
// [stack] CTOR? HOMEOBJ CTOR KEY? FUN CTOR
|
||||
// [stack] # static method
|
||||
// [stack] CTOR? HOMEOBJ KEY? WRAPPED? FUN HOMEOBJ
|
||||
// [stack] CTOR? HOMEOBJ KEY? FUN HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(JSOP_INITHOMEOBJECT)) {
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED? FUN
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? FUN
|
||||
return false;
|
||||
}
|
||||
if (isAsync) {
|
||||
if (!bce_->emit1(JSOP_POP)) {
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (propertyState_ == PropertyState::PropValue) {
|
||||
|
@ -18,8 +18,6 @@
|
||||
#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
|
||||
#include "vm/BytecodeUtil.h" // JSOp
|
||||
#include "vm/JSAtom.h" // JSAtom
|
||||
#include "vm/JSFunction.h" // JSFunction, FunctionPrefixKind
|
||||
#include "vm/JSScript.h" // FunctionAsyncKind
|
||||
#include "vm/NativeObject.h" // PlainObject
|
||||
#include "vm/Scope.h" // LexicalScope
|
||||
|
||||
@ -244,8 +242,7 @@ class MOZ_STACK_CLASS PropertyEmitter {
|
||||
const mozilla::Maybe<uint32_t>& keyPos, Kind kind = Kind::Prototype);
|
||||
MOZ_MUST_USE bool prepareForComputedPropValue();
|
||||
|
||||
MOZ_MUST_USE bool emitInitHomeObject(
|
||||
FunctionAsyncKind kind = FunctionAsyncKind::SyncFunction);
|
||||
MOZ_MUST_USE bool emitInitHomeObject();
|
||||
|
||||
// @param key
|
||||
// Property key
|
||||
@ -518,7 +515,7 @@ class MOZ_RAII AutoSaveLocalStrictMode {
|
||||
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||
// ce.prepareForPropValue(Some(offset_of_m));
|
||||
// emit(function_for_m);
|
||||
// ce.emitInitHomeObject(FunctionAsyncKind::Async);
|
||||
// ce.emitInitHomeObject();
|
||||
// ce.emitInitProp(atom_of_m);
|
||||
//
|
||||
// `get p() { super.f(); }` in class
|
||||
|
@ -1897,12 +1897,14 @@ GeneralParser<ParseHandler, Unit>::functionBody(InHandling inHandling,
|
||||
if (!pc_->declareDotGeneratorName()) {
|
||||
return null();
|
||||
}
|
||||
NameNodeType generator = newDotGeneratorName();
|
||||
if (!generator) {
|
||||
return null();
|
||||
}
|
||||
if (!handler_.prependInitialYield(handler_.asList(body), generator)) {
|
||||
return null();
|
||||
if (pc_->isGenerator()) {
|
||||
NameNodeType generator = newDotGeneratorName();
|
||||
if (!generator) {
|
||||
return null();
|
||||
}
|
||||
if (!handler_.prependInitialYield(handler_.asList(body), generator)) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1984,11 +1986,6 @@ JSFunction* AllocNewFunction(JSContext* cx, HandleAtom atom,
|
||||
: JSFunction::INTERPRETED_GENERATOR_OR_ASYNC);
|
||||
}
|
||||
|
||||
// We store the async wrapper in a slot for later access.
|
||||
if (asyncKind == FunctionAsyncKind::AsyncFunction) {
|
||||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||
}
|
||||
|
||||
fun = NewFunctionWithProto(cx, nullptr, 0, flags, nullptr, atom, proto,
|
||||
allocKind, TenuredObject);
|
||||
if (!fun) {
|
||||
@ -2564,8 +2561,18 @@ GeneralParser<ParseHandler, Unit>::functionDefinition(
|
||||
}
|
||||
|
||||
RootedObject proto(cx_);
|
||||
if (generatorKind == GeneratorKind::Generator ||
|
||||
asyncKind == FunctionAsyncKind::AsyncFunction) {
|
||||
if (asyncKind == FunctionAsyncKind::AsyncFunction &&
|
||||
generatorKind == GeneratorKind::Generator) {
|
||||
proto = GlobalObject::getOrCreateAsyncGenerator(cx_, cx_->global());
|
||||
if (!proto) {
|
||||
return null();
|
||||
}
|
||||
} else if (asyncKind == FunctionAsyncKind::AsyncFunction) {
|
||||
proto = GlobalObject::getOrCreateAsyncFunctionPrototype(cx_, cx_->global());
|
||||
if (!proto) {
|
||||
return null();
|
||||
}
|
||||
} else if (generatorKind == GeneratorKind::Generator) {
|
||||
proto =
|
||||
GlobalObject::getOrCreateGeneratorFunctionPrototype(cx_, cx_->global());
|
||||
if (!proto) {
|
||||
|
@ -477,6 +477,7 @@ class FunctionBox : public ObjectBox, public SharedContext {
|
||||
bool needsFinalYield() const { return isGenerator() || isAsync(); }
|
||||
bool needsDotGeneratorName() const { return isGenerator() || isAsync(); }
|
||||
bool needsIteratorResult() const { return isGenerator(); }
|
||||
bool needsPromiseResult() const { return isAsync() && !isGenerator(); }
|
||||
|
||||
bool isArrow() const { return function()->isArrow(); }
|
||||
|
||||
|
@ -30,6 +30,30 @@ function wasmFailValidateText(str, pattern) {
|
||||
assertErrorMessage(() => new WebAssembly.Module(binary), WebAssembly.CompileError, pattern);
|
||||
}
|
||||
|
||||
// Expected compilation failure can happen in a couple of ways:
|
||||
//
|
||||
// - The compiler can be available but not capable of recognizing some opcodes:
|
||||
// Compilation will start, but will fail with a CompileError. This is what
|
||||
// happens without --wasm-gc if opcodes enabled by --wasm-gc are used.
|
||||
//
|
||||
// - The compiler can be unavailable: Compilation will not start at all but will
|
||||
// throw an Error. This is what happens with "--wasm-gc --wasm-compiler=X" if
|
||||
// X does not support the features enabled by --wasm-gc.
|
||||
|
||||
function wasmCompilationShouldFail(bin, compile_error_regex) {
|
||||
try {
|
||||
new WebAssembly.Module(bin);
|
||||
} catch (e) {
|
||||
if (e instanceof WebAssembly.CompileError) {
|
||||
assertEq(compile_error_regex.test(e), true);
|
||||
} else if (e instanceof Error) {
|
||||
assertEq(/can't use wasm debug\/gc without baseline/.test(e), true);
|
||||
} else {
|
||||
throw new Error("Unexpected exception value:\n" + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mismatchError(actual, expect) {
|
||||
var str = `type mismatch: expression has type ${actual} but expected ${expect}`;
|
||||
return RegExp(str);
|
||||
|
50
js/src/jit-test/tests/basic/offThreadCompileScript-03.js
Normal file
50
js/src/jit-test/tests/basic/offThreadCompileScript-03.js
Normal file
@ -0,0 +1,50 @@
|
||||
// |jit-test| skip-if: helperThreadCount() === 0
|
||||
|
||||
// Test offThreadCompileScript for different function types.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
var id;
|
||||
|
||||
id = offThreadCompileScript(`
|
||||
function f() { return "pass"; }
|
||||
f();
|
||||
`);
|
||||
assertEq(runOffThreadScript(id), "pass");
|
||||
|
||||
id = offThreadCompileScript(`
|
||||
function* f() { return "pass"; }
|
||||
f().next();
|
||||
`);
|
||||
assertDeepEq(runOffThreadScript(id), {value: "pass", done: true});
|
||||
|
||||
id = offThreadCompileScript(`
|
||||
async function f() { return "pass"; }
|
||||
f();
|
||||
`);
|
||||
assertEventuallyEq(runOffThreadScript(id), "pass");
|
||||
|
||||
id = offThreadCompileScript(`
|
||||
async function* f() { return "pass"; }
|
||||
f().next();
|
||||
`);
|
||||
assertEventuallyDeepEq(runOffThreadScript(id), {value: "pass", done: true});
|
||||
|
||||
// Copied from js/src/tests/shell.js
|
||||
function getPromiseResult(promise) {
|
||||
var result, error, caught = false;
|
||||
promise.then(r => { result = r; },
|
||||
e => { caught = true; error = e; });
|
||||
drainJobQueue();
|
||||
if (caught)
|
||||
throw error;
|
||||
return result;
|
||||
}
|
||||
|
||||
function assertEventuallyEq(promise, expected) {
|
||||
assertEq(getPromiseResult(promise), expected);
|
||||
}
|
||||
|
||||
function assertEventuallyDeepEq(promise, expected) {
|
||||
assertDeepEq(getPromiseResult(promise), expected);
|
||||
}
|
@ -68,7 +68,9 @@ dbg.onEnterFrame = frame => {
|
||||
// Popping the frame. But async function frames are popped multiple
|
||||
// times: for the "initial suspend", at each await, and on return. The
|
||||
// debugger offers no easy way to distinguish them (bug 1470558).
|
||||
if (typeof completion.return === "string") {
|
||||
// For now there's an "await" property, but bug 1470558 may come up
|
||||
// with a different solution, so don't rely on it!
|
||||
if (!completion.await) {
|
||||
// Returning (not awaiting or at initial suspend).
|
||||
assertEq(asyncStack.pop(), frame);
|
||||
log += ")";
|
||||
|
@ -27,7 +27,6 @@ dbg.onEnterFrame = frame => {
|
||||
g.job();
|
||||
drainJobQueue();
|
||||
assertEq(log,
|
||||
"(job)(job(t5)(t5)(t3)(t3))" +
|
||||
"(job(t5)(t3))" +
|
||||
"(t5)(t3)".repeat(3) + "(job)" +
|
||||
"(t5)(t5)(job)");
|
||||
|
||||
|
@ -14,14 +14,13 @@ let resumption = undefined;
|
||||
dbg.onEnterFrame = frame => {
|
||||
if (frame.type == "call" && frame.callee.name === "f") {
|
||||
frame.onPop = completion => {
|
||||
assertEq(completion.return, resumption.return);
|
||||
assertEq(completion.return.isPromise, true);
|
||||
hits++;
|
||||
};
|
||||
|
||||
// Don't tell anyone, but if we force-return a generator object here,
|
||||
// the robots accept it as one of their own and plug it right into the
|
||||
// async function machinery. This may be handy against Skynet someday.
|
||||
resumption = frame.eval(`(function* f2() { hit2 = true; throw "fit"; })()`);
|
||||
// If we force-return a generator object here, the caller will receive
|
||||
// a promise object resolved with that generator.
|
||||
resumption = frame.eval(`(function* f2() { hit2 = true; })()`);
|
||||
assertEq(resumption.return.class, "Generator");
|
||||
return resumption;
|
||||
}
|
||||
@ -29,7 +28,8 @@ dbg.onEnterFrame = frame => {
|
||||
|
||||
let p = g.f(0);
|
||||
assertEq(hits, 1);
|
||||
assertEq(g.hit2, false);
|
||||
let pw = gw.makeDebuggeeValue(p);
|
||||
assertEq(pw.isPromise, true);
|
||||
assertEq(pw.promiseState, "rejected");
|
||||
assertEq(pw.promiseReason, "fit");
|
||||
assertEq(pw.promiseState, "fulfilled");
|
||||
assertEq(pw.promiseValue, resumption.return);
|
||||
|
@ -14,11 +14,10 @@ let gw = dbg.addDebuggee(g);
|
||||
let errw = gw.makeDebuggeeValue(g.err);
|
||||
|
||||
// Repeat the test for each onEnterFrame event.
|
||||
// It fires up to three times:
|
||||
// It fires up to two times:
|
||||
// - when the async function g.f is called;
|
||||
// - when we enter it to run to `await 1`;
|
||||
// - when we resume after the await to run to the end.
|
||||
for (let when = 0; when < 3; when++) {
|
||||
for (let when = 0; when < 2; when++) {
|
||||
let hits = 0;
|
||||
dbg.onEnterFrame = frame => {
|
||||
return hits++ < when ? undefined : {throw: errw};
|
||||
|
@ -26,5 +26,4 @@ function test(when) {
|
||||
|
||||
// onEnterFrame with hits==0 is not a resume point; {return:} behaves differently there
|
||||
// (see onEnterFrame-async-resumption-02.js).
|
||||
test(1); // force return from first resume point, immediately after the initial suspend
|
||||
test(2); // force return from second resume point, immediately after the await instruction
|
||||
test(1); // force return from first resume point, immediately after the await instruction
|
||||
|
@ -17,9 +17,9 @@ let dbg = new Debugger(g);
|
||||
dbg.onEnterFrame = frame => {
|
||||
if (!("hits" in frame)) {
|
||||
frame.hits = 1;
|
||||
} else if (++frame.hits == 3) {
|
||||
// First two hits happen when g.af() is called;
|
||||
// third hit is resuming at the `await` inside the try block.
|
||||
} else if (++frame.hits === 2) {
|
||||
// First hit happens when g.af() is called;
|
||||
// second hit is resuming at the `await` inside the try block.
|
||||
return {throw: "fit"};
|
||||
}
|
||||
};
|
||||
|
@ -0,0 +1,32 @@
|
||||
// A Debugger can {return:} from the first onEnterFrame for an async generator.
|
||||
// (The exact behavior is undocumented; we're testing that it doesn't crash.)
|
||||
|
||||
ignoreUnhandledRejections();
|
||||
|
||||
let g = newGlobal({newCompartment: true});
|
||||
g.hit2 = false;
|
||||
g.eval(`async function* f(x) { await x; return "ponies"; }`);
|
||||
|
||||
let dbg = new Debugger;
|
||||
let gw = dbg.addDebuggee(g);
|
||||
let hits = 0;
|
||||
let resumption = undefined;
|
||||
dbg.onEnterFrame = frame => {
|
||||
if (frame.type == "call" && frame.callee.name === "f") {
|
||||
frame.onPop = completion => {
|
||||
assertEq(completion.return, resumption.return);
|
||||
hits++;
|
||||
};
|
||||
|
||||
// If we force-return a generator object here, the caller will never
|
||||
// receive an async generator object.
|
||||
resumption = frame.eval(`(function* f2() { hit2 = true; })()`);
|
||||
assertEq(resumption.return.class, "Generator");
|
||||
return resumption;
|
||||
}
|
||||
};
|
||||
|
||||
let it = g.f(0);
|
||||
assertEq(hits, 1);
|
||||
assertEq(gw.makeDebuggeeValue(it), resumption.return);
|
||||
assertEq(g.hit2, false);
|
@ -0,0 +1,49 @@
|
||||
// A Debugger can {return:} from the first onEnterFrame for an async function.
|
||||
// (The exact behavior is undocumented; we're testing that it doesn't crash.)
|
||||
|
||||
ignoreUnhandledRejections();
|
||||
|
||||
let g = newGlobal({newCompartment: true});
|
||||
g.eval(`async function f(x) { await x; return "ponies"; }`);
|
||||
g.eval(`async function f2(x) { await x; return "moar ponies"; }`);
|
||||
|
||||
let dbg = new Debugger;
|
||||
let gw = dbg.addDebuggee(g);
|
||||
let hits = 0;
|
||||
let resumption = undefined;
|
||||
let savedAsyncGen = undefined;
|
||||
dbg.onEnterFrame = frame => {
|
||||
if (frame.type == "call" && frame.callee.name === "f2") {
|
||||
frame.onPop = completion => {
|
||||
if (savedAsyncGen === undefined) {
|
||||
savedAsyncGen = completion.return;
|
||||
}
|
||||
};
|
||||
}
|
||||
if (frame.type == "call" && frame.callee.name === "f") {
|
||||
frame.onPop = completion => {
|
||||
hits++;
|
||||
};
|
||||
|
||||
return {return: savedAsyncGen};
|
||||
}
|
||||
};
|
||||
|
||||
let p2 = g.f2(0);
|
||||
let p = g.f(0);
|
||||
|
||||
assertEq(hits, 1);
|
||||
|
||||
drainJobQueue();
|
||||
|
||||
assertEq(hits, 1);
|
||||
|
||||
let pw2 = gw.makeDebuggeeValue(p2);
|
||||
assertEq(pw2.isPromise, true);
|
||||
assertEq(pw2.promiseState, "fulfilled");
|
||||
assertEq(pw2.promiseValue, "moar ponies");
|
||||
|
||||
let pw = gw.makeDebuggeeValue(p);
|
||||
assertEq(pw.isPromise, true);
|
||||
assertEq(pw.promiseState, "fulfilled");
|
||||
assertEq(pw.promiseValue, "moar ponies");
|
@ -0,0 +1,54 @@
|
||||
// A Debugger can {return:} from the first onEnterFrame for an async generator.
|
||||
// (The exact behavior is undocumented; we're testing that it doesn't crash.)
|
||||
|
||||
ignoreUnhandledRejections();
|
||||
|
||||
let g = newGlobal({newCompartment: true});
|
||||
g.eval(`async function* f(x) { await x; return "ponies"; }`);
|
||||
g.eval(`async function* f2(x) { await x; return "moar ponies"; }`);
|
||||
|
||||
let dbg = new Debugger;
|
||||
let gw = dbg.addDebuggee(g);
|
||||
let hits = 0;
|
||||
let resumption = undefined;
|
||||
let savedAsyncGen = undefined;
|
||||
dbg.onEnterFrame = frame => {
|
||||
if (frame.type == "call" && frame.callee.name === "f2") {
|
||||
frame.onPop = completion => {
|
||||
if (savedAsyncGen === undefined) {
|
||||
savedAsyncGen = completion.return;
|
||||
}
|
||||
};
|
||||
}
|
||||
if (frame.type == "call" && frame.callee.name === "f") {
|
||||
frame.onPop = completion => {
|
||||
hits++;
|
||||
};
|
||||
|
||||
return {return: savedAsyncGen};
|
||||
}
|
||||
};
|
||||
|
||||
let it2 = g.f2(123);
|
||||
let it = g.f(0);
|
||||
|
||||
let p2 = it2.next();
|
||||
let p = it.next();
|
||||
|
||||
assertEq(hits, 1);
|
||||
|
||||
drainJobQueue();
|
||||
|
||||
assertEq(hits, 1);
|
||||
|
||||
let pw2 = gw.makeDebuggeeValue(p2);
|
||||
assertEq(pw2.isPromise, true);
|
||||
assertEq(pw2.promiseState, "fulfilled");
|
||||
assertEq(pw2.promiseValue.getProperty("value").return, "moar ponies");
|
||||
assertEq(pw2.promiseValue.getProperty("done").return, true);
|
||||
|
||||
let pw = gw.makeDebuggeeValue(p);
|
||||
assertEq(pw.isPromise, true);
|
||||
assertEq(pw.promiseState, "fulfilled");
|
||||
assertEq(pw.promiseValue.getProperty("value").return, undefined);
|
||||
assertEq(pw.promiseValue.getProperty("done").return, true);
|
18
js/src/jit-test/tests/debug/save-queue-resets-draining.js
Normal file
18
js/src/jit-test/tests/debug/save-queue-resets-draining.js
Normal file
@ -0,0 +1,18 @@
|
||||
// The draining state is reset when saving the job queue.
|
||||
|
||||
let g = newGlobal({newCompartment: true});
|
||||
|
||||
let dbg = new Debugger();
|
||||
let gw = dbg.addDebuggee(g);
|
||||
|
||||
dbg.onDebuggerStatement = frame => {
|
||||
// Enqueue a new job from within the debugger while executing another job
|
||||
// from outside of the debugger.
|
||||
enqueueJob(function() {});
|
||||
};
|
||||
|
||||
g.eval(`
|
||||
enqueueJob(function() {
|
||||
debugger;
|
||||
});
|
||||
`);
|
@ -0,0 +1,44 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL no longer apply after the initial Ion
|
||||
// compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// The rest argument is overwritten with a non-Array object.
|
||||
function test() {
|
||||
var badRest = {
|
||||
*[Symbol.iterator]() {
|
||||
yield 3;
|
||||
yield 4;
|
||||
}
|
||||
};
|
||||
function maybeInvalidate(rest) {
|
||||
// Use a WithStatement to prevent Ion-inlining. This ensures any
|
||||
// bailouts due to type changes don't occur in this function, but
|
||||
// instead in the caller.
|
||||
with ({});
|
||||
|
||||
if (i >= 1900) {
|
||||
return badRest;
|
||||
}
|
||||
return rest;
|
||||
}
|
||||
function fn(...rest) {
|
||||
rest = maybeInvalidate(rest);
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 4000; ++i) {
|
||||
assertEq(fn(1, 2), i < 1900 ? 3 : 7);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,37 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL no longer apply after the initial Ion
|
||||
// compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b, c = 0, d = 0) {
|
||||
return a + b + c + d;
|
||||
}
|
||||
|
||||
// The rest argument contains holes.
|
||||
function test() {
|
||||
function maybeInvalidate(rest) {
|
||||
// Use a WithStatement to prevent Ion-inlining. This ensures any
|
||||
// bailouts due to type changes don't occur in this function, but
|
||||
// instead in the caller.
|
||||
with ({});
|
||||
|
||||
if (i >= 1900) {
|
||||
rest[3] = 4;
|
||||
}
|
||||
}
|
||||
function fn(...rest) {
|
||||
maybeInvalidate(rest);
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 4000; ++i) {
|
||||
assertEq(fn(1, 2), i < 1900 ? 3 : 7);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,40 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL no longer apply after the initial Ion
|
||||
// compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// The rest argument has an own @@iterator property.
|
||||
function test() {
|
||||
function MyIter() {
|
||||
return [3, 4][Symbol.iterator]();
|
||||
}
|
||||
function maybeInvalidate(rest) {
|
||||
// Use a WithStatement to prevent Ion-inlining. This ensures any
|
||||
// bailouts due to type changes don't occur in this function, but
|
||||
// instead in the caller.
|
||||
with ({});
|
||||
|
||||
if (i >= 1900) {
|
||||
rest[Symbol.iterator] = MyIter;
|
||||
}
|
||||
}
|
||||
function fn(...rest) {
|
||||
maybeInvalidate(rest);
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 4000; ++i) {
|
||||
assertEq(fn(1, 2), i < 1900 ? 3 : 7);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,43 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL no longer apply after the initial Ion
|
||||
// compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// The rest arguments don't share a common prototype.
|
||||
function test() {
|
||||
class MyArray1 extends Array { }
|
||||
class MyArray2 extends Array { }
|
||||
function maybeInvalidate(rest) {
|
||||
// Use a WithStatement to prevent Ion-inlining. This ensures any
|
||||
// bailouts due to type changes don't occur in this function, but
|
||||
// instead in the caller.
|
||||
with ({});
|
||||
|
||||
if (i >= 1900) {
|
||||
if (i & 1)
|
||||
rest = new MyArray1(3, 4);
|
||||
else
|
||||
rest = new MyArray2(5, 6);
|
||||
}
|
||||
return rest;
|
||||
}
|
||||
function fn(...rest) {
|
||||
rest = maybeInvalidate(rest);
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 4000; ++i) {
|
||||
assertEq(fn(1, 2), i < 1900 ? 3 : (i & 1) ? 7 : 11);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,39 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL no longer apply after the initial Ion
|
||||
// compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// The rest argument's prototype isn't Array.prototype.
|
||||
function test() {
|
||||
class MyArray extends Array { }
|
||||
function maybeInvalidate(rest) {
|
||||
// Use a WithStatement to prevent Ion-inlining. This ensures any
|
||||
// bailouts due to type changes don't occur in this function, but
|
||||
// instead in the caller.
|
||||
with ({});
|
||||
|
||||
if (i >= 1900) {
|
||||
rest = new MyArray(3, 4);
|
||||
}
|
||||
return rest;
|
||||
}
|
||||
function fn(...rest) {
|
||||
rest = maybeInvalidate(rest);
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 4000; ++i) {
|
||||
assertEq(fn(1, 2), i < 1900 ? 3 : 7);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,37 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL no longer apply after the initial Ion
|
||||
// compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Array.prototype's [[Prototype]] was changed, as a result all properties are marked as unknown.
|
||||
function test() {
|
||||
function maybeInvalidate() {
|
||||
// Use a WithStatement to prevent Ion-inlining. This ensures any
|
||||
// bailouts due to type changes don't occur in this function, but
|
||||
// instead in the caller.
|
||||
with ({});
|
||||
|
||||
if (i === 1900) {
|
||||
Object.setPrototypeOf(Array.prototype, null);
|
||||
}
|
||||
}
|
||||
function fn(...rest) {
|
||||
maybeInvalidate();
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 4000; ++i) {
|
||||
assertEq(fn(1, 2), 3);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,40 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL no longer apply after the initial Ion
|
||||
// compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Array.prototype[@@iterator] was modified.
|
||||
function test() {
|
||||
function maybeInvalidate() {
|
||||
// Use a WithStatement to prevent Ion-inlining. This ensures any
|
||||
// bailouts due to type changes don't occur in this function, but
|
||||
// instead in the caller.
|
||||
with ({});
|
||||
|
||||
if (i === 1900) {
|
||||
var ArrayPrototypeIterator = Array.prototype[Symbol.iterator];
|
||||
Array.prototype[Symbol.iterator] = function() {
|
||||
return ArrayPrototypeIterator.call([3, 4]);
|
||||
};
|
||||
}
|
||||
}
|
||||
function fn(...rest) {
|
||||
maybeInvalidate();
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 4000; ++i) {
|
||||
assertEq(fn(1, 2), i < 1900 ? 3 : 7);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,38 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL no longer apply after the initial Ion
|
||||
// compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// %ArrayIteratorPrototype%'s [[Prototype]] was changed, as a result all properties are marked as unknown.
|
||||
function test() {
|
||||
function maybeInvalidate() {
|
||||
// Use a WithStatement to prevent Ion-inlining. This ensures any
|
||||
// bailouts due to type changes don't occur in this function, but
|
||||
// instead in the caller.
|
||||
with ({});
|
||||
|
||||
if (i === 1900) {
|
||||
var ArrayIteratorPrototype = Object.getPrototypeOf(Array.prototype[Symbol.iterator]());
|
||||
Object.setPrototypeOf(ArrayIteratorPrototype, null);
|
||||
}
|
||||
}
|
||||
function fn(...rest) {
|
||||
maybeInvalidate();
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 4000; ++i) {
|
||||
assertEq(fn(1, 2), 3);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,45 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL no longer apply after the initial Ion
|
||||
// compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// %ArrayIteratorPrototype%.next was modified.
|
||||
function test() {
|
||||
function maybeInvalidate() {
|
||||
// Use a WithStatement to prevent Ion-inlining. This ensures any
|
||||
// bailouts due to type changes don't occur in this function, but
|
||||
// instead in the caller.
|
||||
with ({});
|
||||
|
||||
if (i === 1900) {
|
||||
var ArrayIteratorPrototype = Object.getPrototypeOf(Array.prototype[Symbol.iterator]());
|
||||
var ArrayIteratorPrototypeNext = ArrayIteratorPrototype.next;
|
||||
ArrayIteratorPrototype.next = function() {
|
||||
var res = ArrayIteratorPrototypeNext.call(this);
|
||||
if (!res.done) {
|
||||
res.value += 2;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
}
|
||||
}
|
||||
function fn(...rest) {
|
||||
maybeInvalidate();
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 4000; ++i) {
|
||||
assertEq(fn(1, 2), i < 1900 ? 3 : 7);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,33 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL can't be applied during the initial
|
||||
// Ion compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// The rest argument is overwritten with a non-Array object.
|
||||
function test() {
|
||||
var badRest = {
|
||||
*[Symbol.iterator]() {
|
||||
yield 3;
|
||||
yield 4;
|
||||
}
|
||||
};
|
||||
function fn(...rest) {
|
||||
rest = badRest;
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 2000; ++i) {
|
||||
assertEq(fn(1, 2), 7);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,27 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL can't be applied during the initial
|
||||
// Ion compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b, c = 0, d = 0) {
|
||||
return a + b + c + d;
|
||||
}
|
||||
|
||||
// The rest argument contains holes.
|
||||
function test() {
|
||||
function fn(...rest) {
|
||||
rest[3] = 4;
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 2000; ++i) {
|
||||
assertEq(fn(1, 2), 7);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,30 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL can't be applied during the initial
|
||||
// Ion compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// The rest argument has an own @@iterator property.
|
||||
function test() {
|
||||
function MyIter() {
|
||||
return [3, 4][Symbol.iterator]();
|
||||
}
|
||||
function fn(...rest) {
|
||||
rest[Symbol.iterator] = MyIter;
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 2000; ++i) {
|
||||
assertEq(fn(1, 2), 7);
|
||||
}
|
||||
}
|
||||
test();
|
@ -0,0 +1,32 @@
|
||||
// Tests when JSOP_OPTIMIZE_SPREADCALL can't be applied during the initial
|
||||
// Ion compilation.
|
||||
|
||||
// JSOP_OPTIMIZE_SPREADCALL can be optimised when the following conditions
|
||||
// are fulfilled:
|
||||
// (1) the argument is an array
|
||||
// (2) the array has no hole
|
||||
// (3) array[@@iterator] is not modified
|
||||
// (4) the array's prototype is Array.prototype
|
||||
// (5) Array.prototype[@@iterator] is not modified
|
||||
// (6) %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// The rest arguments don't share a common prototype.
|
||||
function test() {
|
||||
class MyArray1 extends Array { }
|
||||
class MyArray2 extends Array { }
|
||||
function fn(...rest) {
|
||||
if (i & 1)
|
||||
rest = new MyArray1(3, 4);
|
||||
else
|
||||
rest = new MyArray2(5, 6);
|
||||
return add(...rest);
|
||||
}
|
||||
for (var i = 0; i < 2000; ++i) {
|
||||
assertEq(fn(1, 2), (i & 1) ? 7 : 11);
|
||||
}
|
||||
}
|
||||
test();
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user