merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-10-31 16:46:52 +01:00
Родитель 71aac2e8b7 fc603eada8
Коммит a581a8b8cf
113 изменённых файлов: 2799 добавлений и 1004 удалений

95
browser/extensions/e10srollout/bootstrap.js поставляемый
Просмотреть файл

@ -9,9 +9,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/UpdateUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/TelemetryArchive.jsm");
Cu.import("resource://gre/modules/TelemetryController.jsm");
// The amount of people to be part of e10s
const TEST_THRESHOLD = {
@ -21,7 +18,7 @@ const TEST_THRESHOLD = {
const ADDON_ROLLOUT_POLICY = {
"beta" : "50allmpc", // Any WebExtension or addon with mpc = true
"release" : "49a", // 10 tested add-ons + any WebExtension
"release" : "50allmpc", // Any WebExtension or addon with mpc = true
};
const PREF_COHORT_SAMPLE = "e10s.rollout.cohortSample";
@ -33,9 +30,6 @@ const PREF_TOGGLE_E10S = "browser.tabs.remote.autostart.2";
const PREF_E10S_ADDON_POLICY = "extensions.e10s.rollout.policy";
const PREF_E10S_ADDON_BLOCKLIST = "extensions.e10s.rollout.blocklist";
const PREF_E10S_HAS_NONEXEMPT_ADDON = "extensions.e10s.rollout.hasAddon";
const PREF_DISABLED_FOR_SPINNERS = "e10s.rollout.disabledByLongSpinners";
const LONG_SPINNER_HISTOGRAM = "FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS";
function startup() {
// In theory we only need to run this once (on install()), but
@ -47,8 +41,6 @@ function startup() {
// to take effect, so we keep the data based on how it was when
// the session started.
defineCohort();
setUpSpinnerCheck();
}
function install() {
@ -178,10 +170,6 @@ function optedOut() {
* string must be returned.
*/
function getTemporaryDisqualification() {
if (Preferences.isSet(PREF_DISABLED_FOR_SPINNERS)) {
return "longspinner";
}
let applicationLanguage =
Cc["@mozilla.org/chrome/chrome-registry;1"]
.getService(Ci.nsIXULChromeRegistry)
@ -194,84 +182,3 @@ function getTemporaryDisqualification() {
return "";
}
let performLongSpinnerCheck = Task.async(function*() {
if (!Services.appinfo.browserTabsRemoteAutostart) {
return;
}
const DAYS_OLD = 3;
let thresholdDate = new Date(Date.now() - (1000 * 60 * 60 * 24 * DAYS_OLD));
let allPingsInfo = yield TelemetryArchive.promiseArchivedPingList();
let recentPingsInfo = allPingsInfo.filter(ping => {
let pingDate = new Date(ping.timestampCreated);
return pingDate > thresholdDate;
});
let pingList = [];
for (let pingInfo of recentPingsInfo) {
pingList.push(yield TelemetryArchive.promiseArchivedPingById(pingInfo.id));
}
pingList.push(TelemetryController.getCurrentPingData(/* subsession = */ true));
let totalSessionTime = 0;
let totalSpinnerTime = 0;
for (let ping of pingList) {
try {
if (ping.type != "main") {
continue;
}
if (!ping.environment.settings.e10sEnabled) {
continue;
}
totalSessionTime = ping.payload.info.subsessionLength;
if (!(LONG_SPINNER_HISTOGRAM in ping.payload.histograms)) {
// The Histogram might not be defined in this ping if no data was recorded for it.
// In this case, we still add the session length because that was a valid session
// without a long spinner.
continue;
}
let histogram = ping.payload.histograms[LONG_SPINNER_HISTOGRAM];
for (let spinnerTime of Object.keys(histogram.values)) {
// Only consider spinners that took more than 2 seconds.
// Note: the first bucket size that fits this criteria is
// 2297ms. And the largest bucket is 64000ms, meaning that
// any pause larger than that is only counted as a 64s pause.
// For reference, the bucket sizes are:
// 0, 1000, 2297, 5277, 12124, 27856, 64000
if (spinnerTime >= 2000) {
totalSpinnerTime += spinnerTime * histogram.values[spinnerTime];
}
}
} catch (e) { /* just in case there's a malformed ping, ignore it silently */ }
}
totalSpinnerTime /= 1000; // session time is in seconds, but spinner time in ms
const ACCEPTABLE_THRESHOLD = 20 / 3600; // 20 seconds per hour, per bug 1301131
if ((totalSpinnerTime / totalSessionTime) > ACCEPTABLE_THRESHOLD) {
Preferences.set(PREF_DISABLED_FOR_SPINNERS, true);
} else {
Preferences.reset(PREF_DISABLED_FOR_SPINNERS);
}
});
function setUpSpinnerCheck() {
let {setTimeout, setInterval} = Cu.import("resource://gre/modules/Timer.jsm");
// Perform an initial check after 5min (which should give good clearance from
// the startup process), and then a subsequent check every hour.
setTimeout(performLongSpinnerCheck, 1000 * 60 * 5);
setInterval(performLongSpinnerCheck, 1000 * 60 * 60);
}

Просмотреть файл

@ -10,7 +10,7 @@
<Description about="urn:mozilla:install-manifest">
<em:id>e10srollout@mozilla.org</em:id>
<em:version>1.4</em:version>
<em:version>1.5</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:multiprocessCompatible>true</em:multiprocessCompatible>

Просмотреть файл

@ -347,10 +347,11 @@ HTMLAnchorElement::ToString(nsAString& aSource)
return GetHref(aSource);
}
NS_IMETHODIMP
NS_IMETHODIMP
HTMLAnchorElement::GetPing(nsAString& aValue)
{
return GetURIListAttr(nsGkAtoms::ping, aValue);
GetAttr(kNameSpaceID_None, nsGkAtoms::ping, aValue);
return NS_OK;
}
NS_IMETHODIMP

Просмотреть файл

@ -221,10 +221,11 @@ HTMLAreaElement::ToString(nsAString& aSource)
return GetHref(aSource);
}
NS_IMETHODIMP
NS_IMETHODIMP
HTMLAreaElement::GetPing(nsAString& aValue)
{
return GetURIListAttr(nsGkAtoms::ping, aValue);
GetAttr(kNameSpaceID_None, nsGkAtoms::ping, aValue);
return NS_OK;
}
NS_IMETHODIMP

Просмотреть файл

@ -1679,68 +1679,6 @@ nsGenericHTMLElement::IsScrollGrabAllowed(JSContext*, JSObject*)
return nsContentUtils::IsSystemPrincipal(prin);
}
nsresult
nsGenericHTMLElement::GetURIListAttr(nsIAtom* aAttr, nsAString& aResult)
{
aResult.Truncate();
nsAutoString value;
if (!GetAttr(kNameSpaceID_None, aAttr, value))
return NS_OK;
nsIDocument* doc = OwnerDoc();
nsCOMPtr<nsIURI> baseURI = GetBaseURI();
nsString::const_iterator end;
value.EndReading(end);
nsAString::const_iterator iter;
value.BeginReading(iter);
while (iter != end) {
while (*iter == ' ' && iter != end) {
++iter;
}
if (iter == end) {
break;
}
nsAString::const_iterator start = iter;
while (iter != end && *iter != ' ') {
++iter;
}
if (!aResult.IsEmpty()) {
aResult.Append(NS_LITERAL_STRING(" "));
}
const nsSubstring& uriPart = Substring(start, iter);
nsCOMPtr<nsIURI> attrURI;
nsresult rv =
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(attrURI),
uriPart, doc, baseURI);
if (NS_FAILED(rv)) {
aResult.Append(uriPart);
continue;
}
MOZ_ASSERT(attrURI);
nsAutoCString spec;
rv = attrURI->GetSpec(spec);
if (NS_WARN_IF(NS_FAILED(rv))) {
aResult.Append(uriPart);
continue;
}
AppendUTF8toUTF16(spec, aResult);
}
return NS_OK;
}
HTMLMenuElement*
nsGenericHTMLElement::GetContextMenu() const
{

Просмотреть файл

@ -1062,20 +1062,6 @@ protected:
SetHTMLAttr(aAttr, value, aRv);
}
/**
* This method works like GetURIAttr, except that it supports multiple
* URIs separated by whitespace (one or more U+0020 SPACE characters).
*
* Gets the absolute URI values of an attribute, by resolving any relative
* URIs in the attribute against the baseuri of the element. If a substring
* isn't a relative URI, the substring is returned as is. Only works for
* attributes in null namespace.
*
* @param aAttr name of attribute.
* @param aResult result value [out]
*/
nsresult GetURIListAttr(nsIAtom* aAttr, nsAString& aResult);
/**
* Locates the nsIEditor associated with this node. In general this is
* equivalent to GetEditorInternal(), but for designmode or contenteditable,

Просмотреть файл

@ -5244,7 +5244,7 @@ bool
ContentParent::RecvAccumulateChildHistogram(
InfallibleTArray<Accumulation>&& aAccumulations)
{
Telemetry::AccumulateChild(aAccumulations);
Telemetry::AccumulateChild(GeckoProcessType_Content, aAccumulations);
return true;
}
@ -5252,6 +5252,6 @@ bool
ContentParent::RecvAccumulateChildKeyedHistogram(
InfallibleTArray<KeyedAccumulation>&& aAccumulations)
{
Telemetry::AccumulateChildKeyed(aAccumulations);
Telemetry::AccumulateChildKeyed(GeckoProcessType_Content, aAccumulations);
return true;
}

Просмотреть файл

@ -137,6 +137,20 @@ GPUChild::RecvNotifyUiObservers(const nsCString& aTopic)
return true;
}
bool
GPUChild::RecvAccumulateChildHistogram(InfallibleTArray<Accumulation>&& aAccumulations)
{
Telemetry::AccumulateChild(GeckoProcessType_GPU, aAccumulations);
return true;
}
bool
GPUChild::RecvAccumulateChildKeyedHistogram(InfallibleTArray<KeyedAccumulation>&& aAccumulations)
{
Telemetry::AccumulateChildKeyed(GeckoProcessType_GPU, aAccumulations);
return true;
}
void
GPUChild::ActorDestroy(ActorDestroyReason aWhy)
{

Просмотреть файл

@ -38,6 +38,8 @@ public:
bool RecvInitComplete(const GPUDeviceData& aData) override;
bool RecvReportCheckerboard(const uint32_t& aSeverity, const nsCString& aLog) override;
bool RecvInitCrashReporter(Shmem&& shmem) override;
bool RecvAccumulateChildHistogram(InfallibleTArray<Accumulation>&& aAccumulations) override;
bool RecvAccumulateChildKeyedHistogram(InfallibleTArray<KeyedAccumulation>&& aAccumulations) override;
void ActorDestroy(ActorDestroyReason aWhy) override;
bool RecvGraphicsError(const nsCString& aError) override;
bool RecvNotifyUiObservers(const nsCString& aTopic) override;

Просмотреть файл

@ -97,9 +97,11 @@ GPUParent::Init(base::ProcessId aParentPid,
DeviceManagerDx::Init();
DeviceManagerD3D9::Init();
#endif
if (NS_FAILED(NS_InitMinimalXPCOM())) {
return false;
}
CompositorThreadHolder::Start();
APZThreadUtils::SetControllerThread(CompositorThreadHolder::Loop());
APZCTreeManager::InitializeGlobalState();

Просмотреть файл

@ -137,6 +137,11 @@ public:
return mGPUChild;
}
// Returns whether or not a GPU process was ever launched.
bool AttemptedGPUProcess() const {
return mNumProcessAttempts > 0;
}
private:
// Called from our xpcom-shutdown observer.
void OnXPCOMShutdown();

Просмотреть файл

@ -14,6 +14,8 @@ using base::ProcessId from "base/process.h";
using mozilla::TimeDuration from "mozilla/TimeStamp.h";
using mozilla::CSSToLayoutDeviceScale from "Units.h";
using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
namespace mozilla {
namespace gfx {
@ -88,6 +90,10 @@ child:
// Have a message be broadcasted to the UI process by the UI process
// observer service.
async NotifyUiObservers(nsCString aTopic);
// Messages for reporting telemetry to the UI process.
async AccumulateChildHistogram(Accumulation[] accumulations);
async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations);
};
} // namespace gfx

Просмотреть файл

@ -1737,10 +1737,6 @@ ChildImpl::CloseForCurrentThread()
threadLocalInfo->mClosed = true;
#endif
if (threadLocalInfo->mActor) {
threadLocalInfo->mActor->FlushPendingInterruptQueue();
}
// Clearing the thread local will synchronously close the actor.
DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
MOZ_ASSERT(status == PR_SUCCESS);

Просмотреть файл

@ -489,10 +489,7 @@ MessageChannel::MessageChannel(MessageListener *aListener)
mTransactionStack(nullptr),
mTimedOutMessageSeqno(0),
mTimedOutMessageNestedLevel(0),
#if defined(MOZ_CRASHREPORTER) && defined(OS_WIN)
mPending(AnnotateAllocator<Message>(*this)),
#endif
mRemoteStackDepthGuess(false),
mRemoteStackDepthGuess(0),
mSawInterruptOutMsg(false),
mIsWaitingForIncoming(false),
mAbortOnError(false),
@ -508,12 +505,8 @@ MessageChannel::MessageChannel(MessageListener *aListener)
mIsSyncWaitingOnNonMainThread = false;
#endif
RefPtr<CancelableRunnable> runnable =
NewNonOwningCancelableRunnableMethod(this, &MessageChannel::OnMaybeDequeueOne);
mDequeueOneTask = new RefCountedTask(runnable.forget());
runnable = NewNonOwningCancelableRunnableMethod(this, &MessageChannel::DispatchOnChannelConnected);
mOnChannelConnectedTask = new RefCountedTask(runnable.forget());
mOnChannelConnectedTask =
NewNonOwningCancelableRunnableMethod(this, &MessageChannel::DispatchOnChannelConnected);
#ifdef OS_WIN
mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
@ -635,8 +628,6 @@ MessageChannel::Clear()
gParentProcessBlocker = nullptr;
}
mDequeueOneTask->Cancel();
mWorkerLoop = nullptr;
delete mLink;
mLink = nullptr;
@ -649,7 +640,11 @@ MessageChannel::Clear()
}
// Free up any memory used by pending messages.
for (RefPtr<MessageTask> task : mPending) {
task->Clear();
}
mPending.clear();
mOutOfTurnReplies.clear();
while (!mDeferred.empty()) {
mDeferred.pop();
@ -879,19 +874,6 @@ MessageChannel::ShouldDeferMessage(const Message& aMsg)
return mSide == ParentSide && aMsg.transaction_id() != CurrentNestedInsideSyncTransaction();
}
// Predicate that is true for messages that should be consolidated if 'compress' is set.
class MatchingKinds {
typedef IPC::Message Message;
Message::msgid_t mType;
int32_t mRoutingId;
public:
MatchingKinds(Message::msgid_t aType, int32_t aRoutingId) :
mType(aType), mRoutingId(aRoutingId) {}
bool operator()(const Message &msg) {
return msg.type() == mType && msg.routing_id() == mRoutingId;
}
};
void
MessageChannel::OnMessageReceivedFromLink(Message&& aMsg)
{
@ -925,31 +907,34 @@ MessageChannel::OnMessageReceivedFromLink(Message&& aMsg)
MOZ_RELEASE_ASSERT(aMsg.compress_type() == IPC::Message::COMPRESSION_NONE ||
aMsg.nested_level() == IPC::Message::NOT_NESTED);
bool compress = false;
bool reuseTask = false;
if (aMsg.compress_type() == IPC::Message::COMPRESSION_ENABLED) {
compress = (!mPending.empty() &&
mPending.back().type() == aMsg.type() &&
mPending.back().routing_id() == aMsg.routing_id());
bool compress = (!mPending.isEmpty() &&
mPending.getLast()->Msg().type() == aMsg.type() &&
mPending.getLast()->Msg().routing_id() == aMsg.routing_id());
if (compress) {
// This message type has compression enabled, and the back of the
// queue was the same message type and routed to the same destination.
// Replace it with the newer message.
MOZ_RELEASE_ASSERT(mPending.back().compress_type() ==
IPC::Message::COMPRESSION_ENABLED);
mPending.pop_back();
MOZ_RELEASE_ASSERT(mPending.getLast()->Msg().compress_type() ==
IPC::Message::COMPRESSION_ENABLED);
mPending.getLast()->Msg() = Move(aMsg);
reuseTask = true;
}
} else if (aMsg.compress_type() == IPC::Message::COMPRESSION_ALL) {
// Check the message queue for another message with this type/destination.
auto it = std::find_if(mPending.rbegin(), mPending.rend(),
MatchingKinds(aMsg.type(), aMsg.routing_id()));
if (it != mPending.rend()) {
// This message type has compression enabled, and the queue holds
// a message with the same message type and routed to the same destination.
// Erase it. Note that, since we always compress these redundancies, There Can
// Be Only One.
compress = true;
MOZ_RELEASE_ASSERT((*it).compress_type() == IPC::Message::COMPRESSION_ALL);
mPending.erase((++it).base());
} else if (aMsg.compress_type() == IPC::Message::COMPRESSION_ALL && !mPending.isEmpty()) {
for (RefPtr<MessageTask> p = mPending.getLast(); p; p = p->getPrevious()) {
if (p->Msg().type() == aMsg.type() &&
p->Msg().routing_id() == aMsg.routing_id())
{
// This message type has compression enabled, and the queue
// holds a message with the same message type and routed to the
// same destination. Erase it. Note that, since we always
// compress these redundancies, There Can Be Only One.
MOZ_RELEASE_ASSERT(p->Msg().compress_type() == IPC::Message::COMPRESSION_ALL);
p->remove();
break;
}
}
}
@ -959,7 +944,7 @@ MessageChannel::OnMessageReceivedFromLink(Message&& aMsg)
wakeUpSyncSend ||
AwaitingIncomingMessage();
// Although we usually don't need to post an OnMaybeDequeueOne task if
// Although we usually don't need to post a message task if
// shouldWakeUp is true, it's easier to post anyway than to have to
// guarantee that every Send call processes everything it's supposed to
// before returning.
@ -968,6 +953,10 @@ MessageChannel::OnMessageReceivedFromLink(Message&& aMsg)
IPC_LOG("Receive on link thread; seqno=%d, xid=%d, shouldWakeUp=%d",
aMsg.seqno(), aMsg.transaction_id(), shouldWakeUp);
if (reuseTask) {
return;
}
// There are three cases we're concerned about, relating to the state of the
// main thread:
//
@ -990,29 +979,26 @@ MessageChannel::OnMessageReceivedFromLink(Message&& aMsg)
// blocked. This is okay, since we always check for pending events before
// blocking again.
mPending.push_back(Move(aMsg));
RefPtr<MessageTask> task = new MessageTask(this, Move(aMsg));
mPending.insertBack(task);
if (shouldWakeUp) {
NotifyWorkerThread();
}
if (shouldPostTask) {
if (!compress) {
// If we compressed away the previous message, we'll re-use
// its pending task.
RefPtr<DequeueTask> task = new DequeueTask(mDequeueOneTask);
mWorkerLoop->PostTask(task.forget());
}
task->Post();
}
}
void
MessageChannel::PeekMessages(mozilla::function<bool(const Message& aMsg)> aInvoke)
{
// FIXME: We shouldn't be holding the lock for aInvoke!
MonitorAutoLock lock(*mMonitor);
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); it++) {
Message &msg = *it;
for (RefPtr<MessageTask> it : mPending) {
const Message &msg = it->Msg();
if (!aInvoke(msg)) {
break;
}
@ -1022,6 +1008,8 @@ MessageChannel::PeekMessages(mozilla::function<bool(const Message& aMsg)> aInvok
void
MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction)
{
mMonitor->AssertCurrentThreadOwns();
IPC_LOG("ProcessPendingRequests for seqno=%d, xid=%d",
aTransaction.SequenceNumber(), aTransaction.TransactionID());
@ -1038,8 +1026,8 @@ MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction)
mozilla::Vector<Message> toProcess;
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); ) {
Message &msg = *it;
for (RefPtr<MessageTask> p = mPending.getFirst(); p; ) {
Message &msg = p->Msg();
MOZ_RELEASE_ASSERT(!aTransaction.IsCanceled(),
"Calling ShouldDeferMessage when cancelled");
@ -1053,10 +1041,11 @@ MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction)
if (!defer) {
if (!toProcess.append(Move(msg)))
MOZ_CRASH();
it = mPending.erase(it);
p = p->removeAndGetNext();
continue;
}
it++;
p = p->getNext();
}
if (toProcess.empty()) {
@ -1358,9 +1347,9 @@ MessageChannel::Call(Message* aMsg, Message* aReply)
{
recvd = Move(it->second);
mOutOfTurnReplies.erase(it);
} else if (!mPending.empty()) {
recvd = Move(mPending.front());
mPending.pop_front();
} else if (!mPending.isEmpty()) {
RefPtr<MessageTask> task = mPending.popFirst();
recvd = Move(task->Msg());
} else {
// because of subtleties with nested event loops, it's possible
// that we got here and nothing happened. or, we might have a
@ -1449,18 +1438,19 @@ MessageChannel::WaitForIncomingMessage()
NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
#endif
{ // Scope for lock
MonitorAutoLock lock(*mMonitor);
AutoEnterWaitForIncoming waitingForIncoming(*this);
if (mChannelState != ChannelConnected) {
return false;
}
if (!HasPendingEvents()) {
return WaitForInterruptNotify();
}
MonitorAutoLock lock(*mMonitor);
AutoEnterWaitForIncoming waitingForIncoming(*this);
if (mChannelState != ChannelConnected) {
return false;
}
if (!HasPendingEvents()) {
return WaitForInterruptNotify();
}
return OnMaybeDequeueOne();
MOZ_RELEASE_ASSERT(!mPending.isEmpty());
RefPtr<MessageTask> task = mPending.getFirst();
RunMessage(*task);
return true;
}
bool
@ -1468,7 +1458,7 @@ MessageChannel::HasPendingEvents()
{
AssertWorkerThread();
mMonitor->AssertCurrentThreadOwns();
return Connected() && !mPending.empty();
return Connected() && !mPending.isEmpty();
}
bool
@ -1479,7 +1469,7 @@ MessageChannel::InterruptEventOccurred()
IPC_ASSERT(InterruptStackDepth() > 0, "not in wait loop");
return (!Connected() ||
!mPending.empty() ||
!mPending.isEmpty() ||
(!mOutOfTurnReplies.empty() &&
mOutOfTurnReplies.find(mInterruptStack.top().seqno()) !=
mOutOfTurnReplies.end()));
@ -1503,19 +1493,12 @@ MessageChannel::ProcessPendingRequest(Message &&aUrgent)
}
bool
MessageChannel::DequeueOne(Message *recvd)
MessageChannel::ShouldRunMessage(const Message& aMsg)
{
AssertWorkerThread();
mMonitor->AssertCurrentThreadOwns();
if (!Connected()) {
ReportConnectionError("OnMaybeDequeueOne");
return false;
if (!mTimedOutMessageSeqno) {
return true;
}
if (!mDeferred.empty())
MaybeUndeferIncall();
// If we've timed out a message and we're awaiting the reply to the timed
// out message, we have to be careful what messages we process. Here's what
// can go wrong:
@ -1532,56 +1515,131 @@ MessageChannel::DequeueOne(Message *recvd)
// message unless the child would need the response to that message in order
// to process M. Those messages are the ones that have a higher nested level
// than M or that are part of the same transaction as M.
if (mTimedOutMessageSeqno) {
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); it++) {
Message &msg = *it;
if (msg.nested_level() > mTimedOutMessageNestedLevel ||
(msg.nested_level() == mTimedOutMessageNestedLevel
&& msg.transaction_id() == mTimedOutMessageSeqno))
{
*recvd = Move(msg);
mPending.erase(it);
return true;
}
}
if (aMsg.nested_level() < mTimedOutMessageNestedLevel ||
(aMsg.nested_level() == mTimedOutMessageNestedLevel
&& aMsg.transaction_id() != mTimedOutMessageSeqno))
{
return false;
}
if (mPending.empty())
return false;
*recvd = Move(mPending.front());
mPending.pop_front();
return true;
}
bool
MessageChannel::OnMaybeDequeueOne()
{
AssertWorkerThread();
mMonitor->AssertNotCurrentThreadOwns();
Message recvd;
MonitorAutoLock lock(*mMonitor);
if (!DequeueOne(&recvd))
return false;
if (IsOnCxxStack() && recvd.is_interrupt() && recvd.is_reply()) {
// We probably just received a reply in a nested loop for an
// Interrupt call sent before entering that loop.
mOutOfTurnReplies[recvd.seqno()] = Move(recvd);
return false;
}
DispatchMessage(Move(recvd));
return true;
}
void
MessageChannel::RunMessage(MessageTask& aTask)
{
AssertWorkerThread();
mMonitor->AssertCurrentThreadOwns();
Message& msg = aTask.Msg();
if (!Connected()) {
ReportConnectionError("RunMessage");
return;
}
// Check that we're going to run the first message that's valid to run.
#ifdef DEBUG
for (RefPtr<MessageTask> task : mPending) {
if (task == &aTask) {
break;
}
MOZ_ASSERT(!ShouldRunMessage(task->Msg()));
}
#endif
if (!mDeferred.empty()) {
MaybeUndeferIncall();
}
if (!ShouldRunMessage(msg)) {
return;
}
MOZ_RELEASE_ASSERT(aTask.isInList());
aTask.remove();
if (IsOnCxxStack() && msg.is_interrupt() && msg.is_reply()) {
// We probably just received a reply in a nested loop for an
// Interrupt call sent before entering that loop.
mOutOfTurnReplies[msg.seqno()] = Move(msg);
return;
}
DispatchMessage(Move(msg));
}
nsresult
MessageChannel::MessageTask::Run()
{
if (!mChannel) {
return NS_OK;
}
mChannel->AssertWorkerThread();
mChannel->mMonitor->AssertNotCurrentThreadOwns();
MonitorAutoLock lock(*mChannel->mMonitor);
// In case we choose not to run this message, we may need to be able to Post
// it again.
mScheduled = false;
if (!isInList()) {
return NS_OK;
}
mChannel->RunMessage(*this);
return NS_OK;
}
// Warning: This method removes the receiver from whatever list it might be in.
nsresult
MessageChannel::MessageTask::Cancel()
{
if (!mChannel) {
return NS_OK;
}
mChannel->AssertWorkerThread();
mChannel->mMonitor->AssertNotCurrentThreadOwns();
MonitorAutoLock lock(*mChannel->mMonitor);
if (!isInList()) {
return NS_OK;
}
remove();
return NS_OK;
}
void
MessageChannel::MessageTask::Post()
{
MOZ_RELEASE_ASSERT(!mScheduled);
MOZ_RELEASE_ASSERT(isInList());
mScheduled = true;
RefPtr<MessageTask> self = this;
mChannel->mWorkerLoop->PostTask(self.forget());
}
void
MessageChannel::MessageTask::Clear()
{
mChannel->AssertWorkerThread();
mChannel = nullptr;
}
void
MessageChannel::DispatchMessage(Message &&aMsg)
{
AssertWorkerThread();
mMonitor->AssertCurrentThreadOwns();
Maybe<AutoNoJSAPI> nojsapi;
if (ScriptSettingsInitialized() && NS_IsMainThread())
nojsapi.emplace();
@ -1780,29 +1838,9 @@ MessageChannel::MaybeUndeferIncall()
--mRemoteStackDepthGuess;
MOZ_RELEASE_ASSERT(call.nested_level() == IPC::Message::NOT_NESTED);
mPending.push_back(Move(call));
}
void
MessageChannel::FlushPendingInterruptQueue()
{
AssertWorkerThread();
mMonitor->AssertNotCurrentThreadOwns();
{
MonitorAutoLock lock(*mMonitor);
if (mDeferred.empty()) {
if (mPending.empty())
return;
const Message& last = mPending.back();
if (!last.is_interrupt() || last.is_reply())
return;
}
}
while (OnMaybeDequeueOne());
RefPtr<MessageTask> task = new MessageTask(this, Move(call));
mPending.insertBack(task);
task->Post();
}
void
@ -1825,18 +1863,10 @@ MessageChannel::EnqueuePendingMessages()
MaybeUndeferIncall();
for (size_t i = 0; i < mDeferred.size(); ++i) {
RefPtr<DequeueTask> task = new DequeueTask(mDequeueOneTask);
mWorkerLoop->PostTask(task.forget());
}
// XXX performance tuning knob: could process all or k pending
// messages here, rather than enqueuing for later processing
for (size_t i = 0; i < mPending.size(); ++i) {
RefPtr<DequeueTask> task = new DequeueTask(mDequeueOneTask);
mWorkerLoop->PostTask(task.forget());
}
RepostAllMessages();
}
static inline bool
@ -1943,7 +1973,7 @@ MessageChannel::OnChannelConnected(int32_t peer_id)
MOZ_RELEASE_ASSERT(!mPeerPidSet);
mPeerPidSet = true;
mPeerPid = peer_id;
RefPtr<DequeueTask> task = new DequeueTask(mOnChannelConnectedTask);
RefPtr<CancelableRunnable> task = mOnChannelConnectedTask;
mWorkerLoop->PostTask(task.forget());
}
@ -2291,16 +2321,14 @@ MessageChannel::DebugAbort(const char* file, int line, const char* cond,
mDeferred.size());
printf_stderr(" out-of-turn Interrupt replies stack size: %" PRIuSIZE "\n",
mOutOfTurnReplies.size());
printf_stderr(" Pending queue size: %" PRIuSIZE ", front to back:\n",
mPending.size());
MessageQueue pending = Move(mPending);
while (!pending.empty()) {
while (!pending.isEmpty()) {
printf_stderr(" [ %s%s ]\n",
pending.front().is_interrupt() ? "intr" :
(pending.front().is_sync() ? "sync" : "async"),
pending.front().is_reply() ? "reply" : "");
pending.pop_front();
pending.getFirst()->Msg().is_interrupt() ? "intr" :
(pending.getFirst()->Msg().is_sync() ? "sync" : "async"),
pending.getFirst()->Msg().is_reply() ? "reply" : "");
pending.popFirst();
}
NS_RUNTIMEABORT(why);
@ -2348,13 +2376,33 @@ MessageChannel::EndTimeout()
mTimedOutMessageSeqno = 0;
mTimedOutMessageNestedLevel = 0;
for (size_t i = 0; i < mPending.size(); i++) {
// There may be messages in the queue that we expected to process from
// OnMaybeDequeueOne. But during the timeout, that function will skip
// some messages. Now they're ready to be processed, so we enqueue more
// tasks.
RefPtr<DequeueTask> task = new DequeueTask(mDequeueOneTask);
mWorkerLoop->PostTask(task.forget());
RepostAllMessages();
}
void
MessageChannel::RepostAllMessages()
{
bool needRepost = false;
for (RefPtr<MessageTask> task : mPending) {
if (!task->IsScheduled()) {
needRepost = true;
}
}
if (!needRepost) {
// If everything is already scheduled to run, do nothing.
return;
}
// In some cases we may have deferred dispatch of some messages in the
// queue. Now we want to run them again. However, we can't just re-post
// those messages since the messages after them in mPending would then be
// before them in the event queue. So instead we cancel everything and
// re-post all messages in the correct order.
MessageQueue queue = Move(mPending);
while (RefPtr<MessageTask> task = queue.popFirst()) {
RefPtr<MessageTask> newTask = new MessageTask(this, Move(task->Msg()));
mPending.insertBack(newTask);
newTask->Post();
}
}
@ -2397,8 +2445,8 @@ MessageChannel::CancelTransaction(int transaction)
}
bool foundSync = false;
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); ) {
Message &msg = *it;
for (RefPtr<MessageTask> p = mPending.getFirst(); p; ) {
Message &msg = p->Msg();
// If there was a race between the parent and the child, then we may
// have a queued sync message. We want to drop this message from the
@ -2409,11 +2457,11 @@ MessageChannel::CancelTransaction(int transaction)
MOZ_RELEASE_ASSERT(msg.transaction_id() != transaction);
IPC_LOG("Removing msg from queue seqno=%d xid=%d", msg.seqno(), msg.transaction_id());
foundSync = true;
it = mPending.erase(it);
p = p->removeAndGetNext();
continue;
}
it++;
p = p->getNext();
}
}

Просмотреть файл

@ -183,8 +183,6 @@ class MessageChannel : HasResultCodes
*/
int32_t GetTopmostMessageRoutingId() const;
void FlushPendingInterruptQueue();
// Unsound_IsClosed and Unsound_NumQueuedMessages are safe to call from any
// thread, but they make no guarantees about whether you'll get an
// up-to-date value; the values are written on one thread and read without
@ -276,10 +274,6 @@ class MessageChannel : HasResultCodes
void MaybeUndeferIncall();
void EnqueuePendingMessages();
// Executed on the worker thread. Dequeues one pending message.
bool OnMaybeDequeueOne();
bool DequeueOne(Message *recvd);
// Dispatches an incoming message to its appropriate handler.
void DispatchMessage(Message &&aMsg);
@ -311,6 +305,8 @@ class MessageChannel : HasResultCodes
void EndTimeout();
void CancelTransaction(int transaction);
void RepostAllMessages();
// The "remote view of stack depth" can be different than the
// actual stack depth when there are out-of-turn replies. When we
// receive one, our actual Interrupt stack depth doesn't decrease, but
@ -456,119 +452,41 @@ class MessageChannel : HasResultCodes
}
private:
#if defined(MOZ_CRASHREPORTER) && defined(OS_WIN)
// TODO: Remove the condition OS_WIN above once we move to GCC 5 or higher,
// the code will be able to get compiled as std::deque will meet C++11
// allocator requirements.
template<class T>
struct AnnotateAllocator
class MessageTask :
public CancelableRunnable,
public LinkedListElement<RefPtr<MessageTask>>
{
typedef T value_type;
AnnotateAllocator(MessageChannel& channel) : mChannel(channel) {}
template<class U> AnnotateAllocator(const AnnotateAllocator<U>& other) :
mChannel(other.mChannel) {}
template<class U> bool operator==(const AnnotateAllocator<U>&) { return true; }
template<class U> bool operator!=(const AnnotateAllocator<U>&) { return false; }
T* allocate(size_t n) {
void* p = ::operator new(n * sizeof(T), std::nothrow);
if (!p && n) {
// Sort the pending messages by its type, note the sorting algorithm
// has to be in-place to avoid memory allocation.
MessageQueue& q = mChannel.mPending;
std::sort(q.begin(), q.end(), [](const Message& a, const Message& b) {
return a.type() < b.type();
});
public:
explicit MessageTask(MessageChannel* aChannel, Message&& aMessage)
: mChannel(aChannel), mMessage(Move(aMessage)), mScheduled(false)
{}
// Iterate over the sorted queue to find the message that has the
// highest number of count.
const char* topName = nullptr;
const char* curName = nullptr;
msgid_t topType = 0, curType = 0;
uint32_t topCount = 0, curCount = 0;
for (MessageQueue::iterator it = q.begin(); it != q.end(); ++it) {
Message &msg = *it;
if (msg.type() == curType) {
++curCount;
} else {
if (curCount > topCount) {
topName = curName;
topType = curType;
topCount = curCount;
}
curName = StringFromIPCMessageType(msg.type());
curType = msg.type();
curCount = 1;
}
}
// In case the last type is the top one.
if (curCount > topCount) {
topName = curName;
topType = curType;
topCount = curCount;
}
NS_IMETHOD Run() override;
nsresult Cancel() override;
void Post();
void Clear();
CrashReporter::AnnotatePendingIPC(q.size(), topCount, topName, topType);
bool IsScheduled() const { return mScheduled; }
mozalloc_handle_oom(n * sizeof(T));
}
return static_cast<T*>(p);
}
void deallocate(T* p, size_t n) {
::operator delete(p);
}
MessageChannel& mChannel;
Message& Msg() { return mMessage; }
const Message& Msg() const { return mMessage; }
private:
MessageTask() = delete;
MessageTask(const MessageTask&) = delete;
MessageChannel* mChannel;
Message mMessage;
bool mScheduled : 1;
};
typedef std::deque<Message, AnnotateAllocator<Message>> MessageQueue;
#else
typedef std::deque<Message> MessageQueue;
#endif
bool ShouldRunMessage(const Message& aMsg);
void RunMessage(MessageTask& aTask);
typedef LinkedList<RefPtr<MessageTask>> MessageQueue;
typedef std::map<size_t, Message> MessageMap;
typedef IPC::Message::msgid_t msgid_t;
// XXXkhuey this can almost certainly die.
// All dequeuing tasks require a single point of cancellation,
// which is handled via a reference-counted task.
class RefCountedTask
{
public:
explicit RefCountedTask(already_AddRefed<CancelableRunnable> aTask)
: mTask(aTask)
{ }
private:
~RefCountedTask() { }
public:
void Run() { mTask->Run(); }
void Cancel() { mTask->Cancel(); }
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedTask)
private:
RefPtr<CancelableRunnable> mTask;
};
// Wrap an existing task which can be cancelled at any time
// without the wrapper's knowledge.
class DequeueTask : public CancelableRunnable
{
public:
explicit DequeueTask(RefCountedTask* aTask)
: mTask(aTask)
{ }
NS_IMETHOD Run() override {
if (mTask) {
mTask->Run();
}
return NS_OK;
}
nsresult Cancel() override {
mTask = nullptr;
return NS_OK;
}
private:
RefPtr<RefCountedTask> mTask;
};
private:
// Based on presumption the listener owns and overlives the channel,
// this is never nullified.
@ -584,9 +502,6 @@ class MessageChannel : HasResultCodes
// during channel shutdown.
int mWorkerLoopID;
// A task encapsulating dequeuing one pending message.
RefPtr<RefCountedTask> mDequeueOneTask;
// Timeout periods are broken up in two to prevent system suspension from
// triggering an abort. This method (called by WaitForEvent with a 'did
// timeout' flag) decides if we should wait again for half of mTimeoutMs
@ -673,9 +588,7 @@ class MessageChannel : HasResultCodes
int32_t mTimedOutMessageSeqno;
int mTimedOutMessageNestedLevel;
// Queue of all incoming messages, except for replies to sync and urgent
// messages, which are delivered directly to mRecvd, and any pending urgent
// incall, which is stored in mPendingUrgentRequest.
// Queue of all incoming messages.
//
// If both this side and the other side are functioning correctly, the queue
// can only be in certain configurations. Let
@ -687,7 +600,7 @@ class MessageChannel : HasResultCodes
//
// The queue can only match this configuration
//
// A<* (S< | C< | R< (?{mStack.size() == 1} A<* (S< | C<)))
// A<* (S< | C< | R< (?{mInterruptStack.size() == 1} A<* (S< | C<)))
//
// The other side can send as many async messages |A<*| as it wants before
// sending us a blocking message.
@ -702,7 +615,7 @@ class MessageChannel : HasResultCodes
// |mRemoteStackDepth|, and races don't matter to the queue.)
//
// Final case, the other side replied to our most recent out-call |R<|.
// If that was the *only* out-call on our stack, |?{mStack.size() == 1}|,
// If that was the *only* out-call on our stack, |?{mInterruptStack.size() == 1}|,
// then other side "finished with us," and went back to its own business.
// That business might have included sending any number of async message
// |A<*| until sending a blocking message |(S< | C<)|. If we had more than
@ -727,7 +640,7 @@ class MessageChannel : HasResultCodes
//
// Then when processing an in-call |c|, it must be true that
//
// mStack.size() == c.remoteDepth
// mInterruptStack.size() == c.remoteDepth
//
// I.e., my depth is actually the same as what the other side thought it
// was when it sent in-call |c|. If this fails to hold, we have detected
@ -788,7 +701,7 @@ class MessageChannel : HasResultCodes
// Task and state used to asynchronously notify channel has been connected
// safely. This is necessary to be able to cancel notification if we are
// closed at the same time.
RefPtr<RefCountedTask> mOnChannelConnectedTask;
RefPtr<CancelableRunnable> mOnChannelConnectedTask;
bool mPeerPidSet;
int32_t mPeerPid;
};

