зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
a581a8b8cf
|
@ -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(<ok, 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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, ®S.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
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче