Merge mozilla-inbound to mozilla-central. a=merge

This commit is contained in:
Cosmin Sabou 2019-02-28 12:57:50 +02:00
commit 00f3836a87
11263 changed files with 13708 additions and 31533 deletions

View File

@ -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);

View File

@ -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
});

View File

@ -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);
}
}
}

View File

@ -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(

View File

@ -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) {

View File

@ -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]

View File

@ -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);
});

View File

@ -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>

View File

@ -8,4 +8,4 @@ setInterval(timer, 1000);
self.onmessage = () => {
console.log('hi')
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -89,6 +89,10 @@ this.addEventListener("message", function(event) {
},
window: global,
onThreadAttached() {
postMessage(JSON.stringify({ type: "attached" }));
},
};
const threadActor = new ThreadActor(parent, global);

View File

@ -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!");

View File

@ -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);

View File

@ -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");

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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);
};

View File

@ -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() &&

View File

@ -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) {

View File

@ -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:

View File

@ -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(),

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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

View File

@ -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.
*/

View File

@ -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

View File

@ -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;
}

View File

@ -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,

View File

@ -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

View File

@ -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());

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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",

View File

@ -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.
*

View File

@ -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()) {

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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++) {

View File

@ -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());

View File

@ -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;

View File

@ -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 =

View File

@ -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();
}

View File

@ -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);

View File

@ -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)) {

View 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");
}

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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)) {

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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;

View File

@ -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()) {

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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) {

View File

@ -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(); }

View File

@ -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);

View 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);
}

View File

@ -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 += ")";

View File

@ -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)");

View File

@ -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);

View File

@ -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};

View File

@ -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

View File

@ -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"};
}
};

View File

@ -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);

View File

@ -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");

View File

@ -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);

View 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;
});
`);

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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