Просмотреть файл

@ -3334,14 +3334,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
onstack.addstmt(StmtReturn(ExprCall(
ExprSelect(p.channelVar(), '.', p.onCxxStackVar().name))))
# void ProcessIncomingRacingInterruptCall
processincoming = MethodDefn(
MethodDecl('FlushPendingInterruptQueue', ret=Type.VOID))
processincoming.addstmt(StmtExpr(ExprCall(ExprSelect(_actorChannel(ExprVar.THIS), '.', 'FlushPendingInterruptQueue'))))
self.cls.addstmts([ onentered, onexited,
onenteredcall, onexitedcall,
onstack, processincoming, Whitespace.NL ])
onstack, Whitespace.NL ])
# OnChannelClose()
onclose = MethodDefn(MethodDecl('OnChannelClose'))

Просмотреть файл

@ -785,7 +785,7 @@ struct JSClass {
// application.
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
#define JSCLASS_GLOBAL_SLOT_COUNT \
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 37)
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 39)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \

Просмотреть файл

@ -7068,7 +7068,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
ParseContext* outerpc = m.parser().pc;
Directives directives(outerpc);
FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, directives, NotGenerator,
/* tryAnnexB = */ false);
SyncFunction, /* tryAnnexB = */ false);
if (!funbox)
return false;
funbox->initWithEnclosingParseContext(outerpc, frontend::Statement);

Просмотреть файл

@ -0,0 +1,47 @@
/* 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/. */
// Called when creating async function.
// See the comment for js::CreateAsyncFunction what unwrapped, wrapped and the
// return value are.
function AsyncFunction_wrap(unwrapped) {
var wrapper = function() {
// The try block is required to handle throws in default arguments properly.
try {
return AsyncFunction_start(callFunction(std_Function_apply, unwrapped, this, arguments));
} catch (e) {
var promiseCtor = GetBuiltinConstructor('Promise');
return callFunction(Promise_static_reject, promiseCtor, e);
}
};
return CreateAsyncFunction(wrapper, unwrapped);
}
function AsyncFunction_start(generator) {
return AsyncFunction_resume(generator, undefined, generator.next);
}
function AsyncFunction_resume(gen, v, method) {
var promiseCtor = GetBuiltinConstructor('Promise');
let result;
try {
// get back into async function, run to next await point
result = callFunction(method, gen, v);
} catch (exc) {
// The async function itself failed.
return callFunction(Promise_static_reject, promiseCtor, exc);
}
if (result.done)
return callFunction(Promise_static_resolve, promiseCtor, result.value);
// If we get here, `await` occurred. `gen` is paused at a yield point.
return callFunction(Promise_then,
callFunction(Promise_static_resolve, promiseCtor, result.value),
function(val) {
return AsyncFunction_resume(gen, val, gen.next);
}, function (err) {
return AsyncFunction_resume(gen, err, gen.throw);
});
}

Просмотреть файл

@ -966,6 +966,7 @@ js::FutexRuntime::wait(JSContext* cx, js::UniqueLock<js::Mutex>& locked,
// See explanation below.
if (state_ == WaitingInterrupted) {
UnlockGuard<Mutex> unlock(locked);
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED);
return false;
}

Просмотреть файл

@ -704,7 +704,7 @@ ObjectDefineProperties(JSContext* cx, HandleObject obj, HandleValue properties)
// Step 5.b.
if (desc.object() && desc.enumerable()) {
if (!GetProperty(cx, props, props, nextKey, &descObj) ||
!ToPropertyDescriptor(cx, descObj, false, &desc) ||
!ToPropertyDescriptor(cx, descObj, true, &desc) ||
!descriptors.append(desc) ||
!descriptorKeys.append(nextKey))
{

Просмотреть файл

@ -1919,8 +1919,8 @@ CommonStaticResolveRejectImpl(JSContext* cx, unsigned argc, Value* vp, Resolutio
/**
* ES2016, 25.4.4.4, Promise.reject.
*/
static bool
Promise_reject(JSContext* cx, unsigned argc, Value* vp)
bool
js::Promise_reject(JSContext* cx, unsigned argc, Value* vp)
{
return CommonStaticResolveRejectImpl(cx, argc, vp, RejectMode);
}
@ -1949,8 +1949,8 @@ PromiseObject::unforgeableReject(JSContext* cx, HandleValue value)
/**
* ES2016, 25.4.4.5, Promise.resolve.
*/
static bool
Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp)
bool
js::Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp)
{
return CommonStaticResolveRejectImpl(cx, argc, vp, ResolveMode);
}
@ -2051,8 +2051,8 @@ js::OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise, HandleVal
}
// ES2016, 25.4.5.3.
static bool
Promise_then(JSContext* cx, unsigned argc, Value* vp)
bool
js::Promise_then(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);

Просмотреть файл

@ -153,6 +153,13 @@ class PromiseTask : public JS::AsyncTask
virtual void execute() = 0;
};
bool
Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp);
bool
Promise_reject(JSContext* cx, unsigned argc, Value* vp);
bool
Promise_then(JSContext* cx, unsigned argc, Value* vp);
} // namespace js
#endif /* builtin_Promise_h */

Просмотреть файл

@ -93,6 +93,7 @@ enum UnaryOperator {
UNOP_BITNOT,
UNOP_TYPEOF,
UNOP_VOID,
UNOP_AWAIT,
UNOP_LIMIT
};
@ -162,7 +163,8 @@ static const char* const unopNames[] = {
"!", /* UNOP_NOT */
"~", /* UNOP_BITNOT */
"typeof", /* UNOP_TYPEOF */
"void" /* UNOP_VOID */
"void", /* UNOP_VOID */
"await" /* UNOP_AWAIT */
};
static const char* const nodeTypeNames[] = {
@ -466,7 +468,7 @@ class NodeBuilder
MOZ_MUST_USE bool function(ASTType type, TokenPos* pos,
HandleValue id, NodeVector& args, NodeVector& defaults,
HandleValue body, HandleValue rest, GeneratorStyle generatorStyle,
bool isExpression, MutableHandleValue dst);
bool isAsync, bool isExpression, MutableHandleValue dst);
MOZ_MUST_USE bool variableDeclarator(HandleValue id, HandleValue init, TokenPos* pos,
MutableHandleValue dst);
@ -1590,7 +1592,7 @@ bool
NodeBuilder::function(ASTType type, TokenPos* pos,
HandleValue id, NodeVector& args, NodeVector& defaults,
HandleValue body, HandleValue rest,
GeneratorStyle generatorStyle, bool isExpression,
GeneratorStyle generatorStyle, bool isAsync, bool isExpression,
MutableHandleValue dst)
{
RootedValue array(cx), defarray(cx);
@ -1601,6 +1603,7 @@ NodeBuilder::function(ASTType type, TokenPos* pos,
bool isGenerator = generatorStyle != GeneratorStyle::None;
RootedValue isGeneratorVal(cx, BooleanValue(isGenerator));
RootedValue isAsyncVal(cx, BooleanValue(isAsync));
RootedValue isExpressionVal(cx, BooleanValue(isExpression));
RootedValue cb(cx, callbacks[type]);
@ -1624,6 +1627,7 @@ NodeBuilder::function(ASTType type, TokenPos* pos,
"body", body,
"rest", rest,
"generator", isGeneratorVal,
"async", isAsyncVal,
"style", styleVal,
"expression", isExpressionVal,
dst);
@ -1636,6 +1640,7 @@ NodeBuilder::function(ASTType type, TokenPos* pos,
"body", body,
"rest", rest,
"generator", isGeneratorVal,
"async", isAsyncVal,
"expression", isExpressionVal,
dst);
}
@ -1804,6 +1809,7 @@ class ASTSerializer
bool function(ParseNode* pn, ASTType type, MutableHandleValue dst);
bool functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& defaults,
bool isAsync, bool isExpression,
MutableHandleValue body, MutableHandleValue rest);
bool functionBody(ParseNode* pn, TokenPos* pos, MutableHandleValue dst);
@ -1880,6 +1886,9 @@ ASTSerializer::unop(ParseNodeKind kind, JSOp op)
if (IsTypeofKind(kind))
return UNOP_TYPEOF;
if (kind == PNK_AWAIT)
return UNOP_AWAIT;
switch (op) {
case JSOP_NEG:
return UNOP_NEG;
@ -2907,7 +2916,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
return leftAssociate(pn, dst);
case PNK_POW:
return rightAssociate(pn, dst);
return rightAssociate(pn, dst);
case PNK_DELETENAME:
case PNK_DELETEPROP:
@ -2919,6 +2928,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
case PNK_NOT:
case PNK_BITNOT:
case PNK_POS:
case PNK_AWAIT:
case PNK_NEG: {
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
@ -3396,6 +3406,7 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
: GeneratorStyle::ES6)
: GeneratorStyle::None;
bool isAsync = pn->pn_funbox->isAsync();
bool isExpression =
#if JS_HAS_EXPR_CLOSURES
func->isExprBody();
@ -3416,13 +3427,14 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
rest.setUndefined();
else
rest.setNull();
return functionArgsAndBody(pn->pn_body, args, defaults, &body, &rest) &&
builder.function(type, &pn->pn_pos, id, args, defaults, body,
rest, generatorStyle, isExpression, dst);
return functionArgsAndBody(pn->pn_body, args, defaults, isAsync, isExpression, &body, &rest) &&
builder.function(type, &pn->pn_pos, id, args, defaults, body,
rest, generatorStyle, isAsync, isExpression, dst);
}
bool
ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& defaults,
bool isAsync, bool isExpression,
MutableHandleValue body, MutableHandleValue rest)
{
ParseNode* pnargs;
@ -3456,6 +3468,14 @@ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector&
pnstart = pnstart->pn_next;
}
// Async arrow with expression body is converted into STATEMENTLIST
// to insert initial yield.
if (isAsync && isExpression) {
MOZ_ASSERT(pnstart->getKind() == PNK_RETURN);
return functionArgs(pn, pnargs, args, defaults, rest) &&
expression(pnstart->pn_kid, body);
}
return functionArgs(pn, pnargs, args, defaults, rest) &&
functionBody(pnstart, &pnbody->pn_pos, body);
}

Просмотреть файл

@ -93,6 +93,9 @@
#define REGEXP_STICKY_FLAG 0x08
#define REGEXP_UNICODE_FLAG 0x10
#define ASYNC_WRAPPED_SLOT 1
#define ASYNC_UNWRAPPED_SLOT 1
#define MODULE_OBJECT_ENVIRONMENT_SLOT 2
#define MODULE_STATE_FAILED 0

Просмотреть файл

@ -63,7 +63,7 @@ class MOZ_STACK_CLASS BytecodeCompiler
JSScript* compileEvalScript(HandleObject environment, HandleScope enclosingScope);
ModuleObject* compileModule();
bool compileFunctionBody(MutableHandleFunction fun, Handle<PropertyNameVector> formals,
GeneratorKind generatorKind);
GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
ScriptSourceObject* sourceObjectPtr() const;
@ -435,7 +435,8 @@ BytecodeCompiler::compileModule()
bool
BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun,
Handle<PropertyNameVector> formals,
GeneratorKind generatorKind)
GeneratorKind generatorKind,
FunctionAsyncKind asyncKind)
{
MOZ_ASSERT(fun);
MOZ_ASSERT(fun->isTenured());
@ -453,7 +454,7 @@ BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun,
ParseNode* fn;
do {
Directives newDirectives = directives;
fn = parser->standaloneFunctionBody(fun, enclosingScope, formals, generatorKind,
fn = parser->standaloneFunctionBody(fun, enclosingScope, formals, generatorKind, asyncKind,
directives, &newDirectives);
if (!fn && !handleParseFailure(newDirectives))
return false;
@ -646,7 +647,8 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
MOZ_ASSERT(!lazy->isLegacyGenerator());
ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->strict(), lazy->generatorKind());
ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->strict(), lazy->generatorKind(),
lazy->asyncKind());
if (!pn)
return false;
@ -679,7 +681,8 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
static bool
CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyCompileOptions& options,
Handle<PropertyNameVector> formals, SourceBufferHolder& srcBuf,
HandleScope enclosingScope, GeneratorKind generatorKind)
HandleScope enclosingScope, GeneratorKind generatorKind,
FunctionAsyncKind asyncKind)
{
MOZ_ASSERT(!options.isRunOnce);
@ -689,7 +692,7 @@ CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyComp
BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, enclosingScope,
TraceLogger_ParserCompileFunction);
compiler.setSourceArgumentsNotIncluded();
return compiler.compileFunctionBody(fun, formals, generatorKind);
return compiler.compileFunctionBody(fun, formals, generatorKind, asyncKind);
}
bool
@ -698,7 +701,8 @@ frontend::CompileFunctionBody(JSContext* cx, MutableHandleFunction fun,
Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf,
HandleScope enclosingScope)
{
return CompileFunctionBody(cx, fun, options, formals, srcBuf, enclosingScope, NotGenerator);
return CompileFunctionBody(cx, fun, options, formals, srcBuf, enclosingScope, NotGenerator,
SyncFunction);
}
bool
@ -708,10 +712,9 @@ frontend::CompileFunctionBody(JSContext* cx, MutableHandleFunction fun,
{
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
return CompileFunctionBody(cx, fun, options, formals, srcBuf, emptyGlobalScope,
NotGenerator);
NotGenerator, SyncFunction);
}
bool
frontend::CompileStarGeneratorBody(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,
@ -720,5 +723,16 @@ frontend::CompileStarGeneratorBody(JSContext* cx, MutableHandleFunction fun,
{
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
return CompileFunctionBody(cx, fun, options, formals, srcBuf, emptyGlobalScope,
StarGenerator);
StarGenerator, SyncFunction);
}
bool
frontend::CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,
Handle<PropertyNameVector> formals,
JS::SourceBufferHolder& srcBuf)
{
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
return CompileFunctionBody(cx, fun, options, formals, srcBuf, emptyGlobalScope,
StarGenerator, AsyncFunction);
}

Просмотреть файл

@ -68,6 +68,11 @@ CompileStarGeneratorBody(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,
Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf);
MOZ_MUST_USE bool
CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,
Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf);
ScriptSourceObject*
CreateScriptSourceObject(ExclusiveContext* cx, const ReadOnlyCompileOptions& options);

Просмотреть файл

@ -2465,6 +2465,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
case PNK_YIELD_STAR:
case PNK_YIELD:
case PNK_AWAIT:
MOZ_ASSERT(pn->isArity(PN_BINARY));
*answer = true;
return true;
@ -6928,6 +6929,11 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
if (!pn->functionIsHoisted()) {
/* JSOP_LAMBDA_ARROW is always preceded by a new.target */
MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW));
if (funbox->isAsync()) {
MOZ_ASSERT(!needsProto);
return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow());
}
if (fun->isArrow()) {
if (sc->allowNewTarget()) {
if (!emit1(JSOP_NEWTARGET))
@ -6942,6 +6948,13 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
MOZ_ASSERT(pn->getOp() == JSOP_FUNWITHPROTO || pn->getOp() == JSOP_LAMBDA);
pn->setOp(JSOP_FUNWITHPROTO);
}
if (pn->getOp() == JSOP_DEFFUN) {
if (!emitIndex32(JSOP_LAMBDA, index))
return false;
return emit1(JSOP_DEFFUN);
}
return emitIndex32(pn->getOp(), index);
}
@ -6972,7 +6985,14 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext());
MOZ_ASSERT(pn->getOp() == JSOP_NOP);
switchToPrologue();
if (!emitIndex32(JSOP_DEFFUN, index))
if (funbox->isAsync()) {
if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow()))
return false;
} else {
if (!emitIndex32(JSOP_LAMBDA, index))
return false;
}
if (!emit1(JSOP_DEFFUN))
return false;
if (!updateSourceCoordNotes(pn->pn_pos.begin))
return false;
@ -6982,7 +7002,12 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
// For functions nested within functions and blocks, make a lambda and
// initialize the binding name of the function in the current scope.
auto emitLambda = [index](BytecodeEmitter* bce, const NameLocation&, bool) {
bool isAsync = funbox->isAsync();
auto emitLambda = [index, isAsync](BytecodeEmitter* bce, const NameLocation&, bool) {
if (isAsync) {
return bce->emitAsyncWrapper(index, /* needsHomeObject = */ false,
/* isArrow = */ false);
}
return bce->emitIndexOp(JSOP_LAMBDA, index);
};
@ -6995,6 +7020,74 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
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) {
// 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
// getintrinsic // unwrapped AsyncFunction_wrap
// undefined // unwrapped AsyncFunction_wrap undefined
// dupat 2 // unwrapped AsyncFunction_wrap undefined unwrapped
// call 1 // 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
// inithomeobject 1 // 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 (needsHomeObject) {
if (!emitAsyncWrapperLambda(index, isArrow))
return false;
}
if (!emitAtomOp(cx->names().AsyncFunction_wrap, JSOP_GETINTRINSIC))
return false;
if (!emit1(JSOP_UNDEFINED))
return false;
if (needsHomeObject) {
if (!emitDupAt(2))
return false;
} else {
if (!emitAsyncWrapperLambda(index, isArrow))
return false;
}
if (!emitCall(JSOP_CALL, 1))
return false;
return true;
}
bool
BytecodeEmitter::emitDo(ParseNode* pn)
{
@ -8376,8 +8469,17 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
propdef->pn_right->pn_funbox->needsHomeObject())
{
MOZ_ASSERT(propdef->pn_right->pn_funbox->function()->allowSuperProperty());
if (!emit2(JSOP_INITHOMEOBJECT, isIndex))
bool isAsync = propdef->pn_right->pn_funbox->isAsync();
if (isAsync) {
if (!emit1(JSOP_SWAP))
return false;
}
if (!emit2(JSOP_INITHOMEOBJECT, isIndex + isAsync))
return false;
if (isAsync) {
if (!emit1(JSOP_POP))
return false;
}
}
// Class methods are not enumerable.
@ -9243,6 +9345,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
break;
case PNK_YIELD:
case PNK_AWAIT:
if (!emitYield(pn))
return false;
break;

Просмотреть файл

@ -603,6 +603,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op);
MOZ_MUST_USE bool emitPropIncDec(ParseNode* pn);
MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow);
MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow);
MOZ_MUST_USE bool emitComputedPropertyName(ParseNode* computedPropName);
// Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM

Просмотреть файл

@ -311,6 +311,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
case PNK_CONDITIONAL:
case PNK_TYPEOFNAME:
case PNK_TYPEOFEXPR:
case PNK_AWAIT:
case PNK_VOID:
case PNK_NOT:
case PNK_BITNOT:
@ -1783,6 +1784,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
case PNK_YIELD:
case PNK_AWAIT:
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME) ||
(pn->pn_right->isKind(PNK_ASSIGN) &&

Просмотреть файл

@ -432,6 +432,11 @@ class FullParseHandler
return new_<BinaryNode>(PNK_YIELD_STAR, JSOP_NOP, pos, value, gen);
}
ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value, ParseNode* gen) {
TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
return new_<BinaryNode>(PNK_AWAIT, JSOP_YIELD, pos, value, gen);
}
// Statements
ParseNode* newStatementList(const TokenPos& pos) {

Просмотреть файл

@ -507,6 +507,7 @@ class NameResolver
break;
case PNK_YIELD:
case PNK_AWAIT:
MOZ_ASSERT(cur->isArity(PN_BINARY));
if (cur->pn_left) {
if (!resolve(cur->pn_left, prefix))

Просмотреть файл

@ -290,7 +290,8 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
// variable, or an assignment of a PNK_GENERATOR node to the '.generator'
// local, for a synthesized, prepended initial yield. Yum!
case PNK_YIELD_STAR:
case PNK_YIELD: {
case PNK_YIELD:
case PNK_AWAIT: {
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT(pn->pn_right);
MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME) ||

Просмотреть файл

@ -120,6 +120,7 @@ class ObjectBox;
F(VOID) \
F(NOT) \
F(BITNOT) \
F(AWAIT) \
\
/* \
* Binary operators. \
@ -350,7 +351,8 @@ IsTypeofKind(ParseNodeKind kind)
* PNK_NEG
* PNK_VOID, unary pn_kid: UNARY expr
* PNK_NOT,
* PNK_BITNOT
* PNK_BITNOT,
* PNK_AWAIT
* PNK_TYPEOFNAME, unary pn_kid: UNARY expr
* PNK_TYPEOFEXPR
* PNK_PREINCREMENT, unary pn_kid: MEMBER expr

Просмотреть файл

@ -436,7 +436,7 @@ UsedNameTracker::rewind(RewindToken token)
FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead,
JSFunction* fun, Directives directives, bool extraWarnings,
GeneratorKind generatorKind)
GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
: ObjectBox(fun, traceListHead),
SharedContext(cx, Kind::ObjectBox, directives, extraWarnings),
enclosingScope_(nullptr),
@ -450,6 +450,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac
startColumn(0),
length(0),
generatorKindBits_(GeneratorKindAsBits(generatorKind)),
asyncKindBits_(AsyncKindAsBits(asyncKind)),
isGenexpLambda(false),
hasDestructuringArgs(false),
hasParameterExprs(false),
@ -734,7 +735,8 @@ Parser<ParseHandler>::newObjectBox(JSObject* obj)
template <typename ParseHandler>
FunctionBox*
Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, Directives inheritedDirectives,
GeneratorKind generatorKind, bool tryAnnexB)
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB)
{
MOZ_ASSERT(fun);
MOZ_ASSERT_IF(tryAnnexB, !pc->sc()->strict());
@ -748,7 +750,7 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, Directives inheri
*/
FunctionBox* funbox =
alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, inheritedDirectives,
options().extraWarningsOption, generatorKind);
options().extraWarningsOption, generatorKind, asyncKind);
if (!funbox) {
ReportOutOfMemory(context);
return nullptr;
@ -855,7 +857,7 @@ Parser<ParseHandler>::isValidStrictBinding(PropertyName* name)
name != context->names().arguments &&
name != context->names().let &&
name != context->names().static_ &&
!IsKeyword(name);
!(IsKeyword(name) && name != context->names().await);
}
/*
@ -1967,6 +1969,7 @@ Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc)
if (!mn)
return null();
AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, true);
ParseNode* pn = statementList(YieldIsKeyword);
if (!pn)
return null();
@ -2195,6 +2198,7 @@ Parser<SyntaxParseHandler>::finishFunction()
if (pc->sc()->strict())
lazy->setStrict();
lazy->setGeneratorKind(funbox->generatorKind());
lazy->setAsyncKind(funbox->asyncKind());
if (funbox->isLikelyConstructorWrapper())
lazy->setLikelyConstructorWrapper();
if (funbox->isDerivedClassConstructor())
@ -2214,12 +2218,23 @@ Parser<SyntaxParseHandler>::finishFunction()
return true;
}
static YieldHandling
GetYieldHandling(GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
{
if (asyncKind == AsyncFunction)
return YieldIsName;
if (generatorKind == NotGenerator)
return YieldIsName;
return YieldIsKeyword;
}
template <>
ParseNode*
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
HandleScope enclosingScope,
Handle<PropertyNameVector> formals,
GeneratorKind generatorKind,
FunctionAsyncKind asyncKind,
Directives inheritedDirectives,
Directives* newDirectives)
{
@ -2235,7 +2250,7 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
fn->pn_body = argsbody;
FunctionBox* funbox = newFunctionBox(fn, fun, inheritedDirectives, generatorKind,
/* tryAnnexB = */ false);
asyncKind, /* tryAnnexB = */ false);
if (!funbox)
return null();
funbox->initStandaloneFunction(enclosingScope);
@ -2258,7 +2273,8 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
}
funbox->hasDuplicateParameters = duplicatedParam;
YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction);
ParseNode* pn = functionBody(InAllowed, yieldHandling, Statement, StatementListBody);
if (!pn)
return null();
@ -2394,6 +2410,16 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
} else {
MOZ_ASSERT(type == ExpressionBody);
// Async functions are implemented as star generators, and star
// generators are assumed to be statement lists, to prepend initial
// `yield`.
Node stmtList = null();
if (pc->isAsync()) {
stmtList = handler.newStatementList(pos());
if (!stmtList)
return null();
}
Node kid = assignExpr(inHandling, yieldHandling, TripledotProhibited);
if (!kid)
return null();
@ -2401,6 +2427,11 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
pn = handler.newReturnStatement(kid, handler.getPosition(kid));
if (!pn)
return null();
if (pc->isAsync()) {
handler.addStatementToList(stmtList, pn);
pn = stmtList;
}
}
switch (pc->generatorKind()) {
@ -2421,13 +2452,13 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
break;
case StarGenerator:
MOZ_ASSERT(kind != Arrow);
MOZ_ASSERT(type == StatementListBody);
MOZ_ASSERT_IF(!pc->isAsync(), kind != Arrow);
MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
break;
}
if (pc->isGenerator()) {
MOZ_ASSERT(type == StatementListBody);
MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
if (!declareDotGeneratorName())
return null();
Node generator = newDotGeneratorName();
@ -2453,7 +2484,8 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
template <typename ParseHandler>
JSFunction*
Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
GeneratorKind generatorKind, HandleObject proto)
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
HandleObject proto)
{
MOZ_ASSERT_IF(kind == Statement, atom != nullptr);
@ -2509,6 +2541,10 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
: JSFunction::INTERPRETED_GENERATOR);
}
// We store the async wrapper in a slot for later access.
if (asyncKind == AsyncFunction)
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
fun = NewFunctionWithProto(context, nullptr, 0, flags, nullptr, atom, proto,
allocKind, TenuredObject);
if (!fun)
@ -2618,19 +2654,47 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
FunctionBox* funbox = pc->functionBox();
bool parenFreeArrow = false;
TokenStream::Modifier modifier = TokenStream::None;
// Modifier for the following tokens.
// TokenStream::None for the following cases:
// async a => 1
// ^
//
// (a) => 1
// ^
//
// async (a) => 1
// ^
//
// function f(a) {}
// ^
//
// TokenStream::Operand for the following case:
// a => 1
// ^
TokenStream::Modifier firstTokenModifier = TokenStream::None;
// Modifier for the the first token in each argument.
// can be changed to TokenStream::None for the following case:
// async a => 1
// ^
TokenStream::Modifier argModifier = TokenStream::Operand;
if (kind == Arrow) {
TokenKind tt;
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
// In async function, the first token after `async` is already gotten
// with TokenStream::None.
// In sync function, the first token is already gotten with
// TokenStream::Operand.
firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand;
if (!tokenStream.peekToken(&tt, firstTokenModifier))
return false;
if (tt == TOK_NAME || tt == TOK_YIELD)
if (tt == TOK_NAME || tt == TOK_YIELD) {
parenFreeArrow = true;
else
modifier = TokenStream::Operand;
argModifier = firstTokenModifier;
}
}
if (!parenFreeArrow) {
TokenKind tt;
if (!tokenStream.getToken(&tt, modifier))
if (!tokenStream.getToken(&tt, firstTokenModifier))
return false;
if (tt != TOK_LP) {
report(ParseError, false, null(),
@ -2677,8 +2741,9 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
}
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
if (!tokenStream.getToken(&tt, argModifier))
return false;
argModifier = TokenStream::Operand;
MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME || tt == TOK_YIELD);
if (tt == TOK_TRIPLEDOT) {
@ -2719,10 +2784,9 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
funbox->hasDestructuringArgs = true;
Node destruct = destructuringDeclarationWithoutYield(
Node destruct = destructuringDeclarationWithoutYieldOrAwait(
DeclarationKind::FormalParameter,
yieldHandling, tt,
JSMSG_YIELD_IN_DEFAULT);
yieldHandling, tt);
if (!destruct)
return false;
@ -2737,6 +2801,15 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
if (parenFreeArrow)
funbox->setStart(tokenStream);
if (funbox->isAsync() && tokenStream.currentName() == context->names().await) {
// `await` is already gotten as TOK_NAME for the following
// case:
//
// async await => 1
report(ParseError, false, null(), JSMSG_RESERVED_ID, "await");
return false;
}
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
if (!name)
return false;
@ -2791,7 +2864,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
}
funbox->hasParameterExprs = true;
Node def_expr = assignExprWithoutYield(yieldHandling, JSMSG_YIELD_IN_DEFAULT);
Node def_expr = assignExprWithoutYieldOrAwait(yieldHandling);
if (!def_expr)
return false;
if (!handler.setLastFunctionFormalParameterDefault(funcpn, def_expr))
@ -2921,7 +2994,7 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKin
RootedFunction fun(context, handler.nextLazyInnerFunction());
MOZ_ASSERT(!fun->isLegacyGenerator());
FunctionBox* funbox = newFunctionBox(pn, fun, Directives(/* strict = */ false),
fun->generatorKind(), tryAnnexB);
fun->generatorKind(), fun->asyncKind(), tryAnnexB);
if (!funbox)
return false;
@ -3029,9 +3102,11 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yieldHandling,
HandleAtom funName, FunctionSyntaxKind kind,
GeneratorKind generatorKind, InvokedPrediction invoked)
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
InvokedPrediction invoked)
{
MOZ_ASSERT_IF(kind == Statement, funName);
MOZ_ASSERT_IF(asyncKind == AsyncFunction, generatorKind == StarGenerator);
Node pn = handler.newFunctionDefinition();
if (!pn)
@ -3064,7 +3139,7 @@ Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yi
if (!proto)
return null();
}
RootedFunction fun(context, newFunction(funName, kind, generatorKind, proto));
RootedFunction fun(context, newFunction(funName, kind, generatorKind, asyncKind, proto));
if (!fun)
return null();
@ -3083,7 +3158,7 @@ Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yi
// "use foo" directives.
while (true) {
if (trySyntaxParseInnerFunction(pn, fun, inHandling, yieldHandling, kind, generatorKind,
tryAnnexB, directives, &newDirectives))
asyncKind, tryAnnexB, directives, &newDirectives))
{
break;
}
@ -3114,6 +3189,7 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct
YieldHandling yieldHandling,
FunctionSyntaxKind kind,
GeneratorKind generatorKind,
FunctionAsyncKind asyncKind,
bool tryAnnexB,
Directives inheritedDirectives,
Directives* newDirectives)
@ -3143,7 +3219,7 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct
// still expects a FunctionBox to be attached to it during BCE, and
// the syntax parser cannot attach one to it.
FunctionBox* funbox = newFunctionBox(pn, fun, inheritedDirectives, generatorKind,
tryAnnexB);
asyncKind, tryAnnexB);
if (!funbox)
return false;
funbox->initWithEnclosingParseContext(pc, kind);
@ -3175,8 +3251,8 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct
} while (false);
// We failed to do a syntax parse above, so do the full parse.
return innerFunction(pn, pc, fun, inHandling, yieldHandling, kind, generatorKind, tryAnnexB,
inheritedDirectives, newDirectives);
return innerFunction(pn, pc, fun, inHandling, yieldHandling, kind, generatorKind, asyncKind,
tryAnnexB, inheritedDirectives, newDirectives);
}
template <>
@ -3186,13 +3262,14 @@ Parser<SyntaxParseHandler>::trySyntaxParseInnerFunction(Node pn, HandleFunction
YieldHandling yieldHandling,
FunctionSyntaxKind kind,
GeneratorKind generatorKind,
FunctionAsyncKind asyncKind,
bool tryAnnexB,
Directives inheritedDirectives,
Directives* newDirectives)
{
// This is already a syntax parser, so just parse the inner function.
return innerFunction(pn, pc, fun, inHandling, yieldHandling, kind, generatorKind, tryAnnexB,
inheritedDirectives, newDirectives);
return innerFunction(pn, pc, fun, inHandling, yieldHandling, kind, generatorKind, asyncKind,
tryAnnexB, inheritedDirectives, newDirectives);
}
template <typename ParseHandler>
@ -3222,16 +3299,18 @@ template <typename ParseHandler>
bool
Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind, GeneratorKind generatorKind,
bool tryAnnexB, Directives inheritedDirectives,
Directives* newDirectives)
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives)
{
// Note that it is possible for outerpc != this->pc, as we may be
// attempting to syntax parse an inner function from an outer full
// parser. In that case, outerpc is a ParseContext from the full parser
// instead of the current top of the stack of the syntax parser.
FunctionBox* funbox = newFunctionBox(pn, fun, inheritedDirectives, generatorKind, tryAnnexB);
FunctionBox* funbox = newFunctionBox(pn, fun, inheritedDirectives, generatorKind,
asyncKind, tryAnnexB);
if (!funbox)
return false;
funbox->initWithEnclosingParseContext(outerpc, kind);
@ -3262,7 +3341,8 @@ Parser<ParseHandler>::appendToCallSiteObj(Node callSiteObj)
template <>
ParseNode*
Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict,
GeneratorKind generatorKind)
GeneratorKind generatorKind,
FunctionAsyncKind asyncKind)
{
MOZ_ASSERT(checkOptionsCalled);
@ -3271,7 +3351,7 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
return null();
Directives directives(strict);
FunctionBox* funbox = newFunctionBox(pn, fun, directives, generatorKind,
FunctionBox* funbox = newFunctionBox(pn, fun, directives, generatorKind, asyncKind,
/* tryAnnexB = */ false);
if (!funbox)
return null();
@ -3284,15 +3364,15 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
// Our tokenStream has no current token, so pn's position is garbage.
// Substitute the position of the first token in our source. If the function
// is an arrow, use TokenStream::Operand to keep verifyConsistentModifier
// from complaining (we will use TokenStream::Operand in functionArguments).
if (!tokenStream.peekTokenPos(&pn->pn_pos,
fun->isArrow() ? TokenStream::Operand : TokenStream::None))
{
// is a not-async arrow, use TokenStream::Operand to keep
// verifyConsistentModifier from complaining (we will use
// TokenStream::Operand in functionArguments).
TokenStream::Modifier modifier = (fun->isArrow() && asyncKind == SyncFunction)
? TokenStream::Operand : TokenStream::None;
if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier))
return null();
}
YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
FunctionSyntaxKind syntaxKind = Statement;
if (fun->isClassConstructor())
syntaxKind = ClassConstructor;
@ -3329,6 +3409,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
FunctionBox* funbox = pc->functionBox();
RootedFunction fun(context, funbox->function());
AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, funbox->isAsync());
if (!functionArguments(yieldHandling, kind, pn))
return false;
@ -3357,7 +3438,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return false;
if (tt != TOK_LC) {
if (funbox->isStarGenerator() || kind == Method ||
if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method ||
kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
IsConstructorKind(kind)) {
report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
@ -3387,9 +3468,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
// |yield| in the parameters is either a name or keyword, depending on
// whether the arrow function is enclosed in a generator function or not.
// Whereas the |yield| in the function body is always parsed as a name.
YieldHandling bodyYieldHandling = pc->isGenerator() ? YieldIsKeyword : YieldIsName;
MOZ_ASSERT_IF(yieldHandling != bodyYieldHandling, kind == Arrow);
YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind());
Node body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
if (!body)
return false;
@ -3435,7 +3514,8 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling)
Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling,
FunctionAsyncKind asyncKind)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
@ -3455,12 +3535,16 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
}
RootedPropertyName name(context);
GeneratorKind generatorKind = NotGenerator;
GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
if (tt == TOK_MUL) {
if (asyncKind != SyncFunction) {
report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
return null();
}
generatorKind = StarGenerator;
if (!tokenStream.getToken(&tt))
return null();
@ -3479,9 +3563,9 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
return null();
}
YieldHandling newYieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind);
Node fun = functionDefinition(InAllowed, newYieldHandling, name, Statement, generatorKind,
PredictUninvoked);
asyncKind, PredictUninvoked);
if (!fun)
return null();
@ -3498,22 +3582,27 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionExpr(InvokedPrediction invoked)
Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind asyncKind)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
GeneratorKind generatorKind = NotGenerator;
AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction);
GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
if (tt == TOK_MUL) {
if (asyncKind != SyncFunction) {
report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
return null();
}
generatorKind = StarGenerator;
if (!tokenStream.getToken(&tt))
return null();
}
YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
RootedPropertyName name(context);
if (tt == TOK_NAME || tt == TOK_YIELD) {
@ -3524,7 +3613,8 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked)
tokenStream.ungetToken();
}
return functionDefinition(InAllowed, yieldHandling, name, Expression, generatorKind, invoked);
return functionDefinition(InAllowed, yieldHandling, name, Expression, generatorKind,
asyncKind, invoked);
}
/*
@ -4138,16 +4228,22 @@ Parser<ParseHandler>::destructuringDeclaration(DeclarationKind kind, YieldHandli
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::destructuringDeclarationWithoutYield(DeclarationKind kind,
YieldHandling yieldHandling,
TokenKind tt, unsigned msg)
Parser<ParseHandler>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind,
YieldHandling yieldHandling,
TokenKind tt)
{
uint32_t startYieldOffset = pc->lastYieldOffset;
uint32_t startAwaitOffset = pc->lastAwaitOffset;
Node res = destructuringDeclaration(kind, yieldHandling, tt);
if (res && pc->lastYieldOffset != startYieldOffset) {
reportWithOffset(ParseError, false, pc->lastYieldOffset,
msg, js_yield_str);
return null();
if (res) {
if (pc->lastYieldOffset != startYieldOffset) {
reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
return null();
}
if (pc->lastAwaitOffset != startAwaitOffset) {
reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
return null();
}
}
return res;
}
@ -5009,6 +5105,20 @@ Parser<FullParseHandler>::exportDeclaration()
return null();
break;
default: {
if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_FUNCTION) {
tokenStream.consumeKnownToken(nextSameLine);
kid = functionStmt(YieldIsName, AllowDefaultName, AsyncFunction);
if (!kid)
return null();
break;
}
}
tokenStream.ungetToken();
RootedPropertyName name(context, context->names().starDefaultStar);
nameNode = newName(name);
@ -5844,6 +5954,16 @@ Parser<ParseHandler>::newYieldExpression(uint32_t begin, typename ParseHandler::
return handler.newYieldExpression(begin, expr, generator);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::newAwaitExpression(uint32_t begin, typename ParseHandler::Node expr)
{
Node generator = newDotGeneratorName();
if (!generator)
return null();
return handler.newAwaitExpression(begin, expr, generator);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::yieldExpression(InHandling inHandling)
@ -6356,6 +6476,7 @@ JSOpFromPropertyType(PropertyType propType)
case PropertyType::Normal:
case PropertyType::Method:
case PropertyType::GeneratorMethod:
case PropertyType::AsyncMethod:
case PropertyType::Constructor:
case PropertyType::DerivedConstructor:
return JSOP_INITPROP;
@ -6377,8 +6498,8 @@ FunctionSyntaxKindFromPropertyType(PropertyType propType)
case PropertyType::SetterNoExpressionClosure:
return SetterNoExpressionClosure;
case PropertyType::Method:
return Method;
case PropertyType::GeneratorMethod:
case PropertyType::AsyncMethod:
return Method;
case PropertyType::Constructor:
return ClassConstructor;
@ -6392,7 +6513,19 @@ FunctionSyntaxKindFromPropertyType(PropertyType propType)
static GeneratorKind
GeneratorKindFromPropertyType(PropertyType propType)
{
return propType == PropertyType::GeneratorMethod ? StarGenerator : NotGenerator;
if (propType == PropertyType::GeneratorMethod)
return StarGenerator;
if (propType == PropertyType::AsyncMethod)
return StarGenerator;
return NotGenerator;
}
static FunctionAsyncKind
AsyncKindFromPropertyType(PropertyType propType)
{
if (propType == PropertyType::AsyncMethod)
return AsyncFunction;
return SyncFunction;
}
template <typename ParseHandler>
@ -6506,6 +6639,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (propType != PropertyType::Getter && propType != PropertyType::Setter &&
propType != PropertyType::Method && propType != PropertyType::GeneratorMethod &&
propType != PropertyType::AsyncMethod &&
propType != PropertyType::Constructor && propType != PropertyType::DerivedConstructor)
{
report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF);
@ -6938,6 +7072,16 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
}
if (tokenStream.currentName() == context->names().async) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_FUNCTION) {
tokenStream.consumeKnownToken(TOK_FUNCTION);
return functionStmt(yieldHandling, NameRequired, AsyncFunction);
}
}
if (next == TOK_COLON)
return labeledStatement(yieldHandling);
@ -7428,6 +7572,16 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (tt == TOK_YIELD && yieldExpressionsSupported())
return yieldExpression(inHandling);
bool maybeAsyncArrow = false;
if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_NAME || nextSameLine == TOK_YIELD)
maybeAsyncArrow = true;
}
tokenStream.ungetToken();
// Save the tokenizer state in case we find an arrow function and have to
@ -7436,9 +7590,35 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
tokenStream.tell(&start);
PossibleError possibleErrorInner(*this);
Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
if (!lhs)
return null();
Node lhs;
if (maybeAsyncArrow) {
tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
MOZ_ASSERT(tokenStream.currentName() == context->names().async);
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
MOZ_ASSERT(tt == TOK_NAME || tt == TOK_YIELD);
// Check yield validity here.
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
if (!name)
return null();
if (!tokenStream.getToken(&tt))
return null();
if (tt != TOK_ARROW) {
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
"'=>' after argument list", TokenKindToDesc(tt));
return null();
}
} else {
lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
if (!lhs) {
return null();
}
}
ParseNodeKind kind;
JSOp op;
@ -7480,12 +7660,33 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
tokenStream.seek(start);
TokenKind ignored;
if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
if (!tokenStream.peekToken(&next, TokenStream::Operand))
return null();
GeneratorKind generatorKind = NotGenerator;
FunctionAsyncKind asyncKind = SyncFunction;
if (next == TOK_NAME) {
tokenStream.consumeKnownToken(next, TokenStream::Operand);
if (tokenStream.currentName() == context->names().async) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_ARROW) {
tokenStream.ungetToken();
} else {
generatorKind = StarGenerator;
asyncKind = AsyncFunction;
}
} else {
tokenStream.ungetToken();
}
}
Node arrowFunc = functionDefinition(inHandling, yieldHandling, nullptr,
Arrow, NotGenerator);
Arrow, generatorKind, asyncKind);
if (!arrowFunc)
return null();
@ -7514,6 +7715,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
// has block body. An arrow function not ending in such, ends in
// another AssignmentExpression that we can inductively assume was
// peeked consistently.
TokenKind ignored;
if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
return null();
tokenStream.addModifierException(TokenStream::NoneIsOperand);
@ -7754,6 +7956,21 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t
return handler.newDelete(begin, expr);
}
case TOK_AWAIT: {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine, TokenStream::Operand))
return null();
if (nextSameLine != TOK_EOL) {
Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
if (!kid)
return null();
pc->lastAwaitOffset = begin;
return newAwaitExpression(begin, kid);
}
report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_AWAIT);
return null();
}
default: {
Node pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
possibleError, invoked);
@ -7811,13 +8028,13 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
return null();
RootedFunction fun(context, newFunction(/* atom = */ nullptr, Expression,
StarGenerator, proto));
StarGenerator, SyncFunction, proto));
if (!fun)
return null();
// Create box for fun->object early to root it.
Directives directives(/* strict = */ outerpc->sc()->strict());
FunctionBox* genFunbox = newFunctionBox(genfn, fun, directives, StarGenerator,
FunctionBox* genFunbox = newFunctionBox(genfn, fun, directives, StarGenerator, SyncFunction,
/* tryAnnexB = */ false);
if (!genFunbox)
return null();
@ -8083,14 +8300,20 @@ Parser<ParseHandler>::generatorComprehension(uint32_t begin)
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::assignExprWithoutYield(YieldHandling yieldHandling, unsigned msg)
Parser<ParseHandler>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling)
{
uint32_t startYieldOffset = pc->lastYieldOffset;
uint32_t startAwaitOffset = pc->lastAwaitOffset;
Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
if (res && pc->lastYieldOffset != startYieldOffset) {
reportWithOffset(ParseError, false, pc->lastYieldOffset,
msg, js_yield_str);
return null();
if (res) {
if (pc->lastYieldOffset != startYieldOffset) {
reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
return null();
}
if (pc->lastAwaitOffset != startAwaitOffset) {
reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
return null();
}
}
return res;
}
@ -8664,12 +8887,31 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC");
bool isGenerator = false;
bool isAsync = false;
if (ltok == TOK_MUL) {
isGenerator = true;
if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
return null();
}
if (ltok == TOK_NAME && tokenStream.currentName() == context->names().async) {
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
return null();
if (tt != TOK_LP && tt != TOK_COLON && tt != TOK_RC && tt != TOK_ASSIGN) {
isAsync = true;
ltok = tt;
} else {
tokenStream.ungetToken();
tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
}
}
if (isAsync && isGenerator) {
report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
return null();
}
propAtom.set(nullptr);
Node propName;
switch (ltok) {
@ -8691,7 +8933,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
case TOK_NAME: {
propAtom.set(tokenStream.currentName());
// Do not look for accessor syntax on generators
if (isGenerator ||
if (isGenerator || isAsync ||
!(propAtom.get() == context->names().get ||
propAtom.get() == context->names().set))
{
@ -8810,7 +9052,12 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
if (tt == TOK_LP) {
tokenStream.ungetToken();
*propType = isGenerator ? PropertyType::GeneratorMethod : PropertyType::Method;
if (isGenerator)
*propType = PropertyType::GeneratorMethod;
else if (isAsync)
*propType = PropertyType::AsyncMethod;
else
*propType = PropertyType::Method;
return propName;
}
@ -9042,8 +9289,9 @@ Parser<ParseHandler>::methodDefinition(PropertyType propType, HandleAtom funName
{
FunctionSyntaxKind kind = FunctionSyntaxKindFromPropertyType(propType);
GeneratorKind generatorKind = GeneratorKindFromPropertyType(propType);
YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
return functionDefinition(InAllowed, yieldHandling, funName, kind, generatorKind);
FunctionAsyncKind asyncKind = AsyncKindFromPropertyType(propType);
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
return functionDefinition(InAllowed, yieldHandling, funName, kind, generatorKind, asyncKind);
}
template <typename ParseHandler>
@ -9166,6 +9414,17 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
case TOK_YIELD:
case TOK_NAME: {
if (tokenStream.currentName() == context->names().async) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_FUNCTION) {
tokenStream.consumeKnownToken(TOK_FUNCTION);
return functionExpr(PredictUninvoked, AsyncFunction);
}
}
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
if (!name)
return null();

Просмотреть файл

@ -315,6 +315,11 @@ class ParseContext : public Nestable<ParseContext>
static const uint32_t NoYieldOffset = UINT32_MAX;
uint32_t lastYieldOffset;
// lastAwaitOffset stores the offset of the last await that was parsed.
// NoAwaitOffset is its initial value.
static const uint32_t NoAwaitOffset = UINT32_MAX;
uint32_t lastAwaitOffset;
// All inner functions in this context. Only used when syntax parsing.
Rooted<GCVector<JSFunction*, 8>> innerFunctionsForLazy;
@ -358,6 +363,7 @@ class ParseContext : public Nestable<ParseContext>
isStandaloneFunctionBody_(false),
superScopeNeedsHomeObject_(false),
lastYieldOffset(NoYieldOffset),
lastAwaitOffset(NoAwaitOffset),
innerFunctionsForLazy(prs->context, GCVector<JSFunction*, 8>(prs->context)),
newDirectives(newDirectives),
funHasReturnExpr(false),
@ -498,6 +504,14 @@ class ParseContext : public Nestable<ParseContext>
return generatorKind() == StarGenerator;
}
bool isAsync() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync();
}
FunctionAsyncKind asyncKind() const {
return isAsync() ? AsyncFunction : SyncFunction;
}
bool isArrowFunction() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isArrow();
}
@ -553,6 +567,7 @@ enum class PropertyType {
SetterNoExpressionClosure,
Method,
GeneratorMethod,
AsyncMethod,
Constructor,
DerivedConstructor
};
@ -940,13 +955,15 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
*/
ObjectBox* newObjectBox(JSObject* obj);
FunctionBox* newFunctionBox(Node fn, JSFunction* fun, Directives directives,
GeneratorKind generatorKind, bool tryAnnexB);
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB);
/*
* Create a new function object given a name (which is optional if this is
* a function expression).
*/
JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind, GeneratorKind generatorKind,
JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
HandleObject proto);
void trace(JSTracer* trc);
@ -979,6 +996,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
inline Node newName(PropertyName* name);
inline Node newName(PropertyName* name, TokenPos pos);
inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false);
inline Node newAwaitExpression(uint32_t begin, Node expr);
inline bool abortIfSyntaxParser();
@ -1005,12 +1023,14 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// Parse a function, given only its body. Used for the Function and
// Generator constructors.
Node standaloneFunctionBody(HandleFunction fun, HandleScope enclosingScope,
Handle<PropertyNameVector> formals, GeneratorKind generatorKind,
Handle<PropertyNameVector> formals,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
Directives inheritedDirectives, Directives* newDirectives);
// Parse a function, given only its arguments and body. Used for lazily
// parsed functions.
Node standaloneLazyFunction(HandleFunction fun, bool strict, GeneratorKind generatorKind);
Node standaloneLazyFunction(HandleFunction fun, bool strict,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
// Parse an inner function given an enclosing ParseContext and a
// FunctionBox for the inner function.
@ -1027,7 +1047,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// whether it's prohibited due to strictness, JS version, or occurrence
// inside a star generator.
bool yieldExpressionsSupported() {
return versionNumber() >= JSVERSION_1_7 || pc->isGenerator();
return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync();
}
// Match the current token against the BindingIdentifier production with
@ -1066,8 +1086,10 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
* Some parsers have two versions: an always-inlined version (with an 'i'
* suffix) and a never-inlined version (with an 'n' suffix).
*/
Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling);
Node functionExpr(InvokedPrediction invoked = PredictUninvoked);
Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling,
FunctionAsyncKind asyncKind = SyncFunction);
Node functionExpr(InvokedPrediction invoked = PredictUninvoked,
FunctionAsyncKind asyncKind = SyncFunction);
Node statementList(YieldHandling yieldHandling);
@ -1172,7 +1194,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr,
InvokedPrediction invoked = PredictUninvoked);
Node assignExprWithoutYield(YieldHandling yieldHandling, unsigned err);
Node assignExprWithoutYieldOrAwait(YieldHandling yieldHandling);
Node yieldExpression(InHandling inHandling);
Node condExpr1(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling,
@ -1207,7 +1229,8 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
Node funcpn);
Node functionDefinition(InHandling inHandling, YieldHandling yieldHandling, HandleAtom name,
FunctionSyntaxKind kind, GeneratorKind generatorKind,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
InvokedPrediction invoked = PredictUninvoked);
// Parse a function body. Pass StatementListBody if the body is a list of
@ -1232,8 +1255,8 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread);
Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
TokenKind tt);
Node destructuringDeclarationWithoutYield(DeclarationKind kind, YieldHandling yieldHandling,
TokenKind tt, unsigned msg);
Node destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, YieldHandling yieldHandling,
TokenKind tt);
bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
bool checkExportedName(JSAtom* exportName);
@ -1298,11 +1321,13 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, bool tryAnnexB);
bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind, GeneratorKind generatorKind, bool tryAnnexB,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives);
bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, InHandling inHandling,
YieldHandling yieldHandling, FunctionSyntaxKind kind,
GeneratorKind generatorKind, bool tryAnnexB,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives);
bool finishFunctionScopes();
bool finishFunction();

Просмотреть файл

@ -453,6 +453,8 @@ class FunctionBox : public ObjectBox, public SharedContext
uint16_t length;
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
uint8_t asyncKindBits_; /* The FunctionAsyncKing of this function. */
bool isGenexpLambda:1; /* lambda from generator expression */
bool hasDestructuringArgs:1; /* parameter list contains destructuring expression */
bool hasParameterExprs:1; /* parameter list contains expressions */
@ -473,7 +475,8 @@ class FunctionBox : public ObjectBox, public SharedContext
FunctionContextFlags funCxFlags;
FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, JSFunction* fun,
Directives directives, bool extraWarnings, GeneratorKind generatorKind);
Directives directives, bool extraWarnings, GeneratorKind generatorKind,
FunctionAsyncKind asyncKind);
MutableHandle<LexicalScope::Data*> namedLambdaBindings() {
MOZ_ASSERT(context->compartment()->runtimeFromAnyThread()->keepAtoms());
@ -532,6 +535,8 @@ class FunctionBox : public ObjectBox, public SharedContext
bool isGenerator() const { return generatorKind() != NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
FunctionAsyncKind asyncKind() const { return AsyncKindFromBits(asyncKindBits_); }
bool isAsync() const { return asyncKind() == AsyncFunction; }
bool isArrow() const { return function()->isArrow(); }
void setGeneratorKind(GeneratorKind kind) {

Просмотреть файл

@ -289,6 +289,7 @@ class SyntaxParseHandler
MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
Node newAwaitExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
// Statements

Просмотреть файл

@ -107,6 +107,7 @@
macro(THROW, "keyword 'throw'") \
macro(DEBUGGER, "keyword 'debugger'") \
macro(YIELD, "keyword 'yield'") \
macro(AWAIT, "keyword 'await'") \
macro(EXPORT, "keyword 'export'") \
macro(IMPORT, "keyword 'import'") \
macro(CLASS, "keyword 'class'") \

Просмотреть файл

@ -965,6 +965,12 @@ TokenStream::putIdentInTokenbuf(const char16_t* identStart)
bool
TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
{
if (!awaitIsKeyword && kw->tokentype == TOK_AWAIT) {
if (ttp)
*ttp = TOK_NAME;
return true;
}
if (kw->tokentype == TOK_RESERVED)
return reportError(JSMSG_RESERVED_ID, kw->chars);

Просмотреть файл

@ -32,6 +32,8 @@ struct KeywordInfo;
namespace js {
namespace frontend {
class AutoAwaitIsKeyword;
struct TokenPos {
uint32_t begin; // Offset of the token's first char.
uint32_t end; // Offset of 1 past the token's last char.
@ -430,6 +432,9 @@ class MOZ_STACK_CLASS TokenStream
{}
};
bool awaitIsKeyword = false;
friend class AutoAwaitIsKeyword;
public:
typedef Token::Modifier Modifier;
static constexpr Modifier None = Token::None;
@ -1015,6 +1020,25 @@ class MOZ_STACK_CLASS TokenStream
StrictModeGetter* strictModeGetter; // used to test for strict mode
};
class MOZ_STACK_CLASS AutoAwaitIsKeyword
{
private:
TokenStream* ts_;
bool oldAwaitIsKeyword_;
public:
AutoAwaitIsKeyword(TokenStream* ts, bool awaitIsKeyword) {
ts_ = ts;
oldAwaitIsKeyword_ = ts_->awaitIsKeyword;
ts_->awaitIsKeyword = awaitIsKeyword;
}
~AutoAwaitIsKeyword() {
ts_->awaitIsKeyword = oldAwaitIsKeyword_;
ts_ = nullptr;
}
};
extern const char*
TokenKindToDesc(TokenKind tt);

Просмотреть файл

@ -1181,7 +1181,7 @@ function test_syntax(postfixes, check_error, ignore_opts) {
test("for each (let x in y ");
test("for each (let x in y) ");
// asm.js
// ==== asm.js ====
test("(function() { 'use asm'; ");
test("(function() { 'use asm'; var ");
@ -1208,4 +1208,149 @@ function test_syntax(postfixes, check_error, ignore_opts) {
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; } ");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; }) ");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; }); ");
// ==== async/await ====
// async/await function decralation
test("async ");
test("async function ");
test("async function A ");
test("async function A( ");
test("async function A() ");
test("async function A(a ");
test("async function A(a) ");
test("async function A(a) { ");
test("async function A(a) {} ");
test("async function A(a) { await ");
test("async function A(a) { await X ");
test("async function A(a) { await X; ");
test("async function A(a) { await X; } ");
test("async function A(a) { await await ");
test("async function A(a) { await await await ");
test("async function A(a) { await await await X ");
test("async function A(a) { await await await X; ");
test("async function A(a) { await await await X; } ");
opts = { no_fun: true, no_eval: true, module: true };
test("export default async ", opts);
test("export default async function ", opts);
test("export default async function ( ", opts);
test("export default async function () ", opts);
test("export default async function (a ", opts);
test("export default async function (a) ", opts);
test("export default async function (a) { ", opts);
test("export default async function (a) {} ", opts);
test("export default async function (a) { await ", opts);
test("export default async function (a) { await X ", opts);
test("export default async function (a) { await X; ", opts);
test("export default async function (a) { await X; } ", opts);
// async/await function expression
test("(async ");
test("(async function ");
test("(async function A ");
test("(async function A( ");
test("(async function A() ");
test("(async function A(a ");
test("(async function A(a) ");
test("(async function A(a) { ");
test("(async function A(a) {} ");
test("(async function A(a) { await ");
test("(async function A(a) { await X ");
test("(async function A(a) { await X; ");
test("(async function A(a) { await X; } ");
test("(async function A(a) { await X; }) ");
test("(async function ( ");
test("(async function () ");
test("(async function (a ");
test("(async function (a) ");
test("(async function (a) { ");
test("(async function (a) {} ");
test("(async function (a) { await ");
test("(async function (a) { await X ");
test("(async function (a) { await X; ");
test("(async function (a) { await X; } ");
test("(async function (a) { await X; }) ");
// async/await method
test("({ async ");
test("({ async m ");
test("({ async m( ");
test("({ async m() ");
test("({ async m() { ");
test("({ async m() {} ");
test("({ async m() {}, ");
test("class X { async ");
test("class X { async m ");
test("class X { async m( ");
test("class X { async m() ");
test("class X { async m() { ");
test("class X { async m() {} ");
test("class X { static async ");
test("class X { static async m ");
test("class X { static async m( ");
test("class X { static async m() ");
test("class X { static async m() { ");
test("class X { static async m() {} ");
// async/await arrow
test("(async a ");
test("(async a => ");
test("(async a => b ");
test("(async a => b) ");
test("(async a => { ");
test("(async a => { b ");
test("(async a => { b } ");
test("(async a => { b }) ");
test("(async ( ");
test("(async (a ");
test("(async (a) ");
test("(async (a) => ");
test("(async (a) => b ");
test("(async (a) => b) ");
test("(async (a, ");
test("(async (a, b ");
test("(async (a, b) ");
test("(async (a, b) => ");
test("(async (a, b) => b ");
test("(async (a, b) => b) ");
test("(async ([ ");
test("(async ([a ");
test("(async ([a] ");
test("(async ([a]) ");
test("(async ([a]) => ");
test("(async ([a]) => b ");
test("(async ([a]) => b) ");
test("(async ([a, ");
test("(async ([a, b ");
test("(async ([a, b] ");
test("(async ([a, b]) ");
test("(async ([a, b]) => ");
test("(async ([a, b]) => b ");
test("(async ([a, b]) => b) ");
test("(async ({ ");
test("(async ({a ");
test("(async ({a} ");
test("(async ({a}) ");
test("(async ({a}) => ");
test("(async ({a}) => b ");
test("(async ({a}) => b) ");
test("(async ({a, ");
test("(async ({a, b ");
test("(async ({a, b} ");
test("(async ({a, b}) ");
test("(async ({a, b}) => ");
test("(async ({a, b}) => b ");
test("(async ({a, b}) => b) ");
}

Просмотреть файл

@ -2750,15 +2750,14 @@ static const VMFunction DefFunOperationInfo =
bool
BaselineCompiler::emit_JSOP_DEFFUN()
{
RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
frame.syncStack(0);
masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
frame.popRegsAndSync(1);
masm.unboxObject(R0, R0.scratchReg());
masm.loadPtr(frame.addressOfEnvironmentChain(), R1.scratchReg());
prepareVMCall();
pushArg(ImmGCPtr(fun));
pushArg(R0.scratchReg());
pushArg(R1.scratchReg());
pushArg(ImmGCPtr(script));
return callVM(DefFunOperationInfo);

Просмотреть файл

@ -2434,7 +2434,8 @@ CodeGenerator::visitLambda(LLambda* lir)
emitLambdaInit(output, envChain, info);
if (info.flags & JSFunction::EXTENDED) {
MOZ_ASSERT(info.fun->allowSuperProperty() || info.fun->isSelfHostedBuiltin());
MOZ_ASSERT(info.fun->allowSuperProperty() || info.fun->isSelfHostedBuiltin() ||
info.fun->isAsync());
static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
@ -4694,7 +4695,8 @@ CodeGenerator::visitDefFun(LDefFun* lir)
{
Register envChain = ToRegister(lir->environmentChain());
pushArg(ImmGCPtr(lir->mir()->fun()));
Register fun = ToRegister(lir->fun());
pushArg(fun);
pushArg(envChain);
pushArg(ImmGCPtr(current->mir()->info().script()));

Просмотреть файл

@ -13442,13 +13442,9 @@ IonBuilder::jsop_deflexical(uint32_t index)
bool
IonBuilder::jsop_deffun(uint32_t index)
{
JSFunction* fun = script()->getFunction(index);
if (IsAsmJSModule(fun))
return abort("asm.js module function");
MOZ_ASSERT(analysis().usesEnvironmentChain());
MDefFun* deffun = MDefFun::New(alloc(), fun, current->environmentChain());
MDefFun* deffun = MDefFun::New(alloc(), current->pop(), current->environmentChain());
current->add(deffun);
return resumeAfter(deffun);

Просмотреть файл

@ -207,7 +207,11 @@ LIRGenerator::visitDefLexical(MDefLexical* ins)
void
LIRGenerator::visitDefFun(MDefFun* ins)
{
LDefFun* lir = new(alloc()) LDefFun(useRegisterAtStart(ins->environmentChain()));
MDefinition* fun = ins->fun();
MOZ_ASSERT(fun->type() == MIRType::Object);
LDefFun* lir = new(alloc()) LDefFun(useRegisterAtStart(fun),
useRegisterAtStart(ins->environmentChain()));
add(lir, ins);
assignSafepoint(lir, ins);
}

Просмотреть файл

@ -8106,31 +8106,22 @@ class MDefLexical
};
class MDefFun
: public MUnaryInstruction,
public NoTypePolicy::Data
: public MBinaryInstruction,
public ObjectPolicy<0>::Data
{
CompilerFunction fun_;
private:
MDefFun(JSFunction* fun, MDefinition* envChain)
: MUnaryInstruction(envChain),
fun_(fun)
MDefFun(MDefinition* fun, MDefinition* envChain)
: MBinaryInstruction(fun, envChain)
{}
public:
INSTRUCTION_HEADER(DefFun)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, environmentChain))
NAMED_OPERANDS((0, fun), (1, environmentChain))
JSFunction* fun() const {
return fun_;
}
bool possiblyCalls() const override {
return true;
}
bool appendRoots(MRootList& roots) const override {
return roots.append(fun_);
}
};
class MRegExp : public MNullaryInstruction

Просмотреть файл

@ -1550,19 +1550,23 @@ class LDefLexical : public LCallInstructionHelper<0, 0, 0>
}
};
class LDefFun : public LCallInstructionHelper<0, 1, 0>
class LDefFun : public LCallInstructionHelper<0, 2, 0>
{
public:
LIR_HEADER(DefFun)
explicit LDefFun(const LAllocation& envChain)
LDefFun(const LAllocation& fun, const LAllocation& envChain)
{
setOperand(0, envChain);
setOperand(0, fun);
setOperand(1, envChain);
}
const LAllocation* environmentChain() {
const LAllocation* fun() {
return getOperand(0);
}
const LAllocation* environmentChain() {
return getOperand(1);
}
MDefFun* mir() const {
return mir_->toDefFun();
}

Просмотреть файл

@ -181,6 +181,8 @@ MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid array compre
MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initializer too large")
MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after import *")
MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async")
MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression")
MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value")
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
@ -262,6 +264,7 @@ MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found")
MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
MSG_DEF(JSMSG_LEXICAL_DECL_LABEL, 1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled")
MSG_DEF(JSMSG_LINE_BREAK_AFTER_AWAIT, 0, JSEXN_SYNTAXERR, "no line break is allowed after 'await'")
MSG_DEF(JSMSG_GENERATOR_LABEL, 0, JSEXN_SYNTAXERR, "generator functions cannot be labelled")
MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled")
MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")

Просмотреть файл

@ -39,9 +39,11 @@
#include "jit/JitFrameIterator.h"
#include "js/CallNonGenericMethod.h"
#include "js/Proxy.h"
#include "vm/AsyncFunction.h"
#include "vm/Debugger.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/SelfHosting.h"
#include "vm/Shape.h"
#include "vm/SharedImmutableStringsCache.h"
#include "vm/StringBuffer.h"
@ -985,6 +987,11 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen)
if (IsAsmJSFunction(fun))
return AsmJSFunctionToString(cx, fun);
if (IsWrappedAsyncFunction(cx, fun)) {
RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun));
return FunctionToString(cx, unwrapped, lambdaParen);
}
StringBuffer out(cx);
RootedScript script(cx);
@ -1001,6 +1008,11 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen)
}
}
if (fun->isAsync()) {
if (!out.append("async "))
return nullptr;
}
bool funIsMethodOrNonArrowLambda = (fun->isLambda() && !fun->isArrow()) || fun->isMethod() ||
fun->isGetter() || fun->isSetter();
@ -1012,7 +1024,12 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen)
return nullptr;
}
if (!fun->isArrow()) {
if (!(fun->isStarGenerator() ? out.append("function* ") : out.append("function ")))
bool ok;
if (fun->isStarGenerator() && !fun->isAsync())
ok = out.append("function* ");
else
ok = out.append("function ");
if (!ok)
return nullptr;
}
if (fun->name()) {
@ -1658,7 +1675,8 @@ const JSFunctionSpec js::function_methods[] = {
};
static bool
FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind generatorKind)
FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind generatorKind,
FunctionAsyncKind asyncKind)
{
CallArgs args = CallArgsFromVp(argc, vp);
@ -1670,7 +1688,10 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener
}
bool isStarGenerator = generatorKind == StarGenerator;
bool isAsync = asyncKind == AsyncFunction;
MOZ_ASSERT(generatorKind != LegacyGenerator);
MOZ_ASSERT_IF(isAsync, isStarGenerator);
MOZ_ASSERT_IF(!isStarGenerator, !isAsync);
RootedScript maybeScript(cx);
const char* filename;
@ -1681,7 +1702,9 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener
&mutedErrors);
const char* introductionType = "Function";
if (generatorKind != NotGenerator)
if (isAsync)
introductionType = "AsyncFunction";
else if (generatorKind != NotGenerator)
introductionType = "GeneratorFunction";
const char* introducerFilename = filename;
@ -1775,6 +1798,8 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener
RootedAtom anonymousAtom(cx, cx->names().anonymous);
RootedObject proto(cx);
if (isStarGenerator) {
// Unwrapped function of async function should use GeneratorFunction,
// while wrapped function isn't generator.
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, global);
if (!proto)
return false;
@ -1784,10 +1809,11 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener
}
RootedObject globalLexical(cx, &global->lexicalEnvironment());
AllocKind allocKind = isAsync ? AllocKind::FUNCTION_EXTENDED : AllocKind::FUNCTION;
RootedFunction fun(cx, NewFunctionWithProto(cx, nullptr, 0,
JSFunction::INTERPRETED_LAMBDA, globalLexical,
anonymousAtom, proto,
AllocKind::FUNCTION, TenuredObject));
allocKind, TenuredObject));
if (!fun)
return false;
@ -1875,7 +1901,9 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener
: SourceBufferHolder::NoOwnership;
bool ok;
SourceBufferHolder srcBuf(chars.start().get(), chars.length(), ownership);
if (isStarGenerator)
if (isAsync)
ok = frontend::CompileAsyncFunctionBody(cx, &fun, options, formals, srcBuf);
else if (isStarGenerator)
ok = frontend::CompileStarGeneratorBody(cx, &fun, options, formals, srcBuf);
else
ok = frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf);
@ -1886,13 +1914,26 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener
bool
js::Function(JSContext* cx, unsigned argc, Value* vp)
{
return FunctionConstructor(cx, argc, vp, NotGenerator);
return FunctionConstructor(cx, argc, vp, NotGenerator, SyncFunction);
}
bool
js::Generator(JSContext* cx, unsigned argc, Value* vp)
{
return FunctionConstructor(cx, argc, vp, StarGenerator);
return FunctionConstructor(cx, argc, vp, StarGenerator, SyncFunction);
}
bool
js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!FunctionConstructor(cx, argc, vp, StarGenerator, AsyncFunction))
return false;
FixedInvokeArgs<1> args2(cx);
args2[0].set(args.rval());
return CallSelfHostedFunction(cx, cx->names().AsyncFunction_wrap,
NullHandleValue, args2, args.rval());
}
bool

Просмотреть файл

@ -301,6 +301,13 @@ class JSFunction : public js::NativeObject
flags_ |= RESOLVED_NAME;
}
void setAsyncKind(js::FunctionAsyncKind asyncKind) {
if (isInterpretedLazy())
lazyScript()->setAsyncKind(asyncKind);
else
nonLazyScript()->setAsyncKind(asyncKind);
}
bool getUnresolvedLength(JSContext* cx, js::MutableHandleValue v);
JSAtom* getUnresolvedName(JSContext* cx);
@ -469,6 +476,18 @@ class JSFunction : public js::NativeObject
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
js::FunctionAsyncKind asyncKind() const {
return isInterpretedLazy() ? lazyScript()->asyncKind() : nonLazyScript()->asyncKind();
}
bool isAsync() const {
if (isInterpretedLazy())
return lazyScript()->asyncKind() == js::AsyncFunction;
if (hasScript())
return nonLazyScript()->asyncKind() == js::AsyncFunction;
return false;
}
void setScript(JSScript* script_) {
mutableScript() = script_;
}
@ -601,6 +620,9 @@ Function(JSContext* cx, unsigned argc, Value* vp);
extern bool
Generator(JSContext* cx, unsigned argc, Value* vp);
extern bool
AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp);
// Allocate a new function backed by a JSNative. Note that by default this
// creates a singleton object.
extern JSFunction*

Просмотреть файл

@ -2615,6 +2615,7 @@ JSScript::initFromFunctionBox(ExclusiveContext* cx, HandleScript script,
script->isGeneratorExp_ = funbox->isGenexpLambda;
script->setGeneratorKind(funbox->generatorKind());
script->setAsyncKind(funbox->asyncKind());
PositionalFormalParameterIter fi(script);
while (fi && !fi.closedOver())
@ -3104,6 +3105,8 @@ Rebase(JSScript* dst, JSScript* src, T* srcp)
static JSObject*
CloneInnerInterpretedFunction(JSContext* cx, HandleScope enclosingScope, HandleFunction srcFun)
{
/* async function should not appear as inner function. */
MOZ_ASSERT(!srcFun->isAsync());
/* NB: Keep this in sync with XDRInterpretedFunction. */
RootedObject cloneProto(cx);
if (srcFun->isStarGenerator()) {
@ -3272,6 +3275,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
dst->isDerivedClassConstructor_ = src->isDerivedClassConstructor();
dst->needsHomeObject_ = src->needsHomeObject();
dst->isDefaultClassConstructor_ = src->isDefaultClassConstructor();
dst->isAsync_ = src->asyncKind() == AsyncFunction;
if (nconsts != 0) {
GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->consts()->vector);
@ -4004,6 +4008,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
p.version = version;
p.shouldDeclareArguments = false;
p.hasThisBinding = false;
p.isAsync = false;
p.numClosedOverBindings = closedOverBindings.length();
p.numInnerFunctions = innerFunctions.length();
p.generatorKindBits = GeneratorKindAsBits(NotGenerator);

Просмотреть файл

@ -640,6 +640,7 @@ class ScriptSourceObject : public NativeObject
};
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
enum FunctionAsyncKind { SyncFunction, AsyncFunction };
static inline unsigned
GeneratorKindAsBits(GeneratorKind generatorKind) {
@ -652,6 +653,17 @@ GeneratorKindFromBits(unsigned val) {
return static_cast<GeneratorKind>(val);
}
static inline unsigned
AsyncKindAsBits(FunctionAsyncKind asyncKind) {
return static_cast<unsigned>(asyncKind);
}
static inline FunctionAsyncKind
AsyncKindFromBits(unsigned val) {
MOZ_ASSERT(val <= AsyncFunction);
return static_cast<FunctionAsyncKind>(val);
}
/*
* NB: after a successful XDR_DECODE, XDRScript callers must do any required
* subsequent set-up of owning function or script object and then call
@ -988,6 +1000,8 @@ class JSScript : public js::gc::TenuredCell
bool isDerivedClassConstructor_:1;
bool isDefaultClassConstructor_:1;
bool isAsync_:1;
// Add padding so JSScript is gc::Cell aligned. Make padding protected
// instead of private to suppress -Wunused-private-field compiler warnings.
protected:
@ -1276,6 +1290,14 @@ class JSScript : public js::gc::TenuredCell
generatorKindBits_ = GeneratorKindAsBits(kind);
}
js::FunctionAsyncKind asyncKind() const {
return isAsync_ ? js::AsyncFunction : js::SyncFunction;
}
void setAsyncKind(js::FunctionAsyncKind kind) {
isAsync_ = kind == js::AsyncFunction;
}
void setNeedsHomeObject() {
needsHomeObject_ = true;
}
@ -1887,7 +1909,8 @@ class LazyScript : public gc::TenuredCell
uint32_t shouldDeclareArguments : 1;
uint32_t hasThisBinding : 1;
uint32_t numClosedOverBindings : 22;
uint32_t isAsync : 1;
uint32_t numClosedOverBindings : 21;
uint32_t numInnerFunctions : 20;
uint32_t generatorKindBits : 2;
@ -2024,6 +2047,14 @@ class LazyScript : public gc::TenuredCell
p_.generatorKindBits = GeneratorKindAsBits(kind);
}
FunctionAsyncKind asyncKind() const {
return p_.isAsync ? AsyncFunction : SyncFunction;
}
void setAsyncKind(FunctionAsyncKind kind) {
p_.isAsync = kind == AsyncFunction;
}
bool strict() const {
return p_.strict;
}

Просмотреть файл

@ -324,6 +324,7 @@ UNIFIED_SOURCES += [
'threading/Mutex.cpp',
'vm/ArgumentsObject.cpp',
'vm/ArrayBufferObject.cpp',
'vm/AsyncFunction.cpp',
'vm/Caches.cpp',
'vm/CallNonGenericMethod.cpp',
'vm/CharacterEncoding.cpp',
@ -749,6 +750,7 @@ selfhosted.inputs = [
'builtin/SelfHostingDefines.h',
'builtin/Utilities.js',
'builtin/Array.js',
'builtin/AsyncFunctions.js',
'builtin/Classes.js',
'builtin/Date.js',
'builtin/Error.js',

Просмотреть файл

@ -0,0 +1,19 @@
// ObjectDefineProperties with non callable accessor throws.
const descriptors = [
{get: 1}, {set: 1},
{get: []}, {set: []},
{get: {}}, {set: {}},
{get: new Number}, {set: new Number},
{get: 1, set: 1},
{get: [], set: []},
{get: {}, set: {}},
{get: new Number, set: new Number},
];
for (const descriptor of descriptors) {
assertThrowsInstanceOf(() => Object.create(null, {x: descriptor}), TypeError);
assertThrowsInstanceOf(() => Object.defineProperties({}, {x: descriptor}), TypeError);
}
reportCompare(true, true);

Просмотреть файл

@ -149,4 +149,7 @@ testDefault(function* () {}().__proto__.__proto__, "Generator");
// ES6 25.4.5.4 Promise.prototype [ @@toStringTag ]
testDefault(Promise.prototype, "Promise");
// AsyncFunction.prototype [ @@toStringTag ]
testDefault(async function() {}.constructor.prototype, "AsyncFunction");
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,21 @@
var BUGNUMBER = 1185106;
var summary = "Bound names of async functions";
print(BUGNUMBER + ": " + summary);
async function test() {}
assertEq(test.name, "test");
var test2 = (async function test2() {});
assertEq(test2.name, "test2");
var anon = async function() {};
assertEq(anon.name, "");
if (typeof Reflect !== "undefined" && Reflect.parse) {
var tree = Reflect.parse("export default async function() {}", { target: "module" });
assertEq(tree.body[0].declaration.id.name, "*default*");
}
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,51 @@
var BUGNUMBER = 1185106;
var summary = "EarlyErrors for async function";
print(BUGNUMBER + ": " + summary);
function assertThrowsSE(code) {
assertThrows(() => Reflect.parse(code), SyntaxError);
}
if (typeof Reflect !== "undefined" && Reflect.parse) {
// If FormalParameters Contains AwaitExpression is true.
assertThrowsSE("async function a(k = await 3) {}");
assertThrowsSE("(async function(k = await 3) {})");
assertThrowsSE("(async function a(k = await 3) {})");
// If BindingIdentifier is `eval` or `arguments`.
assertThrowsSE("'use strict'; async function eval() {}");
assertThrowsSE("'use strict'; (async function eval() {})");
assertThrowsSE("'use strict'; async function arguments() {}");
assertThrowsSE("'use strict'; (async function arguments() {})");
// If any element of the BoundNames of FormalParameters also occurs in the
// LexicallyDeclaredNames of AsyncFunctionBody.
assertThrowsSE("async function a(x) { let x; }");
assertThrowsSE("(async function(x) { let x; })");
assertThrowsSE("(async function a(x) { let x; })");
// If FormalParameters contains SuperProperty is true.
assertThrowsSE("async function a(k = super.prop) { }");
assertThrowsSE("(async function(k = super.prop) {})");
assertThrowsSE("(async function a(k = super.prop) {})");
// If AsyncFunctionBody contains SuperProperty is true.
assertThrowsSE("async function a() { super.prop(); }");
assertThrowsSE("(async function() { super.prop(); })");
assertThrowsSE("(async function a() { super.prop(); })");
// If FormalParameters contains SuperCall is true.
assertThrowsSE("async function a(k = super()) {}");
assertThrowsSE("(async function(k = super()) {})");
assertThrowsSE("(async function a(k = super()) {})");
// If AsyncFunctionBody contains SuperCall is true.
assertThrowsSE("async function a() { super(); }");
assertThrowsSE("(async function() { super(); })");
assertThrowsSE("(async function a() { super(); })");
}
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,31 @@
// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue
var BUGNUMBER = 1185106;
var summary = "arguments.callee in sloppy mode should return wrapped function";
print(BUGNUMBER + ": " + summary);
async function decl1() {
return arguments.callee;
}
assertEventuallyEq(decl1(), decl1);
var expr1 = async function foo() {
return arguments.callee;
};
assertEventuallyEq(expr1(), expr1);
var expr2 = async function() {
return arguments.callee;
};
assertEventuallyEq(expr2(), expr2);
var obj = {
async method1() {
return arguments.callee;
}
};
assertEventuallyEq(obj.method1(), obj.method1);
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,33 @@
var BUGNUMBER = 1185106;
var summary = "async function constructor and prototype";
print(BUGNUMBER + ": " + summary);
var f1 = async function() {};
var AsyncFunction = f1.constructor;
var AsyncFunctionPrototype = AsyncFunction.prototype;
assertEq(AsyncFunction.name, "AsyncFunction");
assertEq(AsyncFunction.length, 1);
assertEq(Object.getPrototypeOf(async function() {}), AsyncFunctionPrototype);
assertEq(AsyncFunctionPrototype.constructor, AsyncFunction);
var f2 = AsyncFunction("await 1");
assertEq(f2.constructor, AsyncFunction);
assertEq(f2.length, 0);
assertEq(Object.getPrototypeOf(f2), AsyncFunctionPrototype);
var f3 = new AsyncFunction("await 1");
assertEq(f3.constructor, AsyncFunction);
assertEq(f3.length, 0);
assertEq(Object.getPrototypeOf(f3), AsyncFunctionPrototype);
var f4 = AsyncFunction("a", "b", "c", "await 1");
assertEq(f4.constructor, AsyncFunction);
assertEq(f4.length, 3);
assertEq(Object.getPrototypeOf(f4), AsyncFunctionPrototype);
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,14 @@
// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue
var BUGNUMBER = 1185106;
var summary = "Named async function expression should get wrapped function for the name inside it";
print(BUGNUMBER + ": " + summary);
var expr = async function foo() {
return foo;
};
assertEventuallyEq(expr(), expr);
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,12 @@
var BUGNUMBER = 1185106;
var summary = "async function length";
print(BUGNUMBER + ": " + summary);
assertEq(async function() {}.length, 0);
assertEq(async function(a) {}.length, 1);
assertEq(async function(a, b, c) {}.length, 3);
assertEq(async function(a, b, c, ...d) {}.length, 3);
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,61 @@
// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue
var BUGNUMBER = 1185106;
var summary = "async methods semantics";
print(BUGNUMBER + ": " + summary);
class X {
constructor() {
this.value = 42;
}
async getValue() {
return this.value;
}
setValue(value) {
this.value = value;
}
async increment() {
var value = await this.getValue();
this.setValue(value + 1);
return this.getValue();
}
async getBaseClassName() {
return 'X';
}
static async getStaticValue() {
return 44;
}
async 10() {
return 46;
}
async ["foo"]() {
return 47;
}
}
class Y extends X {
async getBaseClassName() {
return super.getBaseClassName();
}
}
var objLiteral = {
async get() {
return 45;
},
someStuff: 5
};
var x = new X();
var y = new Y();
assertEventuallyEq(x.getValue(), 42);
assertEventuallyEq(x.increment(), 43);
assertEventuallyEq(x[10](), 46);
assertEventuallyEq(x.foo(), 47);
assertEventuallyEq(X.getStaticValue(), 44);
assertEventuallyEq(objLiteral.get(), 45);
assertEventuallyEq(y.getBaseClassName(), 'X');
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,39 @@
var BUGNUMBER = 1185106;
var summary = "async name token in property and object destructuring pattern";
print(BUGNUMBER + ": " + summary);
{
let a = { async: 10 };
assertEq(a.async, 10);
}
{
let a = { async() {} };
assertEq(a.async instanceof Function, true);
assertEq(a.async.name, "async");
}
{
let async = 11;
let a = { async };
assertEq(a.async, 11);
}
{
let { async } = { async: 12 };
assertEq(async, 12);
}
{
let { async = 13 } = {};
assertEq(async, 13);
}
{
let { async: a = 14 } = {};
assertEq(a, 14);
}
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,169 @@
// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue
var BUGNUMBER = 1185106;
var summary = "async functions semantics";
print(BUGNUMBER + ": " + summary);
async function empty() {
}
assertEventuallyEq(empty(), undefined);
async function simpleReturn() {
return 1;
}
assertEventuallyEq(simpleReturn(), 1);
async function simpleAwait() {
var result = await 2;
return result;
}
assertEventuallyEq(simpleAwait(), 2);
async function simpleAwaitAsync() {
var result = await simpleReturn();
return 2 + result;
}
assertEventuallyEq(simpleAwaitAsync(), 3);
async function returnOtherAsync() {
return 1 + await simpleAwaitAsync();
}
assertEventuallyEq(returnOtherAsync(), 4);
async function simpleThrower() {
throw new Error();
}
assertEventuallyThrows(simpleThrower(), Error);
async function delegatedThrower() {
var val = await simpleThrower();
return val;
}
async function tryCatch() {
try {
await delegatedThrower();
return 'FAILED';
} catch (_) {
return 5;
}
}
assertEventuallyEq(tryCatch(), 5);
async function tryCatchThrow() {
try {
await delegatedThrower();
return 'FAILED';
} catch (_) {
return delegatedThrower();
}
}
assertEventuallyThrows(tryCatchThrow(), Error);
async function wellFinally() {
try {
await delegatedThrower();
} catch (_) {
return 'FAILED';
} finally {
return 6;
}
}
assertEventuallyEq(wellFinally(), 6);
async function finallyMayFail() {
try {
await delegatedThrower();
} catch (_) {
return 5;
} finally {
return delegatedThrower();
}
}
assertEventuallyThrows(finallyMayFail(), Error);
async function embedded() {
async function inner() {
return 7;
}
return await inner();
}
assertEventuallyEq(embedded(), 7);
// recursion, it works!
async function fib(n) {
return (n == 0 || n == 1) ? n : await fib(n - 1) + await fib(n - 2);
}
assertEventuallyEq(fib(6), 8);
// mutual recursion
async function isOdd(n) {
async function isEven(n) {
return n === 0 || await isOdd(n - 1);
}
return n !== 0 && await isEven(n - 1);
}
assertEventuallyEq(isOdd(12).then(v => v ? "oops" : 12), 12);
// recursion, take three!
var hardcoreFib = async function fib2(n) {
return (n == 0 || n == 1) ? n : await fib2(n - 1) + await fib2(n - 2);
}
assertEventuallyEq(hardcoreFib(7), 13);
var asyncExpr = async function() {
return 10;
}
assertEventuallyEq(asyncExpr(), 10);
var namedAsyncExpr = async function simple() {
return 11;
}
assertEventuallyEq(namedAsyncExpr(), 11);
async function executionOrder() {
var value = 0;
async function first() {
return (value = value === 0 ? 1 : value);
}
async function second() {
return (value = value === 0 ? 2 : value);
}
async function third() {
return (value = value === 0 ? 3 : value);
}
return await first() + await second() + await third() + 6;
}
assertEventuallyEq(executionOrder(), 9);
async function miscellaneous() {
if (arguments.length === 3 &&
arguments.callee.name === "miscellaneous")
return 14;
}
assertEventuallyEq(miscellaneous(1, 2, 3), 14);
function thrower() {
throw 15;
}
async function defaultArgs(arg = thrower()) {
}
assertEventuallyEq(defaultArgs().catch(e => e), 15);
let arrowAwaitExpr = async () => await 2;
assertEventuallyEq(arrowAwaitExpr(), 2);
let arrowAwaitBlock = async () => { return await 2; };
assertEventuallyEq(arrowAwaitBlock(), 2);
// Async functions are not constructible
assertThrows(() => {
async function Person() {
}
new Person();
}, TypeError);
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,26 @@
(function(global) {
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);
}
global.assertEventuallyEq = assertEventuallyEq;
function assertEventuallyThrows(promise, expectedErrorType) {
assertThrowsInstanceOf(() => getPromiseResult(promise), expectedErrorType);
};
global.assertEventuallyThrows = assertEventuallyThrows;
function assertEventuallyDeepEq(promise, expected) {
assertDeepEq(getPromiseResult(promise), expected);
};
global.assertEventuallyDeepEq = assertEventuallyDeepEq;
})(this);

Просмотреть файл

@ -0,0 +1,104 @@
var BUGNUMBER = 1185106;
var summary = "async arrow function syntax";
print(BUGNUMBER + ": " + summary);
if (typeof Reflect !== "undefined" && Reflect.parse) {
// Parameters.
Reflect.parse("async () => 1");
Reflect.parse("async a => 1");
Reflect.parse("async (a) => 1");
Reflect.parse("async async => 1");
Reflect.parse("async (async) => 1");
Reflect.parse("async ([a]) => 1");
Reflect.parse("async ([a, b]) => 1");
Reflect.parse("async ({a}) => 1");
Reflect.parse("async ({a, b}) => 1");
assertThrows(() => Reflect.parse("async await => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async (await) => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async ([await]) => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async ({await}) => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async (a=await) => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async ([a=await]) => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async ({a=await}) => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async (a=await 1) => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async ([a=await 1]) => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async ({a=await 1}) => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async [a] => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async [a, b] => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async {a} => 1"), SyntaxError);
assertThrows(() => Reflect.parse("async {a: b} => 1"), SyntaxError);
// Expression body.
Reflect.parse("async a => a == b");
// Expression body with nested async function.
Reflect.parse("async a => async");
Reflect.parse("async a => async b => c");
Reflect.parse("async a => async function() {}");
Reflect.parse("async a => async function b() {}");
assertThrows(() => Reflect.parse("async a => async b"), SyntaxError);
assertThrows(() => Reflect.parse("async a => async function"), SyntaxError);
assertThrows(() => Reflect.parse("async a => async function()"), SyntaxError);
// Expression body with `await`.
Reflect.parse("async a => await 1");
Reflect.parse("async a => await await 1");
Reflect.parse("async a => await await await 1");
assertThrows(() => Reflect.parse("async a => await"), SyntaxError);
assertThrows(() => Reflect.parse("async a => await await"), SyntaxError);
// `await` is Unary Expression and it cannot have `async` function as an
// operand.
assertThrows(() => Reflect.parse("async a => await async X => Y"), SyntaxError);
Reflect.parse("async a => await (async X => Y)");
// But it can have `async` identifier as an operand.
Reflect.parse("async async => await async");
// Block body.
Reflect.parse("async X => {yield}");
// `yield` handling.
Reflect.parse("async X => yield");
Reflect.parse("async yield => X");
Reflect.parse("async yield => yield");
Reflect.parse("async X => {yield}");
Reflect.parse("async X => {yield}");
Reflect.parse("async yield => {X}");
Reflect.parse("async yield => {yield}");
Reflect.parse("function* g() { async X => yield }");
assertThrows(() => Reflect.parse("'use strict'; async yield => X"), SyntaxError);
assertThrows(() => Reflect.parse("'use strict'; async (yield) => X"), SyntaxError);
assertThrows(() => Reflect.parse("'use strict'; async X => yield"), SyntaxError);
assertThrows(() => Reflect.parse("'use strict'; async X => {yield}"), SyntaxError);
assertThrows(() => Reflect.parse("function* g() { async yield => X }"));
assertThrows(() => Reflect.parse("function* g() { async (yield) => X }"));
assertThrows(() => Reflect.parse("function* g() { async ([yield]) => X }"));
assertThrows(() => Reflect.parse("function* g() { async ({yield}) => X }"));
// Not async functions.
Reflect.parse("async ()");
Reflect.parse("async (a)");
Reflect.parse("async (async)");
Reflect.parse("async ([a])");
Reflect.parse("async ([a, b])");
Reflect.parse("async ({a})");
Reflect.parse("async ({a, b})");
// Async arrow function is assignment expression.
Reflect.parse("a ? async () => {1} : b");
Reflect.parse("a ? b : async () => {1}");
assertThrows(() => Reflect.parse("async () => {1} ? a : b"), SyntaxError);
}
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,20 @@
var BUGNUMBER = 1185106;
var summary = "async/await syntax in module";
print(BUGNUMBER + ": " + summary);
if (typeof parseModule === "function") {
parseModule("async function f() { await 3; }");
parseModule("async function f() { await 3; }");
assertThrows(() => parseModule("var await = 5;"), SyntaxError);
assertThrows(() => parseModule("export var await;"), SyntaxError);
assertThrows(() => parseModule("async function f() { function g() { await 3; } }"), SyntaxError);
if (typeof Reflect !== "undefined" && Reflect.parse) {
assertThrows(() => Reflect.parse("export default async function() { yield; }", { target: "module" }), SyntaxError);
assertThrows(() => Reflect.parse("export default async function() { yield = 1; }", { target: "module" }), SyntaxError);
}
}
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,89 @@
var BUGNUMBER = 1185106;
var summary = "async/await syntax";
print(BUGNUMBER + ": " + summary);
if (typeof Reflect !== "undefined" && Reflect.parse) {
assertEq(Reflect.parse("function a() {}").body[0].async, false);
assertEq(Reflect.parse("function* a() {}").body[0].async, false);
assertEq(Reflect.parse("async function a() {}").body[0].async, true);
assertEq(Reflect.parse("() => {}").body[0].async, undefined);
// Async generators are not allowed (with regards to spec)
assertThrows(() => Reflect.parse("async function* a() {}"), SyntaxError);
// No line terminator after async
assertEq(Reflect.parse("async\nfunction a(){}").body[0].expression.name, "async");
// Async function expressions
assertEq(Reflect.parse("(async function() {})()").body[0].expression.callee.async, true);
assertEq(Reflect.parse("var k = async function() {}").body[0].declarations[0].init.async, true);
assertEq(Reflect.parse("var nmd = async function named() {}").body[0].declarations[0].init.id.name, "named");
// `await` handling for function declaration name inherits.
assertEq(Reflect.parse("async function await() {}").body[0].id.name, "await");
assertThrows(() => Reflect.parse("async function f() { async function await() {} }"), SyntaxError);
// `await` is not allowed in function expression name.
assertThrows(() => Reflect.parse("(async function await() {})"), SyntaxError);
// Awaiting not directly inside an async function is not allowed
assertThrows(() => Reflect.parse("await 4;"), SyntaxError);
assertThrows(() => Reflect.parse("function a() { await 4; }"), SyntaxError);
assertThrows(() => Reflect.parse("function* a() { await 4; }"), SyntaxError);
assertThrows(() => Reflect.parse("async function k() { function a() { await 4; } }"), SyntaxError);
// No line terminator after await is allowed
assertThrows(() => Reflect.parse("async function a() { await\n4; }"), SyntaxError);
// Await is not allowed as a default expr.
assertThrows(() => Reflect.parse("async function a(k = await 3) {}"), SyntaxError);
assertThrows(() => Reflect.parse("async function a() { async function b(k = await 3) {} }"), SyntaxError);
assertThrows(() => Reflect.parse("async function a() { async function b(k = [await 3]) {} }"), SyntaxError);
assertThrows(() => Reflect.parse("async function a() { async function b([k = await 3]) {} }"), SyntaxError);
assertThrows(() => Reflect.parse("async function a() { async function b([k = [await 3]]) {} }"), SyntaxError);
assertThrows(() => Reflect.parse("async function a() { async function b({k = await 3}) {} }"), SyntaxError);
assertThrows(() => Reflect.parse("async function a() { async function b({k = [await 3]}) {} }"), SyntaxError);
// Await is not legal as an identifier in an async function.
assertThrows(() => Reflect.parse("async function a() { var await = 4; }"), SyntaxError);
assertThrows(() => Reflect.parse("async function a() { return await; }"), SyntaxError);
// Await is still available as an identifier name in strict mode code.
Reflect.parse("function a() { 'use strict'; var await = 3; }");
Reflect.parse("'use strict'; var await = 3;");
// Await is treated differently depending on context. Various cases.
Reflect.parse("var await = 3; async function a() { await 4; }");
Reflect.parse("async function a() { await 4; } var await = 5");
Reflect.parse("async function a() { function b() { return await; } }");
Reflect.parse("async function a() { var k = { async: 4 } }");
Reflect.parse("function a() { await: 4 }");
assertEq(Reflect.parse("async function a() { await 4; }")
.body[0].body.body[0].expression.operator, "await");
assertEq(Reflect.parse("async function a() { async function b() { await 4; } }")
.body[0].body.body[0].body.body[0].expression.operator, "await");
// operator priority test
assertEq(Reflect.parse("async function a() { await 2 + 3; }")
.body[0].body.body[0].expression.left.argument.value, 2);
assertEq(Reflect.parse("async function a() { await 2 + 3; }")
.body[0].body.body[0].expression.left.operator, "await");
assertEq(Reflect.parse("async function a() { await 2 + 3; }")
.body[0].body.body[0].expression.right.value, 3);
// blocks and other constructions
assertEq(Reflect.parse("{ async function a() { return 2; } }")
.body[0].body[0].async, true);
// Async function expression is primary expression.
Reflect.parse("(async function a() {}.constructor)");
}
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,24 @@
var BUGNUMBER = 1185106;
var summary = "async function toString";
print(BUGNUMBER + ": " + summary);
async function f1(a, b, c) { await a; }
assertEq(f1.toString(),
"async function f1(a, b, c) { await a; }");
assertEq(async function (a, b, c) { await a; }.toString(),
"async function (a, b, c) { await a; }");
assertEq((async (a, b, c) => await a).toString(),
"async (a, b, c) => await a");
assertEq((async (a, b, c) => { await a; }).toString(),
"async (a, b, c) => { await a; }");
assertEq({ async foo(a, b, c) { await a; } }.foo.toString(),
"async function foo(a, b, c) { await a; }");
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,71 @@
var BUGNUMBER = 1185106;
var summary = "yield handling in async function";
print(BUGNUMBER + ": " + summary);
function testPassArgsBody(argsbody) {
Reflect.parse(`async function a${argsbody}`);
Reflect.parse(`(async function a${argsbody})`);
Reflect.parse(`(async function ${argsbody})`);
Reflect.parse(`({ async m${argsbody} })`);
}
function testErrorArgsBody(argsbody, prefix="") {
assertThrows(() => Reflect.parse(`${prefix} async function a${argsbody}`), SyntaxError);
assertThrows(() => Reflect.parse(`${prefix} (async function a${argsbody})`), SyntaxError);
assertThrows(() => Reflect.parse(`${prefix} (async function ${argsbody})`), SyntaxError);
assertThrows(() => Reflect.parse(`${prefix} ({ async m${argsbody} })`), SyntaxError);
}
function testErrorArgsBodyStrict(argsbody) {
testErrorArgsBody(argsbody);
testErrorArgsBody(argsbody, "'use strict'; ");
assertThrows(() => Reflect.parse(`class X { async m${argsbody} }`), SyntaxError);
assertThrows(() => Reflect.parse(`class X { static async m${argsbody} }`), SyntaxError);
assertThrows(() => Reflect.parse(`export default async function ${argsbody}`, { target: "module" }), SyntaxError);
}
if (typeof Reflect !== "undefined" && Reflect.parse) {
// `yield` handling is inherited in async function declaration name.
Reflect.parse("async function yield() {}");
Reflect.parse("function f() { async function yield() {} }");
assertThrows(() => Reflect.parse("function* g() { async function yield() {} }"), SyntaxError);
assertThrows(() => Reflect.parse("'use strict'; async function yield() {}"), SyntaxError);
// `yield` is treated as an identifier in an async function expression name.
// `yield` is not allowed as an identifier in strict code.
Reflect.parse("(async function yield() {});");
Reflect.parse("function f() { (async function yield() {}); }");
Reflect.parse("function* g() { (async function yield() {}); }");
assertThrows(() => Reflect.parse("'use strict'; (async function yield() {});"), SyntaxError);
// `yield` handling is inherited in async method name.
Reflect.parse("({ async yield() {} });");
Reflect.parse("function f() { ({ async yield() {} }); }");
Reflect.parse("function* g() { ({ async yield() {} }); }");
Reflect.parse("'use strict'; ({ async yield() {} });");
Reflect.parse("class X { async yield() {} }");
Reflect.parse("({ async [yield]() {} });");
Reflect.parse("function f() { ({ async [yield]() {} }); }");
Reflect.parse("function* g() { ({ async [yield]() {} }); }");
assertThrows(() => Reflect.parse("'use strict'; ({ async [yield]() {} });"), SyntaxError);
assertThrows(() => Reflect.parse("class X { async [yield]() {} }"), SyntaxError);
// `yield` is treated as an identifier in an async function parameter
// `yield` is not allowed as an identifier in strict code.
testPassArgsBody("(yield) {}");
testPassArgsBody("(yield = 1) {}");
testPassArgsBody("(a = yield) {}");
testErrorArgsBodyStrict("(yield 3) {}");
testErrorArgsBodyStrict("(a = yield 3) {}");
// `yield` is treated as an identifier in an async function body
// `yield` is not allowed as an identifier in strict code.
testPassArgsBody("() { yield; }");
testPassArgsBody("() { yield = 1; }");
testErrorArgsBodyStrict("() { yield 3; }");
}
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -47,6 +47,16 @@ function genFunDecl(style, id, params, body) {
generator: true,
style: style });
}
function asyncFunDecl(id, params, body) {
return Pattern({ type: "FunctionDeclaration",
id: id,
params: params,
defaults: [],
body: body,
generator: true,
async: true,
style: "es6" });
}
function varDecl(decls) {
return Pattern({ type: "VariableDeclaration", declarations: decls, kind: "var" });
}
@ -166,11 +176,29 @@ function genFunExpr(style, id, args, body) {
generator: true,
style: style });
}
function asyncFunExpr(id, args, body) {
return Pattern({ type: "FunctionExpression",
id: id,
params: args,
body: body,
generator: true,
async: true,
style: "es6" });
}
function arrowExpr(args, body) {
return Pattern({ type: "ArrowFunctionExpression",
params: args,
body: body });
}
function asyncArrowExpr(isExpression, args, body) {
return Pattern({ type: "ArrowFunctionExpression",
params: args,
body: body,
generator: true,
async: true,
expression: isExpression,
style: "es6" });
}
function metaProperty(meta, property) {
return Pattern({ type: "MetaProperty",

Просмотреть файл

@ -0,0 +1,25 @@
// |reftest| skip-if(!xulRuntime.shell)
// async function declaration.
assertDecl("async function foo() {}", asyncFunDecl(ident("foo"), [], blockStmt([])));
// async function expression.
assertExpr("(async function() {})", asyncFunExpr(null, [], blockStmt([])));
assertExpr("(async function foo() {})", asyncFunExpr(ident("foo"), [], blockStmt([])));
// async arrow.
assertExpr("async a => 1", asyncArrowExpr(true, [ident("a")], literal(1)));
assertExpr("async a => { 1 }", asyncArrowExpr(false, [ident("a")], blockStmt([exprStmt(literal(1))])));
assertExpr("async a => { return 1 }", asyncArrowExpr(false, [ident("a")], blockStmt([returnStmt(literal(1))])));
// async method.
assertExpr("({ async foo() {} })", objExpr([{ key: ident("foo"), value: asyncFunExpr(ident("foo"), [], blockStmt([]))}]));
assertStmt("class C { async foo() {} }", classStmt(ident("C"), null, [classMethod(ident("foo"), asyncFunExpr(ident("foo"), [], blockStmt([])), "method", false)]));
assertStmt("class C { static async foo() {} }", classStmt(ident("C"), null, [classMethod(ident("foo"), asyncFunExpr(ident("foo"), [], blockStmt([])), "method", true)]));
// await expression.
assertDecl("async function foo() { await bar }", asyncFunDecl(ident("foo"), [], blockStmt([exprStmt(unExpr("await", ident("bar")))])));
if (typeof reportCompare === 'function')
reportCompare(true, true);

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/PodOperations.h"
#include "jit/JitFrames.h"
#include "vm/AsyncFunction.h"
#include "vm/GlobalObject.h"
#include "vm/Stack.h"
@ -446,8 +447,13 @@ MappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue
vp.setInt32(argsobj.initialLength());
} else {
MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().callee));
if (!argsobj.hasOverriddenCallee())
vp.setObject(argsobj.callee());
if (!argsobj.hasOverriddenCallee()) {
RootedFunction callee(cx, &argsobj.callee());
if (callee->isAsync())
vp.setObject(*GetWrappedAsyncFunction(callee));
else
vp.setObject(*callee);
}
}
return true;
}

118
js/src/vm/AsyncFunction.cpp Normal file
Просмотреть файл

@ -0,0 +1,118 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#include "vm/AsyncFunction.h"
#include "jscompartment.h"
#include "builtin/SelfHostingDefines.h"
#include "vm/GlobalObject.h"
#include "vm/SelfHosting.h"
using namespace js;
using namespace js::gc;
/* static */ bool
GlobalObject::initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global)
{
if (global->getReservedSlot(ASYNC_FUNCTION_PROTO).isObject())
return true;
RootedObject asyncFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
if (!asyncFunctionProto)
return false;
if (!DefineToStringTag(cx, asyncFunctionProto, cx->names().AsyncFunction))
return false;
RootedValue function(cx, global->getConstructor(JSProto_Function));
if (!function.toObjectOrNull())
return false;
RootedObject proto(cx, &function.toObject());
RootedAtom name(cx, cx->names().AsyncFunction);
RootedObject asyncFunction(cx, NewFunctionWithProto(cx, AsyncFunctionConstructor, 1,
JSFunction::NATIVE_CTOR, nullptr, name,
proto));
if (!asyncFunction)
return false;
if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto))
return false;
global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction));
global->setReservedSlot(ASYNC_FUNCTION_PROTO, ObjectValue(*asyncFunctionProto));
return true;
}
JSFunction*
js::GetWrappedAsyncFunction(JSFunction* unwrapped)
{
MOZ_ASSERT(unwrapped->isAsync());
return &unwrapped->getExtendedSlot(ASYNC_WRAPPED_SLOT).toObject().as<JSFunction>();
}
JSFunction*
js::GetUnwrappedAsyncFunction(JSFunction* wrapper)
{
JSFunction* unwrapped = &wrapper->getExtendedSlot(ASYNC_UNWRAPPED_SLOT).toObject().as<JSFunction>();
MOZ_ASSERT(unwrapped->isAsync());
return unwrapped;
}
bool
js::IsWrappedAsyncFunction(JSContext* cx, JSFunction* wrapper)
{
return IsSelfHostedFunctionWithName(wrapper, cx->names().AsyncWrapped);
}
bool
js::CreateAsyncFunction(JSContext* cx, HandleFunction wrapper, HandleFunction unwrapped,
MutableHandleFunction result)
{
// Create a new function with AsyncFunctionPrototype, reusing the script
// and the environment of `wrapper` function, and the name and the length
// of `unwrapped` function.
RootedObject proto(cx, GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global()));
RootedObject scope(cx, wrapper->environment());
RootedAtom atom(cx, unwrapped->name());
RootedFunction wrapped(cx, NewFunctionWithProto(cx, nullptr, 0,
JSFunction::INTERPRETED_LAMBDA,
scope, atom, proto,
AllocKind::FUNCTION_EXTENDED, TenuredObject));
if (!wrapped)
return false;
wrapped->initScript(wrapper->nonLazyScript());
// Link them each other to make GetWrappedAsyncFunction and
// GetUnwrappedAsyncFunction work.
unwrapped->setExtendedSlot(ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped));
wrapped->setExtendedSlot(ASYNC_UNWRAPPED_SLOT, ObjectValue(*unwrapped));
// The script of `wrapper` is self-hosted, so `wrapped` should also be
// set as self-hosted function.
wrapped->setIsSelfHostedBuiltin();
// Set LAZY_FUNCTION_NAME_SLOT to "AsyncWrapped" to make it detectable in
// IsWrappedAsyncFunction.
wrapped->setExtendedSlot(LAZY_FUNCTION_NAME_SLOT, StringValue(cx->names().AsyncWrapped));
// The length of the script of `wrapper` is different than the length of
// `unwrapped`. We should set actual length as resolved length, to avoid
// using the length of the script.
uint16_t length;
if (!unwrapped->getLength(cx, &length))
return false;
RootedValue lengthValue(cx, NumberValue(length));
if (!DefineProperty(cx, wrapped, cx->names().length, lengthValue,
nullptr, nullptr, JSPROP_READONLY))
{
return false;
}
result.set(wrapped);
return true;
}

30
js/src/vm/AsyncFunction.h Normal file
Просмотреть файл

@ -0,0 +1,30 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef vm_AsyncFunction_h
#define vm_AsyncFunction_h
#include "jscntxt.h"
#include "jsobj.h"
namespace js {
JSFunction*
GetWrappedAsyncFunction(JSFunction* unwrapped);
JSFunction*
GetUnwrappedAsyncFunction(JSFunction* wrapper);
bool
IsWrappedAsyncFunction(JSContext* cx, JSFunction* wrapper);
bool
CreateAsyncFunction(JSContext* cx, HandleFunction wrapper, HandleFunction unwrapped,
MutableHandleFunction result);
} // namespace js
#endif /* vm_AsyncFunction_h */

Просмотреть файл

@ -29,6 +29,11 @@
macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
macro(as, as, "as") \
macro(Async, Async, "Async") \
macro(AsyncFunction, AsyncFunction, "AsyncFunction") \
macro(AsyncFunction_wrap, AsyncFunction_wrap, "AsyncFunction_wrap") \
macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \
macro(async, async, "async") \
macro(await, await, "await") \
macro(Bool8x16, Bool8x16, "Bool8x16") \
macro(Bool16x8, Bool16x8, "Bool16x8") \
macro(Bool32x4, Bool32x4, "Bool32x4") \

Просмотреть файл

@ -17,6 +17,7 @@
#include "frontend/ParseNode.h"
#include "gc/Policy.h"
#include "vm/ArgumentsObject.h"
#include "vm/AsyncFunction.h"
#include "vm/GlobalObject.h"
#include "vm/ProxyObject.h"
#include "vm/Shape.h"
@ -1023,7 +1024,9 @@ const Class LexicalEnvironmentObject::class_ = {
};
/* static */ NamedLambdaObject*
NamedLambdaObject::create(JSContext* cx, HandleFunction callee, HandleObject enclosing,
NamedLambdaObject::create(JSContext* cx, HandleFunction callee,
HandleFunction func,
HandleObject enclosing,
gc::InitialHeap heap)
{
MOZ_ASSERT(callee->isNamedLambda());
@ -1045,14 +1048,14 @@ NamedLambdaObject::create(JSContext* cx, HandleFunction callee, HandleObject enc
if (!obj)
return nullptr;
obj->initFixedSlot(lambdaSlot(), ObjectValue(*callee));
obj->initFixedSlot(lambdaSlot(), ObjectValue(*func));
return static_cast<NamedLambdaObject*>(obj);
}
/* static */ NamedLambdaObject*
NamedLambdaObject::createTemplateObject(JSContext* cx, HandleFunction callee, gc::InitialHeap heap)
{
return create(cx, callee, nullptr, heap);
return create(cx, callee, callee, nullptr, heap);
}
/* static */ NamedLambdaObject*
@ -1060,7 +1063,15 @@ NamedLambdaObject::create(JSContext* cx, AbstractFramePtr frame)
{
RootedFunction fun(cx, frame.callee());
RootedObject enclosing(cx, frame.environmentChain());
return create(cx, fun, enclosing, gc::DefaultHeap);
return create(cx, fun, fun, enclosing, gc::DefaultHeap);
}
/* static */ NamedLambdaObject*
NamedLambdaObject::create(JSContext* cx, AbstractFramePtr frame, HandleFunction replacement)
{
RootedFunction fun(cx, frame.callee());
RootedObject enclosing(cx, frame.environmentChain());
return create(cx, fun, replacement, enclosing, gc::DefaultHeap);
}
/* static */ size_t
@ -3365,7 +3376,15 @@ js::InitFunctionEnvironmentObjects(JSContext* cx, AbstractFramePtr frame)
// Named lambdas may have an environment that holds itself for recursion.
if (callee->needsNamedLambdaEnvironment()) {
NamedLambdaObject* declEnv = NamedLambdaObject::create(cx, frame);
NamedLambdaObject* declEnv;
if (callee->isAsync()) {
// Named async function needs special environment to return
// wrapped function for the binding.
RootedFunction fun(cx, GetWrappedAsyncFunction(callee));
declEnv = NamedLambdaObject::create(cx, frame, fun);
} else {
declEnv = NamedLambdaObject::create(cx, frame);
}
if (!declEnv)
return false;
frame.pushOnEnvironmentChain(*declEnv);

Просмотреть файл

@ -515,6 +515,7 @@ class LexicalEnvironmentObject : public EnvironmentObject
class NamedLambdaObject : public LexicalEnvironmentObject
{
static NamedLambdaObject* create(JSContext* cx, HandleFunction callee,
HandleFunction replacement,
HandleObject enclosing, gc::InitialHeap heap);
public:
@ -522,6 +523,8 @@ class NamedLambdaObject : public LexicalEnvironmentObject
gc::InitialHeap heap);
static NamedLambdaObject* create(JSContext* cx, AbstractFramePtr frame);
static NamedLambdaObject* create(JSContext* cx, AbstractFramePtr frame,
HandleFunction replacement);
// For JITs.
static size_t lambdaSlot();

Просмотреть файл

@ -260,8 +260,8 @@ NewSingletonObjectWithObjectPrototype(JSContext* cx, Handle<GlobalObject*> globa
return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
}
static JSObject*
NewSingletonObjectWithFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global)
JSObject*
js::NewSingletonObjectWithFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global)
{
RootedObject proto(cx, global->getOrCreateFunctionPrototype(cx));
if (!proto)

Просмотреть файл

@ -97,6 +97,8 @@ class GlobalObject : public NativeObject
STAR_GENERATOR_OBJECT_PROTO,
STAR_GENERATOR_FUNCTION_PROTO,
STAR_GENERATOR_FUNCTION,
ASYNC_FUNCTION_PROTO,
ASYNC_FUNCTION,
MAP_ITERATOR_PROTO,
SET_ITERATOR_PROTO,
COLLATOR_PROTO,
@ -576,6 +578,19 @@ class GlobalObject : public NativeObject
return global->getOrCreateObject(cx, STAR_GENERATOR_FUNCTION, initStarGenerators);
}
static NativeObject* getOrCreateAsyncFunctionPrototype(JSContext* cx,
Handle<GlobalObject*> global)
{
return MaybeNativeObject(global->getOrCreateObject(cx, ASYNC_FUNCTION_PROTO,
initAsyncFunction));
}
static JSObject* getOrCreateAsyncFunction(JSContext* cx,
Handle<GlobalObject*> global)
{
return global->getOrCreateObject(cx, ASYNC_FUNCTION, initAsyncFunction);
}
static JSObject* getOrCreateMapIteratorPrototype(JSContext* cx,
Handle<GlobalObject*> global)
{
@ -725,6 +740,8 @@ class GlobalObject : public NativeObject
static bool initLegacyGeneratorProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initStarGenerators(JSContext* cx, Handle<GlobalObject*> global);
static bool initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global);
// Implemented in builtin/MapObject.cpp.
static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
@ -985,6 +1002,9 @@ StandardProtoKeyOrNull(const JSObject* obj)
return key;
}
JSObject*
NewSingletonObjectWithFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global);
} // namespace js
template<>

Просмотреть файл

@ -3446,9 +3446,10 @@ CASE(JSOP_DEFFUN)
* a compound statement (not at the top statement level of global code, or
* at the top level of a function body).
*/
ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
ReservedRooted<JSFunction*> fun(&rootFunction0, &REGS.sp[-1].toObject().as<JSFunction>());
if (!DefFunOperation(cx, script, REGS.fp()->environmentChain(), fun))
goto error;
REGS.sp--;
}
END_CASE(JSOP_DEFFUN)
@ -4330,27 +4331,8 @@ js::LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleVa
bool
js::DefFunOperation(JSContext* cx, HandleScript script, HandleObject envChain,
HandleFunction funArg)
HandleFunction fun)
{
/*
* If static link is not current scope, clone fun's object to link to the
* current scope via parent. We do this to enable sharing of compiled
* functions among multiple equivalent scopes, amortizing the cost of
* compilation over a number of executions. Examples include XUL scripts
* and event handlers shared among Firefox or other Mozilla app chrome
* windows, and user-defined JS functions precompiled and then shared among
* requests in server-side JS.
*/
RootedFunction fun(cx, funArg);
if (fun->isNative() || fun->environment() != envChain) {
fun = CloneFunctionObjectIfNotSingleton(cx, fun, envChain, nullptr, TenuredObject);
if (!fun)
return false;
} else {
MOZ_ASSERT(script->treatAsRunOnce());
MOZ_ASSERT(!script->functionNonDelazifying());
}
/*
* We define the function as a property of the variable object and not the
* current scope chain even for the case of function expression statements

Просмотреть файл

@ -55,6 +55,7 @@
macro(private, private_, TOK_STRICT_RESERVED) \
macro(protected, protected_, TOK_STRICT_RESERVED) \
macro(public, public_, TOK_STRICT_RESERVED) \
macro(await, await, TOK_AWAIT) \
/* \
* Yield is a token inside function*. Outside of a function*, it is a \
* future reserved keyword in strict mode, but a keyword in JS1.7 even \

Просмотреть файл

@ -1300,10 +1300,10 @@
* scripts where use of dynamic scoping inhibits optimization.
* Category: Variables and Scopes
* Type: Variables
* Operands: uint32_t funcIndex
* Stack: =>
* Operands:
* Stack: fun =>
*/ \
macro(JSOP_DEFFUN, 127,"deffun", NULL, 5, 0, 0, JOF_OBJECT) \
macro(JSOP_DEFFUN, 127,"deffun", NULL, 1, 1, 0, JOF_BYTE) \
/* Defines the new constant binding on global lexical scope.
*
* Throws if a binding with the same name already exists on the scope, or

Просмотреть файл

@ -38,6 +38,7 @@
#include "jit/InlinableNatives.h"
#include "js/CharacterEncoding.h"
#include "js/Date.h"
#include "vm/AsyncFunction.h"
#include "vm/Compression.h"
#include "vm/GeneratorObject.h"
#include "vm/Interpreter.h"
@ -1831,6 +1832,23 @@ js::ReportIncompatibleSelfHostedMethod(JSContext* cx, const CallArgs& args)
return false;
}
bool
intrinsic_CreateAsyncFunction(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
RootedFunction wrapper(cx, &args[0].toObject().as<JSFunction>());
RootedFunction unwrapped(cx, &args[1].toObject().as<JSFunction>());
RootedFunction wrapped(cx);
if (!CreateAsyncFunction(cx, wrapper, unwrapped, &wrapped))
return false;
args.rval().setObject(*wrapped);
return true;
}
/**
* Returns the default locale as a well-formed, but not necessarily canonicalized,
* BCP-47 language tag.
@ -2230,6 +2248,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
JS_FN("AddContentTelemetry", intrinsic_AddContentTelemetry, 2,0),
JS_FN("CreateAsyncFunction", intrinsic_CreateAsyncFunction, 1,0),
JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0,
IntrinsicIsConstructing),
JS_INLINABLE_FN("SubstringKernel", intrinsic_SubstringKernel, 3,0,
@ -2363,6 +2383,10 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("CallWeakSetMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0),
JS_FN("Promise_static_resolve", Promise_static_resolve, 1, 0),
JS_FN("Promise_static_reject", Promise_reject, 1, 0),
JS_FN("Promise_then", Promise_then, 2, 0),
// See builtin/TypedObject.h for descriptors of the typedobj functions.
JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0),
JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0),

Просмотреть файл

@ -68,17 +68,65 @@
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Move.h"
#include "mozilla/RefPtr.h"
#ifdef __cplusplus
namespace mozilla {
template<typename T>
class LinkedListElement;
namespace detail {
/**
* LinkedList supports refcounted elements using this adapter class. Clients
* using LinkedList<RefPtr<T>> will get a data structure that holds a strong
* reference to T as long as T is in the list.
*/
template<typename T>
struct LinkedListElementTraits
{
typedef T* RawType;
typedef const T* ConstRawType;
typedef T* ClientType;
typedef const T* ConstClientType;
// These static methods are called when an element is added to or removed from
// a linked list. It can be used to keep track ownership in lists that are
// supposed to own their elements. If elements are transferred from one list
// to another, no enter or exit calls happen since the elements still belong
// to a list.
static void enterList(LinkedListElement<T>* elt) {}
static void exitList(LinkedListElement<T>* elt) {}
};
template<typename T>
struct LinkedListElementTraits<RefPtr<T>>
{
typedef T* RawType;
typedef const T* ConstRawType;
typedef RefPtr<T> ClientType;
typedef RefPtr<const T> ConstClientType;
static void enterList(LinkedListElement<RefPtr<T>>* elt) { elt->asT()->AddRef(); }
static void exitList(LinkedListElement<RefPtr<T>>* elt) { elt->asT()->Release(); }
};
} /* namespace detail */
template<typename T>
class LinkedList;
template<typename T>
class LinkedListElement
{
typedef typename detail::LinkedListElementTraits<T> Traits;
typedef typename Traits::RawType RawType;
typedef typename Traits::ConstRawType ConstRawType;
typedef typename Traits::ClientType ClientType;
typedef typename Traits::ConstClientType ConstClientType;
/*
* It's convenient that we return nullptr when getNext() or getPrevious()
* hits the end of the list, but doing so costs an extra word of storage in
@ -155,21 +203,21 @@ public:
* Get the next element in the list, or nullptr if this is the last element
* in the list.
*/
T* getNext() { return mNext->asT(); }
const T* getNext() const { return mNext->asT(); }
RawType getNext() { return mNext->asT(); }
ConstRawType getNext() const { return mNext->asT(); }
/*
* Get the previous element in the list, or nullptr if this is the first
* element in the list.
*/
T* getPrevious() { return mPrev->asT(); }
const T* getPrevious() const { return mPrev->asT(); }
RawType getPrevious() { return mPrev->asT(); }
ConstRawType getPrevious() const { return mPrev->asT(); }
/*
* Insert aElem after this element in the list. |this| must be part of a
* linked list when you call setNext(); otherwise, this method will assert.
*/
void setNext(T* aElem)
void setNext(RawType aElem)
{
MOZ_ASSERT(isInList());
setNextUnsafe(aElem);
@ -180,7 +228,7 @@ public:
* linked list when you call setPrevious(); otherwise, this method will
* assert.
*/
void setPrevious(T* aElem)
void setPrevious(RawType aElem)
{
MOZ_ASSERT(isInList());
setPreviousUnsafe(aElem);
@ -198,6 +246,32 @@ public:
mNext->mPrev = mPrev;
mNext = this;
mPrev = this;
Traits::exitList(this);
}
/*
* Remove this element from the list containing it. Returns a pointer to the
* element that follows this element (before it was removed). This method
* asserts if the element does not belong to a list.
*/
ClientType removeAndGetNext()
{
ClientType r = getNext();
remove();
return r;
}
/*
* Remove this element from the list containing it. Returns a pointer to the
* previous element in the containing list (before the removal). This method
* asserts if the element does not belong to a list.
*/
ClientType removeAndGetPrevious()
{
ClientType r = getPrevious();
remove();
return r;
}
/*
@ -221,6 +295,7 @@ public:
private:
friend class LinkedList<T>;
friend struct detail::LinkedListElementTraits<T>;
enum class NodeKind {
Normal,
@ -237,20 +312,20 @@ private:
* Return |this| cast to T* if we're a normal node, or return nullptr if
* we're a sentinel node.
*/
T* asT()
RawType asT()
{
return mIsSentinel ? nullptr : static_cast<T*>(this);
return mIsSentinel ? nullptr : static_cast<RawType>(this);
}
const T* asT() const
ConstRawType asT() const
{
return mIsSentinel ? nullptr : static_cast<const T*>(this);
return mIsSentinel ? nullptr : static_cast<ConstRawType>(this);
}
/*
* Insert aElem after this element, but don't check that this element is in
* the list. This is called by LinkedList::insertFront().
*/
void setNextUnsafe(T* aElem)
void setNextUnsafe(RawType aElem)
{
LinkedListElement *listElem = static_cast<LinkedListElement*>(aElem);
MOZ_ASSERT(!listElem->isInList());
@ -259,13 +334,15 @@ private:
listElem->mPrev = this;
this->mNext->mPrev = listElem;
this->mNext = listElem;
Traits::enterList(aElem);
}
/*
* Insert aElem before this element, but don't check that this element is in
* the list. This is called by LinkedList::insertBack().
*/
void setPreviousUnsafe(T* aElem)
void setPreviousUnsafe(RawType aElem)
{
LinkedListElement<T>* listElem = static_cast<LinkedListElement<T>*>(aElem);
MOZ_ASSERT(!listElem->isInList());
@ -274,6 +351,8 @@ private:
listElem->mPrev = this->mPrev;
this->mPrev->mNext = listElem;
this->mPrev = listElem;
Traits::enterList(aElem);
}
/*
@ -288,6 +367,10 @@ private:
return;
}
if (!mIsSentinel) {
Traits::enterList(this);
}
MOZ_ASSERT(aOther.mNext->mPrev == &aOther);
MOZ_ASSERT(aOther.mPrev->mNext == &aOther);
@ -307,6 +390,10 @@ private:
*/
aOther.mNext = &aOther;
aOther.mPrev = &aOther;
if (!mIsSentinel) {
Traits::exitList(&aOther);
}
}
LinkedListElement& operator=(const LinkedListElement<T>& aOther) = delete;
@ -317,16 +404,22 @@ template<typename T>
class LinkedList
{
private:
typedef typename detail::LinkedListElementTraits<T> Traits;
typedef typename Traits::RawType RawType;
typedef typename Traits::ConstRawType ConstRawType;
typedef typename Traits::ClientType ClientType;
typedef typename Traits::ConstClientType ConstClientType;
LinkedListElement<T> sentinel;
public:
class Iterator {
T* mCurrent;
RawType mCurrent;
public:
explicit Iterator(T* aCurrent) : mCurrent(aCurrent) {}
explicit Iterator(RawType aCurrent) : mCurrent(aCurrent) {}
T* operator *() const {
RawType operator *() const {
return mCurrent;
}
@ -363,7 +456,7 @@ public:
/*
* Add aElem to the front of the list.
*/
void insertFront(T* aElem)
void insertFront(RawType aElem)
{
/* Bypass setNext()'s this->isInList() assertion. */
sentinel.setNextUnsafe(aElem);
@ -372,7 +465,7 @@ public:
/*
* Add aElem to the back of the list.
*/
void insertBack(T* aElem)
void insertBack(RawType aElem)
{
sentinel.setPreviousUnsafe(aElem);
}
@ -380,24 +473,24 @@ public:
/*
* Get the first element of the list, or nullptr if the list is empty.
*/
T* getFirst() { return sentinel.getNext(); }
const T* getFirst() const { return sentinel.getNext(); }
RawType getFirst() { return sentinel.getNext(); }
ConstRawType getFirst() const { return sentinel.getNext(); }
/*
* Get the last element of the list, or nullptr if the list is empty.
*/
T* getLast() { return sentinel.getPrevious(); }
const T* getLast() const { return sentinel.getPrevious(); }
RawType getLast() { return sentinel.getPrevious(); }
ConstRawType getLast() const { return sentinel.getPrevious(); }
/*
* Get and remove the first element of the list. If the list is empty,
* return nullptr.
*/
T* popFirst()
ClientType popFirst()
{
T* ret = sentinel.getNext();
ClientType ret = sentinel.getNext();
if (ret) {
static_cast<LinkedListElement<T>*>(ret)->remove();
static_cast<LinkedListElement<T>*>(RawType(ret))->remove();
}
return ret;
}
@ -406,11 +499,11 @@ public:
* Get and remove the last element of the list. If the list is empty,
* return nullptr.
*/
T* popLast()
ClientType popLast()
{
T* ret = sentinel.getPrevious();
ClientType ret = sentinel.getPrevious();
if (ret) {
static_cast<LinkedListElement<T>*>(ret)->remove();
static_cast<LinkedListElement<T>*>(RawType(ret))->remove();
}
return ret;
}
@ -531,10 +624,10 @@ public:
private:
friend class LinkedListElement<T>;
void assertContains(const T* aValue) const
void assertContains(const RawType aValue) const
{
#ifdef DEBUG
for (const T* elem = getFirst(); elem; elem = elem->getNext()) {
for (ConstRawType elem = getFirst(); elem; elem = elem->getNext()) {
if (elem == aValue) {
return;
}

Просмотреть файл

@ -156,6 +156,24 @@ TestMove()
list3.clear();
}
static void
TestRemoveAndGet()
{
LinkedList<SomeClass> list;
SomeClass one(1), two(2), three(3);
list.insertBack(&one);
list.insertBack(&two);
list.insertBack(&three);
{ unsigned int check[] { 1, 2, 3 }; CheckListValues(list, check); }
MOZ_RELEASE_ASSERT(two.removeAndGetNext() == &three);
{ unsigned int check[] { 1, 3 }; CheckListValues(list, check); }
MOZ_RELEASE_ASSERT(three.removeAndGetPrevious() == &one);
{ unsigned int check[] { 1 }; CheckListValues(list, check); }
}
struct PrivateClass : private LinkedListElement<PrivateClass> {
friend class mozilla::LinkedList<PrivateClass>;
friend class mozilla::LinkedListElement<PrivateClass>;
@ -177,11 +195,74 @@ TestPrivate()
MOZ_RELEASE_ASSERT(count == 2);
}
struct CountedClass : public LinkedListElement<RefPtr<CountedClass>>
{
int mCount;
void AddRef() { mCount++; }
void Release() { mCount--; }
CountedClass() : mCount(0) {}
~CountedClass() { MOZ_RELEASE_ASSERT(mCount == 0); }
};
static void
TestRefPtrList()
{
LinkedList<RefPtr<CountedClass>> list;
CountedClass* elt1 = new CountedClass;
CountedClass* elt2 = new CountedClass;
list.insertBack(elt1);
list.insertBack(elt2);
MOZ_RELEASE_ASSERT(elt1->mCount == 1);
MOZ_RELEASE_ASSERT(elt2->mCount == 1);
for (RefPtr<CountedClass> p : list) {
MOZ_RELEASE_ASSERT(p->mCount == 2);
}
RefPtr<CountedClass> ptr = list.getFirst();
while (ptr) {
MOZ_RELEASE_ASSERT(ptr->mCount == 2);
RefPtr<CountedClass> next = ptr->getNext();
ptr->remove();
ptr = Move(next);
}
ptr = nullptr;
MOZ_RELEASE_ASSERT(elt1->mCount == 0);
MOZ_RELEASE_ASSERT(elt2->mCount == 0);
list.insertBack(elt1);
elt1->setNext(elt2);
MOZ_RELEASE_ASSERT(elt1->mCount == 1);
MOZ_RELEASE_ASSERT(elt2->mCount == 1);
RefPtr<CountedClass> first = list.popFirst();
MOZ_RELEASE_ASSERT(elt1->mCount == 1);
MOZ_RELEASE_ASSERT(elt2->mCount == 1);
RefPtr<CountedClass> second = list.popFirst();
MOZ_RELEASE_ASSERT(elt1->mCount == 1);
MOZ_RELEASE_ASSERT(elt2->mCount == 1);
first = second = nullptr;
delete elt1;
delete elt2;
}
int
main()
{
TestList();
TestPrivate();
TestMove();
TestRemoveAndGet();
TestRefPtrList();
return 0;
}

Просмотреть файл

@ -643,7 +643,8 @@ VerifyCertificate(CERTCertificate* signerCert, void* voidContext, void* pinArg)
return MapSECStatus(SECFailure);
}
Input certDER;
Result rv = certDER.Init(signerCert->derCert.data, signerCert->derCert.len);
mozilla::pkix::Result rv = certDER.Init(signerCert->derCert.data,
signerCert->derCert.len);
if (rv != Success) {
return mozilla::psm::GetXPCOMFromNSSError(MapResultToPRErrorCode(rv));
}
@ -654,7 +655,7 @@ VerifyCertificate(CERTCertificate* signerCert, void* voidContext, void* pinArg)
KeyPurposeId::id_kp_codeSigning,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/);
if (rv == Result::ERROR_EXPIRED_CERTIFICATE) {
if (rv == mozilla::pkix::Result::ERROR_EXPIRED_CERTIFICATE) {
// For code-signing you normally need trusted 3rd-party timestamps to
// handle expiration properly. The signer could always mess with their
// system clock so you can't trust the certificate was un-expired when

Просмотреть файл

@ -16,6 +16,8 @@ namespace mozilla { namespace ct {
using namespace mozilla::pkix;
typedef mozilla::pkix::Result Result;
// Note: length is always specified in bytes.
// Signed Certificate Timestamp (SCT) Version length
static const size_t kVersionLength = 1;

Просмотреть файл

@ -26,6 +26,8 @@ class MultiLogCTVerifier;
namespace mozilla { namespace psm {
typedef mozilla::pkix::Result Result;
// These values correspond to the CERT_CHAIN_KEY_SIZE_STATUS telemetry.
enum class KeySizeStatus {
NeverChecked = 0,

Просмотреть файл

@ -38,6 +38,8 @@ using namespace mozilla::pkix;
namespace mozilla { namespace psm {
typedef mozilla::pkix::Result Result;
static SECStatus
DigestLength(UniquePK11Context& context, uint32_t length)
{

Просмотреть файл

@ -9,6 +9,8 @@
using namespace mozilla;
using namespace mozilla::pkix;
namespace mozilla { namespace psm {
OCSPVerificationTrustDomain::OCSPVerificationTrustDomain(
NSSCertDBTrustDomain& certDBTrustDomain)
: mCertDBTrustDomain(certDBTrustDomain)
@ -122,3 +124,5 @@ OCSPVerificationTrustDomain::DigestBuf(
{
return mCertDBTrustDomain.DigestBuf(item, digestAlg, digestBuf, digestBufLen);
}
} } // namespace mozilla::psm

Просмотреть файл

@ -12,6 +12,8 @@
namespace mozilla { namespace psm {
typedef mozilla::pkix::Result Result;
class OCSPVerificationTrustDomain : public mozilla::pkix::TrustDomain
{
public:

Просмотреть файл

@ -164,7 +164,7 @@ ContentSignatureVerifier::CreateContextInternal(const nsACString& aData,
SECItem* certSecItem = &node->cert->derCert;
Input certDER;
Result result =
mozilla::pkix::Result result =
certDER.Init(BitwiseCast<uint8_t*, unsigned char*>(certSecItem->data),
certSecItem->len);
if (result != Success) {

Просмотреть файл

@ -257,11 +257,12 @@ VerifyCertificate(CERTCertificate* cert, void* voidContext, void* pinArg)
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
Result result = certVerifier->VerifyCert(cert,
certificateUsageObjectSigner,
Now(), pinArg,
nullptr, // hostname
context->builtChain);
mozilla::pkix::Result result =
certVerifier->VerifyCert(cert,
certificateUsageObjectSigner,
Now(), pinArg,
nullptr, // hostname
context->builtChain);
if (result != Success) {
return GetXPCOMFromNSSError(MapResultToPRErrorCode(result));
}

Просмотреть файл

@ -189,7 +189,7 @@ struct nsCancelHTTPDownloadEvent : Runnable {
}
};
Result
mozilla::pkix::Result
nsNSSHttpServerSession::createSessionFcn(const char* host,
uint16_t portnum,
/*out*/ nsNSSHttpServerSession** pSession)
@ -210,7 +210,7 @@ nsNSSHttpServerSession::createSessionFcn(const char* host,
return Success;
}
Result
mozilla::pkix::Result
nsNSSHttpRequestSession::createFcn(const nsNSSHttpServerSession* session,
const char* http_protocol_variant,
const char* path_and_query_string,
@ -250,7 +250,7 @@ nsNSSHttpRequestSession::createFcn(const nsNSSHttpServerSession* session,
return Success;
}
Result
mozilla::pkix::Result
nsNSSHttpRequestSession::setPostDataFcn(const char* http_data,
const uint32_t http_data_len,
const char* http_content_type)
@ -262,7 +262,7 @@ nsNSSHttpRequestSession::setPostDataFcn(const char* http_data,
return Success;
}
Result
mozilla::pkix::Result
nsNSSHttpRequestSession::trySendAndReceiveFcn(PRPollDesc** pPollDesc,
uint16_t* http_response_code,
const char** http_response_content_type,
@ -353,7 +353,7 @@ nsNSSHttpRequestSession::Release()
}
}
Result
mozilla::pkix::Result
nsNSSHttpRequestSession::internal_send_receive_attempt(bool &retryable_error,
PRPollDesc **pPollDesc,
uint16_t *http_response_code,

Просмотреть файл

@ -17,7 +17,6 @@
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
using namespace mozilla::psm;
template <size_t N>
inline Input
@ -46,7 +45,7 @@ protected:
};
static void
PutAndGet(OCSPCache& cache, const CertID& certID, Result result,
PutAndGet(mozilla::psm::OCSPCache& cache, const CertID& certID, Result result,
Time time, const char* firstPartyDomain = nullptr)
{
// The first time is thisUpdate. The second is validUntil.

Просмотреть файл

@ -320,7 +320,7 @@ class Test(MachCommandBase):
buckets = {}
for test in run_tests:
key = (test['flavor'], test.get('subsuite', ''))
key = (test['flavor'], test['subsuite'])
buckets.setdefault(key, []).append(test)
for (flavor, subsuite), tests in sorted(buckets.items()):

Просмотреть файл

@ -378,17 +378,17 @@ class MachCommands(MachCommandBase):
if test['flavor'] not in ALL_FLAVORS:
continue
key = (test['flavor'], test.get('subsuite', ''))
key = (test['flavor'], test['subsuite'])
if test['flavor'] not in flavors:
unsupported.add(key)
continue
if subsuite == 'default':
# "--subsuite default" means only run tests that don't have a subsuite
if test.get('subsuite'):
if test['subsuite']:
unsupported.add(key)
continue
elif subsuite and test.get('subsuite', '') != subsuite:
elif subsuite and test['subsuite'] != subsuite:
unsupported.add(key)
continue

Просмотреть файл

@ -152,6 +152,10 @@ class ManifestParser(object):
# get the tests
for section, data in sections:
subsuite = ''
if 'subsuite' in data:
subsuite = data['subsuite']
# In case of defaults only, no other section than parent: has to
# be processed.
if defaults_only and not section.startswith('parent:'):
@ -217,6 +221,7 @@ class ManifestParser(object):
else:
_relpath = relpath(path, rootdir)
test['subsuite'] = subsuite
test['path'] = path
test['relpath'] = _relpath

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше