merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 31 Oct 2016 16:46:52 +0100
changeset 320272 8c9eed5227f8
parent 320215 a10837dc8cdf (current diff)
parent 320271 15bfa2b75e1b (diff)
child 320273 b1b66b1780c2
child 320275 37ab1d54a08e
push id20754
push usercbook@mozilla.com
push dateMon, 31 Oct 2016 15:58:35 +0000
treeherderfx-team@b1b66b1780c2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
merge mozilla-inbound to mozilla-central a=merge
--- a/browser/extensions/e10srollout/bootstrap.js
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -4,56 +4,48 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 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 = {
   "beta"    : 0.5,  // 50%
   "release" : 1.0,  // 100%
 };
 
 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";
 const PREF_COHORT_NAME         = "e10s.rollout.cohort";
 const PREF_E10S_OPTED_IN       = "browser.tabs.remote.autostart";
 const PREF_E10S_FORCE_ENABLED  = "browser.tabs.remote.force-enable";
 const PREF_E10S_FORCE_DISABLED = "browser.tabs.remote.force-disable";
 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
   // it's better to also run it on every startup. If the user has
   // made manual changes to the prefs, this will keep the data
   // reported more accurate.
   // It's also fine (and preferred) to just do it here on startup
   // (instead of observing prefs), because e10s takes a restart
   // to take effect, so we keep the data based on how it was when
   // the session started.
   defineCohort();
-
-  setUpSpinnerCheck();
 }
 
 function install() {
   defineCohort();
 }
 
 let cohortDefinedOnThisSession = false;
 
@@ -173,105 +165,20 @@ function optedOut() {
 
 /* If this function returns a non-empty string, it
  * means that this particular user should be temporarily
  * disqualified due to some particular reason.
  * If a user shouldn't be disqualified, then an empty
  * 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)
       .getSelectedLocale("global")
       .split("-")[0];
 
   if (applicationLanguage == "ru") {
     return "ru";
   }
 
   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);
-}
--- a/browser/extensions/e10srollout/install.rdf.in
+++ b/browser/extensions/e10srollout/install.rdf.in
@@ -5,17 +5,17 @@
 
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 
   <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>
 
     <!-- Target Application this theme can install into,
         with minimum and maximum supported versions. -->
     <em:targetApplication>
       <Description>
--- a/dom/html/HTMLAnchorElement.cpp
+++ b/dom/html/HTMLAnchorElement.cpp
@@ -342,20 +342,21 @@ HTMLAnchorElement::SetText(const nsAStri
 }
 
 NS_IMETHODIMP
 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
 HTMLAnchorElement::SetPing(const nsAString& aValue)
 {
   return SetAttr(kNameSpaceID_None, nsGkAtoms::ping, aValue, true);
 }
 
--- a/dom/html/HTMLAreaElement.cpp
+++ b/dom/html/HTMLAreaElement.cpp
@@ -216,20 +216,21 @@ IMPL_URI_PART(Hash)
 #undef IMPL_URI_PART
 
 NS_IMETHODIMP
 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
 HTMLAreaElement::SetPing(const nsAString& aValue)
 {
   return SetAttr(kNameSpaceID_None, nsGkAtoms::ping, aValue, true);
 }
 
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1674,78 +1674,16 @@ nsGenericHTMLElement::GetURIAttr(nsIAtom
 /* static */ bool
 nsGenericHTMLElement::IsScrollGrabAllowed(JSContext*, JSObject*)
 {
   // Only allow scroll grabbing in chrome
   nsIPrincipal* prin = nsContentUtils::SubjectPrincipal();
   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
 {
   nsAutoString value;
   GetHTMLAttr(nsGkAtoms::contextmenu, value);
   if (!value.IsEmpty()) {
     //XXXsmaug How should this work in Shadow DOM?
     nsIDocument* doc = GetUncomposedDoc();
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -1058,30 +1058,16 @@ protected:
   {
     nsAutoString value;
     value.AppendFloat(aValue);
 
     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,
    * this may need to get an editor that's not actually on this element's
    * associated TextControlFrame.  This is used by the spellchecking routines
    * to get the editor affected by changing the spellcheck attribute on this
    * node.
    */
   virtual already_AddRefed<nsIEditor> GetAssociatedEditor();
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5239,19 +5239,19 @@ ContentParent::ForceTabPaint(TabParent* 
   }
   ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch);
 }
 
 bool
 ContentParent::RecvAccumulateChildHistogram(
                 InfallibleTArray<Accumulation>&& aAccumulations)
 {
-  Telemetry::AccumulateChild(aAccumulations);
+  Telemetry::AccumulateChild(GeckoProcessType_Content, aAccumulations);
   return true;
 }
 
 bool
 ContentParent::RecvAccumulateChildKeyedHistogram(
                 InfallibleTArray<KeyedAccumulation>&& aAccumulations)
 {
-  Telemetry::AccumulateChildKeyed(aAccumulations);
+  Telemetry::AccumulateChildKeyed(GeckoProcessType_Content, aAccumulations);
   return true;
 }
--- a/gfx/ipc/GPUChild.cpp
+++ b/gfx/ipc/GPUChild.cpp
@@ -132,16 +132,30 @@ GPUChild::RecvNotifyUiObservers(const ns
   nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
   MOZ_ASSERT(obsSvc);
   if (obsSvc) {
     obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr);
   }
   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)
 {
   if (aWhy == AbnormalShutdown) {
 #ifdef MOZ_CRASHREPORTER
     if (mCrashReporter) {
       mCrashReporter->GenerateCrashReport(OtherPid());
       mCrashReporter = nullptr;
--- a/gfx/ipc/GPUChild.h
+++ b/gfx/ipc/GPUChild.h
@@ -33,16 +33,18 @@ public:
 
   // gfxVarReceiver overrides.
   void OnVarChanged(const GfxVarUpdate& aVar) override;
 
   // PGPUChild overrides.
   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;
 
   static void Destroy(UniquePtr<GPUChild>&& aChild);
 
 private:
   GPUProcessHost* mHost;
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -92,19 +92,21 @@ GPUParent::Init(base::ProcessId aParentP
   gfxVars::Initialize();
   gfxPlatform::InitNullMetadata();
   // Ensure our Factory is initialised, mainly for gfx logging to work.
   gfxPlatform::InitMoz2DLogging();
 #if defined(XP_WIN)
   DeviceManagerDx::Init();
   DeviceManagerD3D9::Init();
 #endif
+
   if (NS_FAILED(NS_InitMinimalXPCOM())) {
     return false;
   }
+
   CompositorThreadHolder::Start();
   APZThreadUtils::SetControllerThread(CompositorThreadHolder::Loop());
   APZCTreeManager::InitializeGlobalState();
   VRManager::ManagerInit();
   LayerTreeOwnerTracker::Initialize();
   mozilla::ipc::SetThisProcessName("GPU Process");
 #ifdef XP_WIN
   wmf::MFStartup();
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -132,16 +132,21 @@ public:
   // true if the message was sent, false if not.
   bool NotifyGpuObservers(const char* aTopic);
 
   // Returns access to the PGPU protocol if a GPU process is present.
   GPUChild* GetGPUChild() {
     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();
 
   bool CreateContentCompositorBridge(base::ProcessId aOtherProcess,
                                      ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint);
   bool CreateContentImageBridge(base::ProcessId aOtherProcess,
                                 ipc::Endpoint<PImageBridgeChild>* aOutEndpoint);
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -9,16 +9,18 @@ include protocol PImageBridge;
 include protocol PVRManager;
 include protocol PVsyncBridge;
 include protocol PVideoDecoderManager;
 
 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 {
 
 union GfxPrefValue {
   bool;
   int32_t;
   uint32_t;
@@ -83,12 +85,16 @@ child:
   // Graphics errors, analogous to PContent::GraphicsError
   async GraphicsError(nsCString aError);
 
   async InitCrashReporter(Shmem shmem);
 
   // 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
 } // namespace mozilla
--- a/ipc/glue/BackgroundImpl.cpp
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -1732,20 +1732,16 @@ ChildImpl::CloseForCurrentThread()
     return;
   }
 
 #ifdef DEBUG
   MOZ_ASSERT(!threadLocalInfo->mClosed);
   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);
 }
 
 // static
 BackgroundChildImpl::ThreadLocal*
 ChildImpl::GetThreadLocalForCurrentThread()
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -484,41 +484,34 @@ MessageChannel::MessageChannel(MessageLi
     mInTimeoutSecondHalf(false),
     mNextSeqno(0),
     mLastSendError(SyncSendError::SendSuccess),
     mDispatchingAsyncMessage(false),
     mDispatchingAsyncMessageNestedLevel(0),
     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),
     mNotifiedChannelDone(false),
     mFlags(REQUIRE_DEFAULT),
     mPeerPidSet(false),
     mPeerPid(-1)
 {
     MOZ_COUNT_CTOR(ipc::MessageChannel);
 
 #ifdef OS_WIN
     mTopFrame = nullptr;
     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);
     MOZ_RELEASE_ASSERT(mEvent, "CreateEvent failed! Nothing is going to work!");
 #endif
 }
 
 MessageChannel::~MessageChannel()
@@ -630,31 +623,33 @@ MessageChannel::Clear()
     //
     // In practice, mListener owns the channel, so the channel gets deleted
     // before mListener.  But just to be safe, mListener is a weak pointer.
 
     if (gParentProcessBlocker == this) {
         gParentProcessBlocker = nullptr;
     }
 
-    mDequeueOneTask->Cancel();
-
     mWorkerLoop = nullptr;
     delete mLink;
     mLink = nullptr;
 
     mOnChannelConnectedTask->Cancel();
 
     if (mChannelErrorTask) {
         mChannelErrorTask->Cancel();
         mChannelErrorTask = nullptr;
     }
 
     // Free up any memory used by pending messages.
+    for (RefPtr<MessageTask> task : mPending) {
+        task->Clear();
+    }
     mPending.clear();
+
     mOutOfTurnReplies.clear();
     while (!mDeferred.empty()) {
         mDeferred.pop();
     }
 }
 
 bool
 MessageChannel::Open(Transport* aTransport, MessageLoop* aIOLoop, Side aSide)
@@ -874,29 +869,16 @@ MessageChannel::ShouldDeferMessage(const
     //
     // Deferring in the parent only sort of breaks message ordering. When the
     // child's message comes in, we can pretend the child hasn't quite
     // finished sending it yet. Since the message is sync, we know that the
     // child hasn't moved on yet.
     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)
 {
     AssertLinkThread();
     mMonitor->AssertCurrentThreadOwns();
 
     if (MaybeInterceptSpecialIOMessage(aMsg))
         return;
@@ -920,59 +902,66 @@ MessageChannel::OnMessageReceivedFromLin
         NotifyWorkerThread();
         return;
     }
 
     // Nested messages cannot be compressed.
     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;
+            }
         }
     }
 
     bool wakeUpSyncSend = AwaitingSyncReply() && !ShouldDeferMessage(aMsg);
 
     bool shouldWakeUp = AwaitingInterruptReply() ||
                         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.
     bool shouldPostTask = !shouldWakeUp || wakeUpSyncSend;
 
     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:
     //
     // (1) We are waiting on a sync reply - main thread is blocked on the
     //     IPC monitor.
     //   - If the message is NESTED_INSIDE_SYNC, we wake up the main thread to
     //     deliver the message depending on ShouldDeferMessage. Otherwise, we
     //     leave it in the mPending queue, posting a task to the main event
@@ -985,83 +974,83 @@ MessageChannel::OnMessageReceivedFromLin
     //
     // (3) We are not waiting on a reply.
     //   - We post a task to the main event loop.
     //
     // Note that, we may notify the main thread even though the monitor is not
     // 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;
         }
     }
 }
 
 void
 MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction)
 {
+    mMonitor->AssertCurrentThreadOwns();
+
     IPC_LOG("ProcessPendingRequests for seqno=%d, xid=%d",
             aTransaction.SequenceNumber(), aTransaction.TransactionID());
 
     // Loop until there aren't any more nested messages to process.
     for (;;) {
         // If we canceled during ProcessPendingRequest, then we need to leave
         // immediately because the results of ShouldDeferMessage will be
         // operating with weird state (as if no Send is in progress). That could
         // cause even NOT_NESTED sync messages to be processed (but not
         // NOT_NESTED async messages), which would break message ordering.
         if (aTransaction.IsCanceled()) {
             return;
         }
 
         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");
             bool defer = ShouldDeferMessage(msg);
 
             // Only log the interesting messages.
             if (msg.is_sync() || msg.nested_level() == IPC::Message::NESTED_INSIDE_CPOW) {
                 IPC_LOG("ShouldDeferMessage(seqno=%d) = %d", msg.seqno(), defer);
             }
 
             if (!defer) {
                 if (!toProcess.append(Move(msg)))
                     MOZ_CRASH();
-                it = mPending.erase(it);
+
+                p = p->removeAndGetNext();
                 continue;
             }
-            it++;
+            p = p->getNext();
         }
 
         if (toProcess.empty()) {
             break;
         }
 
         // Processing these messages could result in more messages, so we
         // loop around to check for more afterwards.
@@ -1353,19 +1342,19 @@ MessageChannel::Call(Message* aMsg, Mess
         Message recvd;
         MessageMap::iterator it;
 
         if ((it = mOutOfTurnReplies.find(mInterruptStack.top().seqno()))
             != mOutOfTurnReplies.end())
         {
             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
             // deferred in-call that needs to be processed.  either way, we
             // won't break the inner while loop again until something new
             // happens.
             continue;
         }
@@ -1444,47 +1433,48 @@ MessageChannel::Call(Message* aMsg, Mess
 bool
 MessageChannel::WaitForIncomingMessage()
 {
 #ifdef OS_WIN
     SyncStackFrame frame(this, true);
     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;
     }
-
-    return OnMaybeDequeueOne();
+    if (!HasPendingEvents()) {
+        return WaitForInterruptNotify();
+    }
+
+    MOZ_RELEASE_ASSERT(!mPending.isEmpty());
+    RefPtr<MessageTask> task = mPending.getFirst();
+    RunMessage(*task);
+    return true;
 }
 
 bool
 MessageChannel::HasPendingEvents()
 {
     AssertWorkerThread();
     mMonitor->AssertCurrentThreadOwns();
-    return Connected() && !mPending.empty();
+    return Connected() && !mPending.isEmpty();
 }
 
 bool
 MessageChannel::InterruptEventOccurred()
 {
     AssertWorkerThread();
     mMonitor->AssertCurrentThreadOwns();
     IPC_ASSERT(InterruptStackDepth() > 0, "not in wait loop");
 
     return (!Connected() ||
-            !mPending.empty() ||
+            !mPending.isEmpty() ||
             (!mOutOfTurnReplies.empty() &&
              mOutOfTurnReplies.find(mInterruptStack.top().seqno()) !=
              mOutOfTurnReplies.end()));
 }
 
 bool
 MessageChannel::ProcessPendingRequest(Message &&aUrgent)
 {
@@ -1498,95 +1488,163 @@ MessageChannel::ProcessPendingRequest(Me
         ReportConnectionError("MessageChannel::ProcessPendingRequest");
         return false;
     }
 
     return true;
 }
 
 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:
     // 1. child sends a NOT_NESTED sync message S
     // 2. parent sends a NESTED_INSIDE_SYNC sync message H at the same time
     // 3. parent times out H
     // 4. child starts processing H and sends a NESTED_INSIDE_SYNC message H' nested
     //    within the same transaction
     // 5. parent dispatches S and sends reply
     // 6. child asserts because it instead expected a reply to H'.
     //
     // To solve this, we refuse to process S in the parent until we get a reply
     // to H. More generally, let the timed out message be M. We don't process a
     // 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();
 
     nsAutoPtr<Message> reply;
 
     IPC_LOG("DispatchMessage: seqno=%d, xid=%d", aMsg.seqno(), aMsg.transaction_id());
 
@@ -1775,39 +1833,19 @@ MessageChannel::MaybeUndeferIncall()
     Message call(Move(mDeferred.top()));
     mDeferred.pop();
 
     // fix up fudge factor we added to account for race
     IPC_ASSERT(0 < mRemoteStackDepthGuess, "fatal logic error");
     --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
 MessageChannel::ExitedCxxStack()
 {
     mListener->OnExitedCxxStack();
     if (mSawInterruptOutMsg) {
         MonitorAutoLock lock(*mMonitor);
@@ -1820,28 +1858,20 @@ MessageChannel::ExitedCxxStack()
 void
 MessageChannel::EnqueuePendingMessages()
 {
     AssertWorkerThread();
     mMonitor->AssertCurrentThreadOwns();
 
     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
 IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
 {
     return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
            (aTimeout <= (PR_IntervalNow() - aStart));
 }
@@ -1938,17 +1968,17 @@ MessageChannel::SetReplyTimeoutMs(int32_
 }
 
 void
 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());
 }
 
 void
 MessageChannel::DispatchOnChannelConnected()
 {
     AssertWorkerThread();
     MOZ_RELEASE_ASSERT(mPeerPidSet);
@@ -2286,26 +2316,24 @@ MessageChannel::DebugAbort(const char* f
     // technically we need the mutex for this, but we're dying anyway
     DumpInterruptStack("  ");
     printf_stderr("  remote Interrupt stack guess: %" PRIuSIZE "\n",
                   mRemoteStackDepthGuess);
     printf_stderr("  deferred stack size: %" PRIuSIZE "\n",
                   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);
 }
 
 void
 MessageChannel::DumpInterruptStack(const char* const pfx) const
 {
@@ -2343,23 +2371,43 @@ void
 MessageChannel::EndTimeout()
 {
     mMonitor->AssertCurrentThreadOwns();
 
     IPC_LOG("Ending timeout of seqno=%d", mTimedOutMessageSeqno);
     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();
     }
 }
 
 void
 MessageChannel::CancelTransaction(int transaction)
 {
     mMonitor->AssertCurrentThreadOwns();
 
@@ -2392,33 +2440,33 @@ MessageChannel::CancelTransaction(int tr
             mTransactionStack->Cancel();
         }
     } else {
         MOZ_RELEASE_ASSERT(mTransactionStack->TransactionID() == transaction);
         mTransactionStack->Cancel();
     }
 
     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
         // queue since if will get cancelled along with the transaction being
         // cancelled. This happens if the message in the queue is NESTED_INSIDE_SYNC.
         if (msg.is_sync() && msg.nested_level() != IPC::Message::NOT_NESTED) {
             MOZ_RELEASE_ASSERT(!foundSync);
             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();
     }
 }
 
 bool
 MessageChannel::IsInTransaction() const
 {
     MonitorAutoLock lock(*mMonitor);
     return !!mTransactionStack;
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -178,18 +178,16 @@ class MessageChannel : HasResultCodes
     /**
      * This function is used by hang annotation code to determine which IPDL
      * actor is highest in the call stack at the time of the hang. It should
      * be called from the main thread when a sync or intr message is about to
      * be sent.
      */
     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
     // locking, on potentially different threads.  Thus you should only use
     // them when you don't particularly care about getting a recent value (e.g.
     // in a memory report).
     bool Unsound_IsClosed() const {
         return mLink ? mLink->Unsound_IsClosed() : true;
@@ -271,20 +269,16 @@ class MessageChannel : HasResultCodes
     bool HasPendingEvents();
 
     void ProcessPendingRequests(AutoEnterTransaction& aTransaction);
     bool ProcessPendingRequest(Message &&aUrgent);
 
     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);
 
     // DispatchMessage will route to one of these functions depending on the
     // protocol type of the message.
     void DispatchSyncMessage(const Message &aMsg, Message*& aReply);
     void DispatchUrgentMessage(const Message &aMsg);
     void DispatchAsyncMessage(const Message &aMsg);
@@ -306,16 +300,18 @@ class MessageChannel : HasResultCodes
 
     bool WaitResponse(bool aWaitTimedOut);
 
     bool ShouldContinueFromTimeout();
 
     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
     // the other side (that sent the reply) thinks it has.  So, the
     // "view" returned here is |stackDepth| minus the number of
     // out-of-turn replies.
     //
     // Only called from the worker thread.
@@ -451,147 +447,66 @@ class MessageChannel : HasResultCodes
     // NOT our worker thread.
     void AssertLinkThread() const
     {
         MOZ_RELEASE_ASSERT(mWorkerLoopID != MessageLoop::current()->id(),
                            "on worker thread but should not be!");
     }
 
   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)
+        {}
+
+        NS_IMETHOD Run() override;
+        nsresult Cancel() override;
+        void Post();
+        void Clear();
+
+        bool IsScheduled() const { return mScheduled; }
 
-          // 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;
-          }
+        Message& Msg() { return mMessage; }
+        const Message& Msg() const { return mMessage; }
+
+    private:
+        MessageTask() = delete;
+        MessageTask(const MessageTask&) = delete;
 
-          CrashReporter::AnnotatePendingIPC(q.size(), topCount, topName, topType);
+        MessageChannel* mChannel;
+        Message mMessage;
+        bool mScheduled : 1;
+    };
 
-          mozalloc_handle_oom(n * sizeof(T));
-        }
-        return static_cast<T*>(p);
-      }
-      void deallocate(T* p, size_t n) {
-        ::operator delete(p);
-      }
-      MessageChannel& mChannel;
-    };
-    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.
     MessageListener* mListener;
     ChannelState mChannelState;
     RefPtr<RefCountedMonitor> mMonitor;
     Side mSide;
     MessageLink* mLink;
     MessageLoop* mWorkerLoop;           // thread where work is done
     RefPtr<CancelableRunnable> mChannelErrorTask;  // NotifyMaybeChannelError runnable
 
     // id() of mWorkerLoop.  This persists even after mWorkerLoop is cleared
     // 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
     // or give up.
     int32_t mTimeoutMs;
     bool mInTimeoutSecondHalf;
 
     // Worker-thread only; sequence numbers for messages that require
@@ -668,46 +583,44 @@ class MessageChannel : HasResultCodes
     // error.
     //
     // A message is only timed out if it initiated a transaction. This avoids
     // hitting a lot of corner cases with message nesting that we don't really
     // care about.
     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
     //
     //   |A<| be an async in-message,
     //   |S<| be a sync in-message,
     //   |C<| be an Interrupt in-call,
     //   |R<| be an Interrupt reply.
     //
     // 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.
     //
     // The first case is |S<|, a sync in-msg.  The other side must be blocked,
     // and thus can't send us any more messages until we process the sync
     // in-msg.
     //
     // The second case is |C<|, an Interrupt in-call; the other side must be blocked.
     // (There's a subtlety here: this in-call might have raced with an
     // out-call, but we detect that with the mechanism below,
     // |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
     // one Interrupt call on our stack, the other side *better* not have sent us
     // another blocking message, because it's blocked on a reply from us.
     //
     MessageQueue mPending;
 
@@ -722,17 +635,17 @@ class MessageChannel : HasResultCodes
     // calls.  With each Interrupt out-call sent, we send along what *we* think the
     // stack depth of the remote side is *before* it will receive the Interrupt call.
     //
     // After sending the out-call, our stack depth is "incremented" by pushing
     // that pending message onto mPending.
     //
     // 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
     // racy Interrupt calls.
     //
     // We then increment mRemoteStackDepth *just before* processing the
     // in-call, since we know the other side is waiting on it, and decrement
     // it *just after* finishing processing that in-call, since our response
@@ -783,17 +696,17 @@ class MessageChannel : HasResultCodes
     bool mNotifiedChannelDone;
 
     // See SetChannelFlags
     ChannelFlags mFlags;
 
     // 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;
 };
 
 void
 CancelCPOWs();
 
 } // namespace ipc
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -3329,24 +3329,19 @@ class _GenerateProtocolActorCode(ipdl.as
             onexitedcall.addstmt(StmtReturn(ExprCall(p.exitedCallVar())))
 
             # bool IsOnCxxStack()
             onstack = MethodDefn(
                 MethodDecl(p.onCxxStackVar().name, ret=Type.BOOL, const=1))
             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'))
         if ptype.isToplevel():
             onclose.addstmts([
                 StmtExpr(ExprCall(destroysubtreevar,
                                   args=[ _DestroyReason.NormalShutdown ])),
                 StmtExpr(ExprCall(deallocsubtreevar)),
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -780,17 +780,17 @@ struct JSClass {
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
 // previously allowed, but is now an ES5 violation and thus unsupported.
 //
 // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
 // the beginning of every global object's slots for use by the
 // 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                                                  \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
 #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp)                              \
   (((clasp)->flags & JSCLASS_IS_GLOBAL)                                       \
    && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
 
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -7063,17 +7063,17 @@ ParseFunction(ModuleValidator& m, ParseN
 
     RootedFunction& fun = m.dummyFunction();
     fun->setAtom(name);
     fun->setArgCount(0);
 
     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);
 
     Directives newDirectives = directives;
     ParseContext funpc(&m.parser(), funbox, &newDirectives);
     if (!funpc.init())
         return false;
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/AsyncFunctions.js
@@ -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);
+                        });
+}
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -961,16 +961,17 @@ js::FutexRuntime::wait(JSContext* cx, js
     MOZ_ASSERT(&cx->runtime()->fx == this);
     MOZ_ASSERT(cx->runtime()->fx.canWait());
     MOZ_ASSERT(state_ == Idle || state_ == WaitingInterrupted);
 
     // Disallow waiting when a runtime is processing an interrupt.
     // See explanation below.
 
     if (state_ == WaitingInterrupted) {
+        UnlockGuard<Mutex> unlock(locked);
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED);
         return false;
     }
 
     const bool isTimed = timeout.isSome();
 
     auto finalEnd = timeout.map([](mozilla::TimeDuration& timeout) {
         return mozilla::TimeStamp::Now() + timeout;
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -699,17 +699,17 @@ ObjectDefineProperties(JSContext* cx, Ha
 
         // Step 5.a.
         if (!GetOwnPropertyDescriptor(cx, props, nextKey, &desc))
             return false;
 
         // 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))
             {
                 return false;
             }
         }
     }
 
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -1914,18 +1914,18 @@ CommonStaticResolveRejectImpl(JSContext*
     // Step 6 of Resolve, 4 of Reject.
     args.rval().setObject(*promise);
     return true;
 }
 
 /**
  * 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);
 }
 
 /**
  * Unforgeable version of ES2016, 25.4.4.4, Promise.reject.
  */
 /* static */ JSObject*
@@ -1944,18 +1944,18 @@ PromiseObject::unforgeableReject(JSConte
 
     // Step 5.
     return promise;
 }
 
 /**
  * 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);
 }
 
 /**
  * Unforgeable version of ES2016, 25.4.4.5, Promise.resolve.
  */
 /* static */ JSObject*
@@ -2046,18 +2046,18 @@ js::OriginalPromiseThen(JSContext* cx, H
     // Step 5.
     if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, resultPromise, resolve, reject))
         return nullptr;
 
     return resultPromise;
 }
 
 // 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);
 
     // Step 1.
     RootedValue promiseVal(cx, args.thisv());
 
     RootedValue onFulfilled(cx, args.get(0));
     RootedValue onRejected(cx, args.get(1));
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -148,11 +148,18 @@ class PromiseTask : public JS::AsyncTask
 
     // Called on a helper thread after StartAsyncTask. After execute()
     // completes, the JS::FinishAsyncTaskCallback will be called. If this fails
     // the task will be enqueued for deletion at some future point without ever
     // calling finishPromise().
     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 */
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -88,16 +88,17 @@ enum UnaryOperator {
 
     UNOP_DELETE = 0,
     UNOP_NEG,
     UNOP_POS,
     UNOP_NOT,
     UNOP_BITNOT,
     UNOP_TYPEOF,
     UNOP_VOID,
+    UNOP_AWAIT,
 
     UNOP_LIMIT
 };
 
 enum VarDeclKind {
     VARDECL_ERR = -1,
     VARDECL_VAR = 0,
     VARDECL_CONST,
@@ -157,17 +158,18 @@ static const char* const binopNames[] = 
 
 static const char* const unopNames[] = {
     "delete",  /* UNOP_DELETE */
     "-",       /* UNOP_NEG */
     "+",       /* UNOP_POS */
     "!",       /* UNOP_NOT */
     "~",       /* UNOP_BITNOT */
     "typeof",  /* UNOP_TYPEOF */
-    "void"     /* UNOP_VOID */
+    "void",    /* UNOP_VOID */
+    "await"    /* UNOP_AWAIT */
 };
 
 static const char* const nodeTypeNames[] = {
 #define ASTDEF(ast, str, method) str,
 #include "jsast.tbl"
 #undef ASTDEF
     nullptr
 };
@@ -461,17 +463,17 @@ class NodeBuilder
 
     MOZ_MUST_USE bool literal(HandleValue val, TokenPos* pos, MutableHandleValue dst);
 
     MOZ_MUST_USE bool identifier(HandleValue name, TokenPos* pos, MutableHandleValue dst);
 
     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);
 
     MOZ_MUST_USE bool switchCase(HandleValue expr, NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
 
     MOZ_MUST_USE bool catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos* pos,
                                   MutableHandleValue dst);
@@ -1585,27 +1587,28 @@ NodeBuilder::arrayPattern(NodeVector& el
 {
     return listNode(AST_ARRAY_PATT, "elements", elts, pos, dst);
 }
 
 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);
     if (!newArray(args, &array))
         return false;
     if (!newArray(defaults, &defarray))
         return false;
 
     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]);
     if (!cb.isNull()) {
         return callback(cb, opt(id), array, body, isGeneratorVal, isExpressionVal, pos, dst);
     }
 
     if (isGenerator) {
@@ -1619,28 +1622,30 @@ NodeBuilder::function(ASTType type, Toke
         styleVal.setString(styleStr);
         return newNode(type, pos,
                        "id", id,
                        "params", array,
                        "defaults", defarray,
                        "body", body,
                        "rest", rest,
                        "generator", isGeneratorVal,
+                       "async", isAsyncVal,
                        "style", styleVal,
                        "expression", isExpressionVal,
                        dst);
     }
 
     return newNode(type, pos,
                    "id", id,
                    "params", array,
                    "defaults", defarray,
                    "body", body,
                    "rest", rest,
                    "generator", isGeneratorVal,
+                   "async", isAsyncVal,
                    "expression", isExpressionVal,
                    dst);
 }
 
 bool
 NodeBuilder::classMethod(HandleValue name, HandleValue body, PropKind kind, bool isStatic,
                          TokenPos* pos, MutableHandleValue dst)
 {
@@ -1799,16 +1804,17 @@ class ASTSerializer
     bool literal(ParseNode* pn, MutableHandleValue dst);
 
     bool pattern(ParseNode* pn, MutableHandleValue dst);
     bool arrayPattern(ParseNode* pn, MutableHandleValue dst);
     bool objectPattern(ParseNode* pn, MutableHandleValue dst);
 
     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);
 
     bool comprehensionBlock(ParseNode* pn, MutableHandleValue dst);
     bool comprehensionIf(ParseNode* pn, MutableHandleValue dst);
     bool comprehension(ParseNode* pn, MutableHandleValue dst);
     bool generatorExpression(ParseNode* pn, MutableHandleValue dst);
 
@@ -1875,16 +1881,19 @@ UnaryOperator
 ASTSerializer::unop(ParseNodeKind kind, JSOp op)
 {
     if (IsDeleteKind(kind))
         return UNOP_DELETE;
 
     if (IsTypeofKind(kind))
         return UNOP_TYPEOF;
 
+    if (kind == PNK_AWAIT)
+        return UNOP_AWAIT;
+
     switch (op) {
       case JSOP_NEG:
         return UNOP_NEG;
       case JSOP_POS:
         return UNOP_POS;
       case JSOP_NOT:
         return UNOP_NOT;
       case JSOP_BITNOT:
@@ -2902,28 +2911,29 @@ ASTSerializer::expression(ParseNode* pn,
       case PNK_BITOR:
       case PNK_BITXOR:
       case PNK_BITAND:
       case PNK_IN:
       case PNK_INSTANCEOF:
         return leftAssociate(pn, dst);
 
       case PNK_POW:
-	return rightAssociate(pn, dst);
+        return rightAssociate(pn, dst);
 
       case PNK_DELETENAME:
       case PNK_DELETEPROP:
       case PNK_DELETEELEM:
       case PNK_DELETEEXPR:
       case PNK_TYPEOFNAME:
       case PNK_TYPEOFEXPR:
       case PNK_VOID:
       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));
 
         UnaryOperator op = unop(pn->getKind(), pn->getOp());
         LOCAL_ASSERT(op > UNOP_ERR && op < UNOP_LIMIT);
 
         RootedValue expr(cx);
         return expression(pn->pn_kid, &expr) &&
@@ -3391,16 +3401,17 @@ ASTSerializer::function(ParseNode* pn, A
 
     GeneratorStyle generatorStyle =
         pn->pn_funbox->isGenerator()
         ? (pn->pn_funbox->isLegacyGenerator()
            ? GeneratorStyle::Legacy
            : GeneratorStyle::ES6)
         : GeneratorStyle::None;
 
+    bool isAsync = pn->pn_funbox->isAsync();
     bool isExpression =
 #if JS_HAS_EXPR_CLOSURES
         func->isExprBody();
 #else
         false;
 #endif
 
     RootedValue id(cx);
@@ -3411,23 +3422,24 @@ ASTSerializer::function(ParseNode* pn, A
     NodeVector args(cx);
     NodeVector defaults(cx);
 
     RootedValue body(cx), rest(cx);
     if (func->hasRest())
         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;
     ParseNode* pnbody;
 
     /* Extract the args and body separately. */
     if (pn->isKind(PNK_PARAMSBODY)) {
         pnargs = pn;
@@ -3451,16 +3463,24 @@ ASTSerializer::functionArgsAndBody(Parse
         ParseNode* pnstart = pnbody->pn_head;
 
         // Skip over initial yield in generator.
         if (pnstart && pnstart->isKind(PNK_YIELD)) {
             MOZ_ASSERT(pnstart->getOp() == JSOP_INITIALYIELD);
             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);
       }
 
       default:
         LOCAL_NOT_REACHED("unexpected function contents");
     }
 }
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -88,16 +88,19 @@
 #define REGEXP_FLAGS_SLOT 2
 
 #define REGEXP_IGNORECASE_FLAG  0x01
 #define REGEXP_GLOBAL_FLAG      0x02
 #define REGEXP_MULTILINE_FLAG   0x04
 #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
 #define MODULE_STATE_PARSED       1
 #define MODULE_STATE_INSTANTIATED 2
 #define MODULE_STATE_EVALUATED    3
 
 #endif
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -58,17 +58,17 @@ class MOZ_STACK_CLASS BytecodeCompiler
     // Call setters for optional arguments.
     void maybeSetSourceCompressor(SourceCompressionTask* sourceCompressor);
     void setSourceArgumentsNotIncluded();
 
     JSScript* compileGlobalScript(ScopeKind scopeKind);
     JSScript* compileEvalScript(HandleObject environment, HandleScope enclosingScope);
     ModuleObject* compileModule();
     bool compileFunctionBody(MutableHandleFunction fun, Handle<PropertyNameVector> formals,
-                             GeneratorKind generatorKind);
+                             GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
 
     ScriptSourceObject* sourceObjectPtr() const;
 
   private:
     JSScript* compileScript(HandleObject environment, SharedContext* sc);
     bool checkLength();
     bool createScriptSource();
     bool maybeCompressSource();
@@ -430,17 +430,18 @@ BytecodeCompiler::compileModule()
 
     MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
     return module;
 }
 
 bool
 BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun,
                                       Handle<PropertyNameVector> formals,
-                                      GeneratorKind generatorKind)
+                                      GeneratorKind generatorKind,
+                                      FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(fun);
     MOZ_ASSERT(fun->isTenured());
 
     fun->setArgCount(formals.length());
 
     if (!createSourceAndParser())
         return false;
@@ -448,17 +449,17 @@ BytecodeCompiler::compileFunctionBody(Mu
     // Speculatively parse using the default directives implied by the context.
     // If a directive is encountered (e.g., "use strict") that changes how the
     // function should have been parsed, we backup and reparse with the new set
     // of directives.
 
     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;
     } while (!fn);
 
     if (!NameFunctions(cx, fn))
         return false;
 
@@ -641,17 +642,18 @@ frontend::CompileLazyFunction(JSContext*
         return false;
     Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars, length,
                                     /* foldConstants = */ true, usedNames, nullptr, lazy);
     if (!parser.checkOptions())
         return false;
 
     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;
 
     if (!NameFunctions(cx, pn))
         return false;
 
     RootedScriptSource sourceObject(cx, lazy->sourceObject());
     MOZ_ASSERT(sourceObject);
@@ -674,51 +676,63 @@ frontend::CompileLazyFunction(JSContext*
     return bce.emitFunctionScript(pn->pn_body);
 }
 
 // Compile a JS function body, which might appear as the value of an event
 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
 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);
 
     // FIXME: make Function pass in two strings and parse them as arguments and
     // ProgramElements respectively.
 
     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
 frontend::CompileFunctionBody(JSContext* cx, MutableHandleFunction fun,
                               const ReadOnlyCompileOptions& options,
                               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
 frontend::CompileFunctionBody(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,
-                               NotGenerator);
+                               NotGenerator, SyncFunction);
 }
 
-
 bool
 frontend::CompileStarGeneratorBody(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);
+                               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);
+}
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -63,16 +63,21 @@ CompileFunctionBody(JSContext* cx, Mutab
                     const ReadOnlyCompileOptions& options,
                     Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf);
 
 MOZ_MUST_USE bool
 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);
 
 /*
  * True if str consists of an IdentifierStart character, followed by one or
  * more IdentifierPart characters, i.e. it matches the IdentifierName production
  * in the language spec.
  *
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2460,16 +2460,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       // This invokes the (user-controllable) iterator protocol.
       case PNK_SPREAD:
         MOZ_ASSERT(pn->isArity(PN_UNARY));
         *answer = true;
         return true;
 
       case PNK_YIELD_STAR:
       case PNK_YIELD:
+      case PNK_AWAIT:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         *answer = true;
         return true;
 
       // Deletion generally has side effects, even if isolated cases have none.
       case PNK_DELETENAME:
       case PNK_DELETEPROP:
       case PNK_DELETEELEM:
@@ -6923,30 +6924,42 @@ BytecodeEmitter::emitFunction(ParseNode*
 
     /* Make the function object a literal in the outer script's pool. */
     unsigned index = objectList.add(pn->pn_funbox);
 
     /* Non-hoisted functions simply emit their respective op. */
     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))
                     return false;
             } else {
                 if (!emit1(JSOP_NULL))
                     return false;
             }
         }
 
         if (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);
     }
 
     MOZ_ASSERT(!needsProto);
 
     bool topLevelFunction;
     if (sc->isFunctionBox() || (sc->isEvalContext() && sc->strict())) {
         // No nested functions inside other functions are top-level.
@@ -6967,40 +6980,120 @@ BytecodeEmitter::emitFunction(ParseNode*
 
             RootedModuleObject module(cx, sc->asModuleContext()->module());
             if (!module->noteFunctionDeclaration(cx, name, fun))
                 return false;
         } else {
             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;
             switchToMain();
         }
     } else {
         // 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);
         };
 
         if (!emitInitializeName(name, emitLambda))
             return false;
         if (!emit1(JSOP_POP))
             return false;
     }
 
     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)
 {
     /* Emit an annotated nop so IonBuilder can recognize the 'do' loop. */
     unsigned noteIndex;
     if (!newSrcNote(SRC_WHILE, &noteIndex))
         return false;
     if (!emit1(JSOP_NOP))
         return false;
@@ -8371,18 +8464,27 @@ BytecodeEmitter::emitPropertyList(ParseN
 
         if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER)
             objp.set(nullptr);
 
         if (propdef->pn_right->isKind(PNK_FUNCTION) &&
             propdef->pn_right->pn_funbox->needsHomeObject())
         {
             MOZ_ASSERT(propdef->pn_right->pn_funbox->function()->allowSuperProperty());
-            if (!emit2(JSOP_INITHOMEOBJECT, isIndex))
-                return false;
+            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.
         if (type == ClassBody) {
             switch (op) {
               case JSOP_INITPROP:        op = JSOP_INITHIDDENPROP;          break;
               case JSOP_INITPROP_GETTER: op = JSOP_INITHIDDENPROP_GETTER;   break;
               case JSOP_INITPROP_SETTER: op = JSOP_INITHIDDENPROP_SETTER;   break;
@@ -9238,16 +9340,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
         break;
 
       case PNK_GENERATOR:
         if (!emit1(JSOP_GENERATOR))
             return false;
         break;
 
       case PNK_YIELD:
+      case PNK_AWAIT:
         if (!emitYield(pn))
             return false;
         break;
 
       case PNK_STATEMENTLIST:
         if (!emitStatementList(pn))
             return false;
         break;
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -598,16 +598,19 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     MOZ_MUST_USE bool emitYield(ParseNode* pn);
     MOZ_MUST_USE bool emitYieldOp(JSOp op);
     MOZ_MUST_USE bool emitYieldStar(ParseNode* iter, ParseNode* gen);
 
     MOZ_MUST_USE bool emitPropLHS(ParseNode* pn);
     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
     // opcode onto the stack in the right order. In the case of SETELEM, the
     // value to be assigned must already be pushed.
     enum class EmitElemOption { Get, Set, Call, IncDec, CompoundAssign };
     MOZ_MUST_USE bool emitElemOperands(ParseNode* pn, EmitElemOption opts);
 
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -306,16 +306,17 @@ ContainsHoistedDeclaration(ExclusiveCont
       case PNK_COMPUTED_NAME:
       case PNK_SPREAD:
       case PNK_MUTATEPROTO:
       case PNK_COLON:
       case PNK_SHORTHAND:
       case PNK_CONDITIONAL:
       case PNK_TYPEOFNAME:
       case PNK_TYPEOFEXPR:
+      case PNK_AWAIT:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_DELETENAME:
       case PNK_DELETEPROP:
       case PNK_DELETEELEM:
       case PNK_DELETEEXPR:
       case PNK_POS:
@@ -1778,16 +1779,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
         return FoldList(cx, pn, parser, inGenexpLambda);
 
       case PNK_YIELD_STAR:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME));
         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) &&
                     pn->pn_right->pn_left->isKind(PNK_NAME) &&
                     pn->pn_right->pn_right->isKind(PNK_GENERATOR)));
         if (!pn->pn_left)
             return true;
         return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -427,16 +427,21 @@ class FullParseHandler
         return new_<BinaryNode>(PNK_YIELD, op, pos, value, gen);
     }
 
     ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value, ParseNode* gen) {
         TokenPos pos(begin, value->pn_pos.end);
         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) {
         return new_<ListNode>(PNK_STATEMENTLIST, pos);
     }
 
     MOZ_MUST_USE bool isFunctionStmt(ParseNode* stmt) {
         while (stmt->isKind(PNK_LABEL))
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -502,16 +502,17 @@ class NameResolver
           case PNK_YIELD_STAR:
             MOZ_ASSERT(cur->isArity(PN_BINARY));
             MOZ_ASSERT(cur->pn_right->isKind(PNK_NAME));
             if (!resolve(cur->pn_left, prefix))
                 return false;
             break;
 
           case PNK_YIELD:
+          case PNK_AWAIT:
             MOZ_ASSERT(cur->isArity(PN_BINARY));
             if (cur->pn_left) {
                 if (!resolve(cur->pn_left, prefix))
                     return false;
             }
             MOZ_ASSERT(cur->pn_right->isKind(PNK_NAME) ||
                        (cur->pn_right->isKind(PNK_ASSIGN) &&
                         cur->pn_right->pn_left->isKind(PNK_NAME) &&
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -285,17 +285,18 @@ PushNodeChildren(ParseNode* pn, NodeStac
         return PushResult::Recyclable;
       }
 
       // The left half is the expression being yielded.  The right half is
       // internal goop: a name reference to the invisible '.generator' local
       // 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) ||
                    (pn->pn_right->isKind(PNK_ASSIGN) &&
                     pn->pn_right->pn_left->isKind(PNK_NAME) &&
                     pn->pn_right->pn_right->isKind(PNK_GENERATOR)));
         if (pn->pn_left)
             stack->push(pn->pn_left);
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -115,16 +115,17 @@ class ObjectBox;
     F(SETTHIS) \
     \
     /* Unary operators. */ \
     F(TYPEOFNAME) \
     F(TYPEOFEXPR) \
     F(VOID) \
     F(NOT) \
     F(BITNOT) \
+    F(AWAIT) \
     \
     /* \
      * Binary operators. \
      * These must be in the same order as TOK_OR and friends in TokenStream.h. \
      */ \
     F(OR) \
     F(AND) \
     F(BITOR) \
@@ -345,17 +346,18 @@ IsTypeofKind(ParseNodeKind kind)
  * PNK_MOD,
  * PNK_POW                  (**) is right-associative, but forms a list
  *                          nonetheless. Special hacks everywhere.
  *
  * PNK_POS,     unary       pn_kid: UNARY expr
  * 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
  * PNK_POSTINCREMENT,
  * PNK_PREDECREMENT,
  * PNK_POSTDECREMENT
  * PNK_NEW      list        pn_head: list of ctor, arg1, arg2, ... argN
  *                          pn_count: 1 + N (where N is number of args)
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -431,30 +431,31 @@ UsedNameTracker::rewind(RewindToken toke
     scopeCounter_ = token.scopeId;
 
     for (UsedNameMap::Range r = map_.all(); !r.empty(); r.popFront())
         r.front().value().resetToScope(token.scriptId, token.scopeId);
 }
 
 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),
     namedLambdaBindings_(nullptr),
     functionScopeBindings_(nullptr),
     extraVarScopeBindings_(nullptr),
     functionNode(nullptr),
     bufStart(0),
     bufEnd(0),
     startLine(1),
     startColumn(0),
     length(0),
     generatorKindBits_(GeneratorKindAsBits(generatorKind)),
+    asyncKindBits_(AsyncKindAsBits(asyncKind)),
     isGenexpLambda(false),
     hasDestructuringArgs(false),
     hasParameterExprs(false),
     hasDirectEvalInParameterExpr(false),
     hasDuplicateParameters(false),
     useAsm(false),
     insideUseAsm(false),
     isAnnexB(false),
@@ -729,31 +730,32 @@ Parser<ParseHandler>::newObjectBox(JSObj
     traceListHead = objbox;
 
     return objbox;
 }
 
 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());
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
      * function.
      */
     FunctionBox* funbox =
         alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, inheritedDirectives,
-                                options().extraWarningsOption, generatorKind);
+                                options().extraWarningsOption, generatorKind, asyncKind);
     if (!funbox) {
         ReportOutOfMemory(context);
         return nullptr;
     }
 
     traceListHead = funbox;
     if (fn)
         handler.setFunctionBox(fn, funbox);
@@ -850,17 +852,17 @@ Parser<ParseHandler>::reportBadReturn(No
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::isValidStrictBinding(PropertyName* name)
 {
     return name != context->names().eval &&
            name != context->names().arguments &&
            name != context->names().let &&
            name != context->names().static_ &&
-           !IsKeyword(name);
+           !(IsKeyword(name) && name != context->names().await);
 }
 
 /*
  * Check that it is permitted to introduce a binding for |name|. Use |pos| for
  * reporting error locations.
  */
 template <typename ParseHandler>
 bool
@@ -1962,16 +1964,17 @@ Parser<FullParseHandler>::moduleBody(Mod
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
 
     Node mn = handler.newModule();
     if (!mn)
         return null();
 
+    AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, true);
     ParseNode* pn = statementList(YieldIsKeyword);
     if (!pn)
         return null();
 
     MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
     mn->pn_body = pn;
 
     TokenKind tt;
@@ -2190,16 +2193,17 @@ Parser<SyntaxParseHandler>::finishFuncti
     if (!lazy)
         return false;
 
     // Flags that need to be copied into the JSScript when we do the full
     // parse.
     if (pc->sc()->strict())
         lazy->setStrict();
     lazy->setGeneratorKind(funbox->generatorKind());
+    lazy->setAsyncKind(funbox->asyncKind());
     if (funbox->isLikelyConstructorWrapper())
         lazy->setLikelyConstructorWrapper();
     if (funbox->isDerivedClassConstructor())
         lazy->setIsDerivedClassConstructor();
     if (funbox->needsHomeObject())
         lazy->setNeedsHomeObject();
     if (funbox->declaredArguments)
         lazy->setShouldDeclareArguments();
@@ -2209,38 +2213,49 @@ Parser<SyntaxParseHandler>::finishFuncti
     // Flags that need to copied back into the parser when we do the full
     // parse.
     PropagateTransitiveParseFlags(funbox, lazy);
 
     fun->initLazyScript(lazy);
     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)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Node fn = handler.newFunctionDefinition();
     if (!fn)
         return null();
 
     ParseNode* argsbody = handler.newList(PNK_PARAMSBODY);
     if (!argsbody)
         return null();
     fn->pn_body = argsbody;
 
     FunctionBox* funbox = newFunctionBox(fn, fun, inheritedDirectives, generatorKind,
-                                         /* tryAnnexB = */ false);
+                                         asyncKind, /* tryAnnexB = */ false);
     if (!funbox)
         return null();
     funbox->initStandaloneFunction(enclosingScope);
 
     ParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
         return null();
     funpc.setIsStandaloneFunctionBody();
@@ -2253,17 +2268,18 @@ Parser<FullParseHandler>::standaloneFunc
 
     bool duplicatedParam = false;
     for (uint32_t i = 0; i < formals.length(); i++) {
         if (!notePositionalFormalParameter(fn, formals[i], false, &duplicatedParam))
             return null();
     }
     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();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     if (tt != TOK_EOF) {
@@ -2389,23 +2405,38 @@ Parser<ParseHandler>::functionBody(InHan
                 // the invalid parameter name at the correct source location.
                 pc->newDirectives->setStrict();
                 return null();
             }
         }
     } 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();
 
         pn = handler.newReturnStatement(kid, handler.getPosition(kid));
         if (!pn)
             return null();
+
+        if (pc->isAsync()) {
+            handler.addStatementToList(stmtList, pn);
+            pn = stmtList;
+        }
     }
 
     switch (pc->generatorKind()) {
       case NotGenerator:
         MOZ_ASSERT(pc->lastYieldOffset == startYieldOffset);
         break;
 
       case LegacyGenerator:
@@ -2416,23 +2447,23 @@ Parser<ParseHandler>::functionBody(InHan
         MOZ_ASSERT(!IsGetterKind(kind));
         MOZ_ASSERT(!IsSetterKind(kind));
         MOZ_ASSERT(!IsConstructorKind(kind));
         MOZ_ASSERT(kind != Method);
         MOZ_ASSERT(type != ExpressionBody);
         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();
         if (!generator)
             return null();
         if (!handler.prependInitialYield(pn, generator))
             return null();
     }
@@ -2448,17 +2479,18 @@ Parser<ParseHandler>::functionBody(InHan
     }
 
     return finishLexicalScope(pc->varScope(), pn);
 }
 
 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);
 
     RootedFunction fun(context);
 
     gc::AllocKind allocKind = gc::AllocKind::FUNCTION;
     JSFunction::Flags flags;
 #ifdef DEBUG
@@ -2504,16 +2536,20 @@ Parser<ParseHandler>::newFunction(Handle
             allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         }
 #endif
         flags = (generatorKind == NotGenerator
                  ? JSFunction::INTERPRETED_NORMAL
                  : 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)
         return nullptr;
     if (options().selfHostingMode) {
         fun->setIsSelfHostedBuiltin();
 #ifdef DEBUG
         if (isGlobalSelfHostedBuiltin)
@@ -2613,29 +2649,57 @@ Parser<ParseHandler>::prefixAccessorName
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
                                         Node funcpn)
 {
     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(),
                    kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
             return false;
         }
 
         // Record the start of function source (for FunctionToString). If we
@@ -2672,18 +2736,19 @@ Parser<ParseHandler>::functionArguments(
 
         while (true) {
             if (hasRest) {
                 report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
                 return false;
             }
 
             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) {
                 if (IsSetterKind(kind)) {
                     report(ParseError, false, null(),
                            JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
                     return false;
                 }
@@ -2714,34 +2779,42 @@ Parser<ParseHandler>::functionArguments(
                 if (duplicatedParam) {
                     // Has duplicated args before the destructuring parameter.
                     report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
                     return false;
                 }
 
                 funbox->hasDestructuringArgs = true;
 
-                Node destruct = destructuringDeclarationWithoutYield(
+                Node destruct = destructuringDeclarationWithoutYieldOrAwait(
                     DeclarationKind::FormalParameter,
-                    yieldHandling, tt,
-                    JSMSG_YIELD_IN_DEFAULT);
+                    yieldHandling, tt);
                 if (!destruct)
                     return false;
 
                 if (!noteDestructuredPositionalFormalParameter(funcpn, destruct))
                     return false;
 
                 break;
               }
 
               case TOK_NAME:
               case TOK_YIELD: {
                 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;
 
                 if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams,
                                                    &duplicatedParam))
                 {
                     return false;
@@ -2786,17 +2859,17 @@ Parser<ParseHandler>::functionArguments(
                     hasDefault = true;
 
                     // The Function.length property is the number of formals
                     // before the first default argument.
                     funbox->length = positionalFormals.length() - 1;
                 }
                 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))
                     return false;
             }
 
             if (parenFreeArrow || IsSetterKind(kind))
                 break;
@@ -2916,17 +2989,17 @@ Parser<FullParseHandler>::skipLazyInnerF
     // When a lazily-parsed function is called, we only fully parse (and emit)
     // that function, not any of its nested children. The initial syntax-only
     // parse recorded the free variables of nested functions and their extents,
     // so we can skip over them after accounting for their free variables.
 
     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;
 
     LazyScript* lazy = fun->lazyScript();
     if (lazy->needsHomeObject())
         funbox->setNeedsHomeObject();
 
     PropagateTransitiveParseFlags(lazy, pc->sc());
@@ -3024,19 +3097,21 @@ Parser<ParseHandler>::templateLiteral(Yi
     } while (tt == TOK_TEMPLATE_HEAD);
     return nodeList;
 }
 
 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)
         return null();
 
     if (invoked)
         pn = handler.setLikelyIIFE(pn);
 
@@ -3059,17 +3134,17 @@ Parser<ParseHandler>::functionDefinition
         // If we are off the main thread, the generator meta-objects have
         // already been created by js::StartOffThreadParseScript, so cx will not
         // be necessary.
         JSContext* cx = context->maybeJSContext();
         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
         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();
 
     // Speculatively parse using the directives of the parent parsing context.
     // If a directive is encountered (e.g., "use strict") that changes how the
     // function should have been parsed, we backup and reparse with the new set
     // of directives.
     Directives directives(pc);
@@ -3078,17 +3153,17 @@ Parser<ParseHandler>::functionDefinition
     TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
     // Parse the inner function. The following is a loop as we may attempt to
     // reparse a function due to failed syntax parsing and encountering new
     // "use foo" directives.
     while (true) {
         if (trySyntaxParseInnerFunction(pn, fun, inHandling, yieldHandling, kind, generatorKind,
-                                        tryAnnexB, directives, &newDirectives))
+                                        asyncKind, tryAnnexB, directives, &newDirectives))
         {
             break;
         }
 
         // Return on error.
         if (tokenStream.hadError() || directives == newDirectives)
             return null();
 
@@ -3109,16 +3184,17 @@ Parser<ParseHandler>::functionDefinition
 
 template <>
 bool
 Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun,
                                                       InHandling inHandling,
                                                       YieldHandling yieldHandling,
                                                       FunctionSyntaxKind kind,
                                                       GeneratorKind generatorKind,
+                                                      FunctionAsyncKind asyncKind,
                                                       bool tryAnnexB,
                                                       Directives inheritedDirectives,
                                                       Directives* newDirectives)
 {
     // Try a syntax parse for this inner function.
     do {
         // If we're assuming this function is an IIFE, always perform a full
         // parse to avoid the overhead of a lazy syntax-only parse. Although
@@ -3138,17 +3214,17 @@ Parser<FullParseHandler>::trySyntaxParse
         tokenStream.tell(&position);
         if (!parser->tokenStream.seek(position, tokenStream))
             return false;
 
         // Make a FunctionBox before we enter the syntax parser, because |pn|
         // 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);
 
         if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, inHandling,
                                    yieldHandling, kind, inheritedDirectives, newDirectives))
         {
             if (parser->hadAbortedSyntaxParse()) {
@@ -3170,34 +3246,35 @@ Parser<FullParseHandler>::trySyntaxParse
             return false;
 
         // Update the end position of the parse node.
         pn->pn_pos.end = tokenStream.currentToken().pos.end;
         return true;
     } 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 <>
 bool
 Parser<SyntaxParseHandler>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun,
                                                         InHandling inHandling,
                                                         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>
 bool
 Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox,
                                     InHandling inHandling, YieldHandling yieldHandling,
                                     FunctionSyntaxKind kind, Directives inheritedDirectives,
                                     Directives* newDirectives)
@@ -3217,26 +3294,28 @@ Parser<ParseHandler>::innerFunction(Node
 
     return leaveInnerFunction(outerpc);
 }
 
 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);
 
     return innerFunction(pn, outerpc, funbox, inHandling, yieldHandling, kind, inheritedDirectives,
                          newDirectives);
 }
 
@@ -3257,47 +3336,48 @@ Parser<ParseHandler>::appendToCallSiteOb
 
     handler.addToCallSiteObject(callSiteObj, rawNode, cookedNode);
     return true;
 }
 
 template <>
 ParseNode*
 Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict,
-                                                 GeneratorKind generatorKind)
+                                                 GeneratorKind generatorKind,
+                                                 FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Node pn = handler.newFunctionDefinition();
     if (!pn)
         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();
     funbox->initFromLazyFunction();
 
     Directives newDirectives = directives;
     ParseContext funpc(this, funbox, &newDirectives);
     if (!funpc.init())
         return null();
 
     // 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))
-    {
-        return null();
-    }
-
-    YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
+    // 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 = GetYieldHandling(generatorKind, asyncKind);
     FunctionSyntaxKind syntaxKind = Statement;
     if (fun->isClassConstructor())
         syntaxKind = ClassConstructor;
     else if (fun->isMethod())
         syntaxKind = Method;
     else if (fun->isGetter())
         syntaxKind = Getter;
     else if (fun->isSetter())
@@ -3324,16 +3404,17 @@ Parser<ParseHandler>::functionFormalPara
 {
     // Given a properly initialized parse context, try to parse an actual
     // function without concern for conversion to strict mode, use of lazy
     // parsing and such.
 
     FunctionBox* funbox = pc->functionBox();
     RootedFunction fun(context, funbox->function());
 
+    AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, funbox->isAsync());
     if (!functionArguments(yieldHandling, kind, pn))
         return false;
 
     Maybe<ParseContext::VarScope> varScope;
     if (funbox->hasParameterExprs) {
         varScope.emplace(this);
         if (!varScope->init(pc))
             return false;
@@ -3352,17 +3433,17 @@ Parser<ParseHandler>::functionFormalPara
     }
 
     // Parse the function body.
     FunctionBodyType bodyType = StatementListBody;
     TokenKind tt;
     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);
             return false;
         }
 
         if (kind != Arrow) {
 #if JS_HAS_EXPR_CLOSURES
@@ -3382,19 +3463,17 @@ Parser<ParseHandler>::functionFormalPara
 #endif
     }
 
     // Arrow function parameters inherit yieldHandling from the enclosing
     // context, but the arrow body doesn't. E.g. in |(a = yield) => yield|,
     // |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;
 
     if ((kind != Method && !IsConstructorKind(kind)) && fun->name()) {
         RootedPropertyName propertyName(context, fun->name()->asPropertyName());
         if (!checkStrictBinding(propertyName, handler.getPosition(pn)))
             return false;
@@ -3430,17 +3509,18 @@ Parser<ParseHandler>::functionFormalPara
     handler.setEndPosition(pn, pos().end);
     handler.setFunctionBody(pn, body);
 
     return true;
 }
 
 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));
 
     // Annex B.3.4 says we can parse function declarations unbraced under if
     // or else as if it were braced. That is, |if (x) function f() {}| is
     // parsed as |if (x) { function f() {} }|.
     Maybe<ParseContext::Statement> synthesizedStmtForAnnexB;
     Maybe<ParseContext::Scope> synthesizedScopeForAnnexB;
@@ -3450,22 +3530,26 @@ Parser<ParseHandler>::functionStmt(Yield
             synthesizedStmtForAnnexB.emplace(pc, StatementKind::Block);
             synthesizedScopeForAnnexB.emplace(this);
             if (!synthesizedScopeForAnnexB->init(pc))
                 return null();
         }
     }
 
     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();
     }
 
     if (tt == TOK_NAME || tt == TOK_YIELD) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
@@ -3474,62 +3558,68 @@ Parser<ParseHandler>::functionStmt(Yield
         name = context->names().starDefaultStar;
         tokenStream.ungetToken();
     } else {
         /* Unnamed function expressions are forbidden in statement context. */
         report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
         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();
 
     if (synthesizedStmtForAnnexB) {
         Node synthesizedStmtList = handler.newStatementList(handler.getPosition(fun));
         if (!synthesizedStmtList)
             return null();
         handler.addStatementToList(synthesizedStmtList, fun);
         return finishLexicalScope(*synthesizedScopeForAnnexB, synthesizedStmtList);
     }
 
     return fun;
 }
 
 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) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else {
         tokenStream.ungetToken();
     }
 
-    return functionDefinition(InAllowed, yieldHandling, name, Expression, generatorKind, invoked);
+    return functionDefinition(InAllowed, yieldHandling, name, Expression, generatorKind,
+                              asyncKind, invoked);
 }
 
 /*
  * Return true if this node, known to be an unparenthesized string literal,
  * could be the string of a directive in a Directive Prologue. Directive
  * strings never contain escape sequences or line continuations.
  * isEscapeFreeStringLiteral, below, checks whether the node itself could be
  * a directive.
@@ -4133,26 +4223,32 @@ Parser<ParseHandler>::destructuringDecla
     if (!pattern || !checkDestructuringPattern(pattern, Some(kind), &possibleError))
         return null();
 
     return pattern;
 }
 
 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;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
 {
@@ -5004,16 +5100,30 @@ Parser<FullParseHandler>::exportDeclarat
                 return null();
             break;
           case TOK_CLASS:
             kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
             if (!kid)
                 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);
             if (!nameNode)
                 return null();
             if (!noteDeclaredName(name, DeclarationKind::Const, pos()))
                 return null();
             kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
@@ -5841,16 +5951,26 @@ Parser<ParseHandler>::newYieldExpression
         return null();
     if (isYieldStar)
         return handler.newYieldStarExpression(begin, expr, generator);
     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)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
     uint32_t begin = pos().begin;
 
     switch (pc->generatorKind()) {
       case StarGenerator:
       {
@@ -6351,16 +6471,17 @@ JSOpFromPropertyType(PropertyType propTy
       case PropertyType::GetterNoExpressionClosure:
         return JSOP_INITPROP_GETTER;
       case PropertyType::Setter:
       case PropertyType::SetterNoExpressionClosure:
         return JSOP_INITPROP_SETTER;
       case PropertyType::Normal:
       case PropertyType::Method:
       case PropertyType::GeneratorMethod:
+      case PropertyType::AsyncMethod:
       case PropertyType::Constructor:
       case PropertyType::DerivedConstructor:
         return JSOP_INITPROP;
       default:
         MOZ_CRASH("unexpected property type");
     }
 }
 
@@ -6372,32 +6493,44 @@ FunctionSyntaxKindFromPropertyType(Prope
         return Getter;
       case PropertyType::GetterNoExpressionClosure:
         return GetterNoExpressionClosure;
       case PropertyType::Setter:
         return Setter;
       case PropertyType::SetterNoExpressionClosure:
         return SetterNoExpressionClosure;
       case PropertyType::Method:
-        return Method;
       case PropertyType::GeneratorMethod:
+      case PropertyType::AsyncMethod:
         return Method;
       case PropertyType::Constructor:
         return ClassConstructor;
       case PropertyType::DerivedConstructor:
         return DerivedClassConstructor;
       default:
         MOZ_CRASH("unexpected property type");
     }
 }
 
 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>
 typename ParseHandler::Node
 Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
                                       ClassContext classContext,
                                       DefaultHandling defaultHandling)
 {
@@ -6501,16 +6634,17 @@ Parser<ParseHandler>::classDefinition(Yi
 
         PropertyType propType;
         Node propName = propertyName(yieldHandling, classMethods, &propType, &propAtom);
         if (!propName)
             return null();
 
         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);
             return null();
         }
 
         if (propType == PropertyType::Getter)
             propType = PropertyType::GetterNoExpressionClosure;
@@ -6933,16 +7067,26 @@ Parser<ParseHandler>::statementListItem(
 
         if (!tokenStream.currentToken().nameContainsEscape() &&
             tokenStream.currentName() == context->names().let &&
             nextTokenContinuesLetDeclaration(next, 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);
 
         return expressionStatement(yieldHandling);
       }
 
       case TOK_NEW:
         return expressionStatement(yieldHandling, PredictInvoked);
@@ -7423,27 +7567,63 @@ Parser<ParseHandler>::assignExpr(InHandl
             return null();
         if (endsExpr)
             return stringLiteral();
     }
 
     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
     // rewind.
     TokenStream::Position start(keepAtoms);
     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;
     switch (tokenStream.currentToken().type) {
       case TOK_ASSIGN:       kind = PNK_ASSIGN;       op = JSOP_NOP;    break;
       case TOK_ADDASSIGN:    kind = PNK_ADDASSIGN;    op = JSOP_ADD;    break;
       case TOK_SUBASSIGN:    kind = PNK_SUBASSIGN;    op = JSOP_SUB;    break;
       case TOK_BITORASSIGN:  kind = PNK_BITORASSIGN;  op = JSOP_BITOR;  break;
@@ -7475,22 +7655,43 @@ Parser<ParseHandler>::assignExpr(InHandl
         bool isBlock = false;
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
         if (next == TOK_LC)
             isBlock = true;
 
         tokenStream.seek(start);
 
-        TokenKind ignored;
-        if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
-            return null();
+        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();
 
         if (isBlock) {
             // This arrow function could be a non-trailing member of a comma
             // expression or a semicolon terminating a full expression.  If so,
             // the next token is that comma/semicolon, gotten with None:
             //
@@ -7509,16 +7710,17 @@ Parser<ParseHandler>::assignExpr(InHandl
             // before Parser::expr() looks for a comma.  Do so here, then
             // immediately add the modifier exception needed for the first
             // case.
             //
             // Note that the second case occurs *only* if the arrow function
             // 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);
         }
         return arrowFunc;
       }
 
       default:
@@ -7749,16 +7951,31 @@ Parser<ParseHandler>::unaryExpr(YieldHan
             if (!report(ParseStrictError, pc->sc()->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND))
                 return null();
             pc->sc()->setBindingsAccessedDynamically();
         }
 
         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);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (!tokenStream.peekTokenSameLine(&tt))
@@ -7806,23 +8023,23 @@ Parser<ParseHandler>::generatorComprehen
     // be necessary.
     RootedObject proto(context);
     JSContext* cx = context->maybeJSContext();
     proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
     if (!proto)
         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();
     genFunbox->isGenexpLambda = true;
     genFunbox->initWithEnclosingParseContext(outerpc, Expression);
 
     ParseContext genpc(this, genFunbox, /* newDirectives = */ nullptr);
     if (!genpc.init())
@@ -8078,24 +8295,30 @@ Parser<ParseHandler>::generatorComprehen
     handler.setBeginPosition(result, begin);
     handler.setEndPosition(result, pos().end);
 
     return result;
 }
 
 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;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread)
 {
@@ -8659,22 +8882,41 @@ Parser<ParseHandler>::propertyName(Yield
 {
     TokenKind ltok;
     if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
         return null();
 
     MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC");
 
     bool isGenerator = false;
+    bool isAsync = false;
     if (ltok == TOK_MUL) {
         isGenerator = true;
         if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
             return null();
     }
 
+    if (ltok == TOK_NAME && tokenStream.currentName() == context->names().async) {
+        TokenKind tt;
+        if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+            return null();
+        if (tt != TOK_LP && tt != TOK_COLON && tt != TOK_RC && tt != TOK_ASSIGN) {
+            isAsync = true;
+            ltok = tt;
+        } else {
+            tokenStream.ungetToken();
+            tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
+        }
+    }
+
+    if (isAsync && isGenerator) {
+        report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
+        return null();
+    }
+
     propAtom.set(nullptr);
     Node propName;
     switch (ltok) {
       case TOK_NUMBER:
         propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number()));
         if (!propAtom.get())
             return null();
         propName = newNumber(tokenStream.currentToken());
@@ -8686,17 +8928,17 @@ Parser<ParseHandler>::propertyName(Yield
         propName = computedPropertyName(yieldHandling, propList);
         if (!propName)
             return null();
         break;
 
       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))
         {
             propName = handler.newObjectLiteralPropertyName(propAtom, pos());
             if (!propName)
                 return null();
             break;
         }
@@ -8805,17 +9047,22 @@ Parser<ParseHandler>::propertyName(Yield
         *propType = tt == TOK_ASSIGN ?
                           PropertyType::CoverInitializedName :
                           PropertyType::Shorthand;
         return propName;
     }
 
     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;
     }
 
     report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
     return null();
 }
 
 template <typename ParseHandler>
@@ -9037,18 +9284,19 @@ Parser<ParseHandler>::objectLiteral(Yiel
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 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>
 bool
 Parser<ParseHandler>::tryNewTarget(Node &newTarget)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NEW));
 
@@ -9161,16 +9409,27 @@ Parser<ParseHandler>::primaryExpr(YieldH
       case TOK_NO_SUBS_TEMPLATE:
         return noSubstitutionTemplate();
 
       case TOK_STRING:
         return stringLiteral();
 
       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();
 
         return identifierReference(name);
       }
 
       case TOK_REGEXP:
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -310,16 +310,21 @@ class ParseContext : public Nestable<Par
     bool superScopeNeedsHomeObject_;
 
   public:
     // lastYieldOffset stores the offset of the last yield that was parsed.
     // NoYieldOffset is its initial value.
     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;
 
     // In a function context, points to a Directive struct that can be updated
     // to reflect new directives encountered in the Directive Prologue that
     // require reparsing the function. In global/module/generator-tail contexts,
     // we don't need to reparse when encountering a DirectivePrologue so this
     // pointer may be nullptr.
@@ -353,16 +358,17 @@ class ParseContext : public Nestable<Par
         varScope_(nullptr),
         innerFunctionBoxesForAnnexB_(prs->context->frontendCollectionPool()),
         positionalFormalParameterNames_(prs->context->frontendCollectionPool()),
         closedOverBindingsForLazy_(prs->context->frontendCollectionPool()),
         scriptId_(prs->usedNames.nextScriptId()),
         isStandaloneFunctionBody_(false),
         superScopeNeedsHomeObject_(false),
         lastYieldOffset(NoYieldOffset),
+        lastAwaitOffset(NoAwaitOffset),
         innerFunctionsForLazy(prs->context, GCVector<JSFunction*, 8>(prs->context)),
         newDirectives(newDirectives),
         funHasReturnExpr(false),
         funHasReturnVoid(false)
     {
         if (isFunctionBox()) {
             if (functionBox()->function()->isNamedLambda())
                 namedLambdaScope_.emplace(prs);
@@ -493,16 +499,24 @@ class ParseContext : public Nestable<Par
     bool isLegacyGenerator() const {
         return generatorKind() == LegacyGenerator;
     }
 
     bool isStarGenerator() const {
         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();
     }
 
     bool isMethod() const {
         return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isMethod();
     }
 
@@ -548,16 +562,17 @@ enum class PropertyType {
     Shorthand,
     CoverInitializedName,
     Getter,
     GetterNoExpressionClosure,
     Setter,
     SetterNoExpressionClosure,
     Method,
     GeneratorMethod,
+    AsyncMethod,
     Constructor,
     DerivedConstructor
 };
 
 // Specify a value for an ES6 grammar parametrization.  We have no enum for
 // [Return] because its behavior is exactly equivalent to checking whether
 // we're in a function box -- easier and simpler than passing an extra
 // parameter everywhere.
@@ -935,23 +950,25 @@ class Parser final : private JS::AutoGCR
     Node parse();
 
     /*
      * Allocate a new parsed object or function container from
      * cx->tempLifoAlloc.
      */
     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);
 
     bool hadAbortedSyntaxParse() {
         return abortedSyntaxParse;
     }
     void clearAbortedSyntaxParse() {
@@ -974,16 +991,17 @@ class Parser final : private JS::AutoGCR
     bool appendToCallSiteObj(Node callSiteObj);
     bool addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
                                         TokenKind* ttp);
     bool checkStatementsEOF();
 
     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();
 
   public:
     /* Public entry points for parsing. */
     Node statement(YieldHandling yieldHandling);
     Node statementListItem(YieldHandling yieldHandling, bool canHaveDirectives = false);
 
@@ -1000,39 +1018,41 @@ class Parser final : private JS::AutoGCR
     Node globalBody(GlobalSharedContext* globalsc);
 
     // Parse a module.
     Node moduleBody(ModuleSharedContext* modulesc);
 
     // 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.
     bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, InHandling inHandling,
                        YieldHandling yieldHandling, FunctionSyntaxKind kind,
                        Directives inheritedDirectives, Directives* newDirectives);
 
     // Parse a function's formal parameters and its body assuming its function
     // ParseContext is already on the stack.
     bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling,
                                          Node pn, FunctionSyntaxKind kind);
 
     // Determine whether |yield| is a valid name in the current context, or
     // 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
     // the given Yield parameter.  If there is no match, report a syntax
     // error.
     PropertyName* bindingIdentifier(YieldHandling yieldHandling);
 
     virtual bool strictMode() { return pc->sc()->strict(); }
@@ -1061,18 +1081,20 @@ class Parser final : private JS::AutoGCR
      * Parsers whose name has a '1' suffix leave the TokenStream state
      * pointing to the token one past the end of the parsed fragment.  For a
      * number of the parsers this is convenient and avoids a lot of
      * unnecessary ungetting and regetting of tokens.
      *
      * 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);
 
     Node blockStatement(YieldHandling yieldHandling,
                         unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
     Node doWhileStatement(YieldHandling yieldHandling);
     Node whileStatement(YieldHandling yieldHandling);
 
@@ -1167,17 +1189,17 @@ class Parser final : private JS::AutoGCR
                                       Node* forInOrOfExpression);
 
     Node expr(InHandling inHandling, YieldHandling yieldHandling,
               TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr,
               InvokedPrediction invoked = PredictUninvoked);
     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,
                    PossibleError* possibleError,
                    InvokedPrediction invoked = PredictUninvoked);
     Node orExpr1(InHandling inHandling, YieldHandling yieldHandling,
                  TripledotHandling tripledotHandling,
                  PossibleError* possibleError,
@@ -1202,17 +1224,18 @@ class Parser final : private JS::AutoGCR
 
     /*
      * Additional JS parsers.
      */
     bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
                            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
     // statements; pass ExpressionBody if the body is a single expression.
     enum FunctionBodyType { StatementListBody, ExpressionBody };
     Node functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
                       FunctionBodyType type);
 
@@ -1227,18 +1250,18 @@ class Parser final : private JS::AutoGCR
     Node comprehensionTail(GeneratorKind comprehensionKind);
     Node comprehension(GeneratorKind comprehensionKind);
     Node arrayComprehension(uint32_t begin);
     Node generatorComprehension(uint32_t begin);
 
     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);
     bool checkExportedNamesForDeclaration(Node node);
 
     enum ClassContext { ClassStatement, ClassExpression };
     Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
                          DefaultHandling defaultHandling);
@@ -1293,21 +1316,23 @@ class Parser final : private JS::AutoGCR
     Node newDotGeneratorName();
     bool declareDotGeneratorName();
 
     bool checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind,
                                  GeneratorKind generatorKind, bool* tryAnnexB);
     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();
     bool leaveInnerFunction(ParseContext* outerpc);
 
   public:
     enum FunctionCallBehavior {
         PermitAssignmentToFunctionCalls,
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -448,16 +448,18 @@ class FunctionBox : public ObjectBox, pu
     ParseNode*      functionNode;           /* back pointer used by asm.js for error messages */
     uint32_t        bufStart;
     uint32_t        bufEnd;
     uint32_t        startLine;
     uint32_t        startColumn;
     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 */
     bool            hasDirectEvalInParameterExpr:1; /* parameter list contains direct eval */
     bool            hasDuplicateParameters:1; /* parameter list contains duplicate names */
     bool            useAsm:1;               /* see useAsmOrInsideUseAsm */
     bool            insideUseAsm:1;         /* see useAsmOrInsideUseAsm */
     bool            isAnnexB:1;             /* need to emit a synthesized Annex B assignment */
@@ -468,17 +470,18 @@ class FunctionBox : public ObjectBox, pu
     bool            usesArguments:1;        /* contains a free use of 'arguments' */
     bool            usesApply:1;            /* contains an f.apply() call */
     bool            usesThis:1;             /* contains 'this' */
     bool            usesReturn:1;           /* contains a 'return' statement */
 
     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());
         return MutableHandle<LexicalScope::Data*>::fromMarkedLocation(&namedLambdaBindings_);
     }
 
     MutableHandle<FunctionScope::Data*> functionScopeBindings() {
         MOZ_ASSERT(context->compartment()->runtimeFromAnyThread()->keepAtoms());
@@ -527,16 +530,18 @@ class FunctionBox : public ObjectBox, pu
     bool isLikelyConstructorWrapper() const {
         return usesArguments && usesApply && usesThis && !usesReturn;
     }
 
     GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
     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) {
         // A generator kind can be set at initialization, or when "yield" is
         // first seen.  In both cases the transition can only happen from
         // NotGenerator.
         MOZ_ASSERT(!isGenerator());
         generatorKindBits_ = GeneratorKindAsBits(kind);
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -284,16 +284,17 @@ class SyntaxParseHandler
 
     MOZ_MUST_USE bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
     MOZ_MUST_USE bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
     MOZ_MUST_USE bool addShorthand(Node literal, Node name, Node expr) { return true; }
     MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
     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
 
     Node newStatementList(const TokenPos& pos) { return NodeGeneric; }
     void addStatementToList(Node list, Node stmt) {}
     void addCaseStatementToList(Node list, Node stmt) {}
     MOZ_MUST_USE bool prependInitialYield(Node stmtList, Node gen) { return true; }
     Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -102,16 +102,17 @@
     macro(NEW,          "keyword 'new'") \
     macro(DELETE,       "keyword 'delete'") \
     macro(TRY,          "keyword 'try'") \
     macro(CATCH,        "keyword 'catch'") \
     macro(FINALLY,      "keyword 'finally'") \
     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'") \
     macro(EXTENDS,      "keyword 'extends'") \
     macro(SUPER,        "keyword 'super'") \
     macro(RESERVED,     "reserved keyword") \
     /* reserved keywords in strict mode */ \
     macro(STRICT_RESERVED, "reserved keyword") \
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -960,16 +960,22 @@ TokenStream::putIdentInTokenbuf(const ch
     }
     userbuf.setAddressOfNextRawChar(tmp);
     return true;
 }
 
 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);
 
     if (kw->tokentype == TOK_STRICT_RESERVED)
         return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
 
     // Working keyword.
     *ttp = kw->tokentype;
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -27,16 +27,18 @@
 #include "js/Vector.h"
 #include "vm/RegExpObject.h"
 
 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.
 
     TokenPos() {}
     TokenPos(uint32_t begin, uint32_t end) : begin(begin), end(end) {}
 
     // Return a TokenPos that covers left, right, and anything in between.
@@ -425,16 +427,19 @@ class MOZ_STACK_CLASS TokenStream
                                 // token.
         bool hitOOM:1;          // Hit OOM.
 
         Flags()
           : isEOF(), isDirtyLine(), sawOctalEscape(), hadError(), hitOOM()
         {}
     };
 
+    bool awaitIsKeyword = false;
+    friend class AutoAwaitIsKeyword;
+
   public:
     typedef Token::Modifier Modifier;
     static constexpr Modifier None = Token::None;
     static constexpr Modifier Operand = Token::Operand;
     static constexpr Modifier KeywordIsName = Token::KeywordIsName;
     static constexpr Modifier TemplateTail = Token::TemplateTail;
 
     typedef Token::ModifierException ModifierException;
@@ -1010,16 +1015,35 @@ class MOZ_STACK_CLASS TokenStream
     UniqueTwoByteChars  sourceMapURL_;      // source map's filename or null
     CharBuffer          tokenbuf;           // current token string buffer
     uint8_t             isExprEnding[TOK_LIMIT];// which tokens definitely terminate exprs?
     ExclusiveContext*   const cx;
     bool                mutedErrors;
     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);
 
 } // namespace frontend
 } // namespace js
 
 extern JS_FRIEND_API(int)
 js_fgets(char* buf, int size, FILE* file);
--- a/js/src/jit-test/lib/syntax.js
+++ b/js/src/jit-test/lib/syntax.js
@@ -1176,17 +1176,17 @@ function test_syntax(postfixes, check_er
   test("for each (var x in y) ");
 
   test("for each (let ");
   test("for each (let x ");
   test("for each (let x in ");
   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 ");
   test("(function() { 'use asm'; var a ");
   test("(function() { 'use asm'; var a = ");
   test("(function() { 'use asm'; var a = 1 ");
   test("(function() { 'use asm'; var a = 1; ");
   test("(function() { 'use asm'; var a = 1; function ");
@@ -1203,9 +1203,154 @@ function test_syntax(postfixes, check_er
   test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f] ");
   test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; ");
   test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return ");
   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; } ");
   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) ");
 }
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2745,25 +2745,24 @@ BaselineCompiler::emit_JSOP_DEFLET()
 
 typedef bool (*DefFunOperationFn)(JSContext*, HandleScript, HandleObject, HandleFunction);
 static const VMFunction DefFunOperationInfo =
     FunctionInfo<DefFunOperationFn>(DefFunOperation, "DefFunOperation");
 
 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);
 }
 
 typedef bool (*InitPropGetterSetterFn)(JSContext*, jsbytecode*, HandleObject, HandlePropertyName,
                                        HandleObject);
 static const VMFunction InitPropGetterSetterInfo =
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2429,17 +2429,18 @@ CodeGenerator::visitLambda(LLambda* lir)
 
     MOZ_ASSERT(!info.singletonType);
 
     masm.createGCObject(output, tempReg, info.fun, gc::DefaultHeap, ool->entry());
 
     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)));
     }
 
     masm.bind(ool->rejoin());
 }
 
@@ -4689,17 +4690,18 @@ typedef bool (*DefFunOperationFn)(JSCont
 static const VMFunction DefFunOperationInfo =
     FunctionInfo<DefFunOperationFn>(DefFunOperation, "DefFunOperation");
 
 void
 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()));
 
     callVM(DefFunOperationInfo, lir);
 }
 
 typedef bool (*CheckOverRecursedFn)(JSContext*);
 static const VMFunction CheckOverRecursedInfo =
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -13437,23 +13437,19 @@ IonBuilder::jsop_deflexical(uint32_t ind
     current->add(deflex);
 
     return resumeAfter(deflex);
 }
 
 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);
 }
 
 bool
 IonBuilder::jsop_throwsetconst()
 {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -202,17 +202,21 @@ LIRGenerator::visitDefLexical(MDefLexica
     LDefLexical* lir = new(alloc()) LDefLexical();
     add(lir, ins);
     assignSafepoint(lir, 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);
 }
 
 void
 LIRGenerator::visitNewArray(MNewArray* ins)
 {
     LNewArray* lir = new(alloc()) LNewArray(temp());
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8101,41 +8101,32 @@ class MDefLexical
         return attrs_;
     }
     bool appendRoots(MRootList& roots) const override {
         return roots.append(name_);
     }
 };
 
 class MDefFun
-  : public MUnaryInstruction,
-    public NoTypePolicy::Data
-{
-    CompilerFunction fun_;
-
+  : public MBinaryInstruction,
+    public ObjectPolicy<0>::Data
+{
   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))
-
-    JSFunction* fun() const {
-        return fun_;
-    }
+    NAMED_OPERANDS((0, fun), (1, environmentChain))
+
     bool possiblyCalls() const override {
         return true;
     }
-    bool appendRoots(MRootList& roots) const override {
-        return roots.append(fun_);
-    }
 };
 
 class MRegExp : public MNullaryInstruction
 {
     CompilerGCPointer<RegExpObject*> source_;
     bool mustClone_;
 
     MRegExp(CompilerConstraintList* constraints, RegExpObject* source)
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1545,28 +1545,32 @@ class LDefLexical : public LCallInstruct
   public:
     LIR_HEADER(DefLexical)
 
     MDefLexical* mir() const {
         return mir_->toDefLexical();
     }
 };
 
-class LDefFun : public LCallInstructionHelper<0, 1, 0>
+class LDefFun : public LCallInstructionHelper<0, 2, 0>
 {
   public:
     LIR_HEADER(DefFun)
 
-    explicit LDefFun(const LAllocation& envChain)
-    {
-        setOperand(0, envChain);
-    }
-
+    LDefFun(const LAllocation& fun, const LAllocation& envChain)
+    {
+        setOperand(0, fun);
+        setOperand(1, envChain);
+    }
+
+    const LAllocation* fun() {
+        return getOperand(0);
+    }
     const LAllocation* environmentChain() {
-        return getOperand(0);
+        return getOperand(1);
     }
     MDefFun* mir() const {
         return mir_->toDefFun();
     }
 };
 
 class LTypeOfV : public LInstructionHelper<1, BOX_PIECES, 1>
 {
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -176,16 +176,18 @@ MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION,      1
 MSG_DEF(JSMSG_UNKNOWN_FORMAT,          1, JSEXN_INTERNALERR, "unknown bytecode format {0}")
 
 // Frontend
 MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS,     3, JSEXN_SYNTAXERR, "{0} functions must have {1} argument{2}")
 MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE,     0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
 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")
 MSG_DEF(JSMSG_BAD_CONST_DECL,          0, JSEXN_SYNTAXERR, "missing = in const declaration")
 MSG_DEF(JSMSG_BAD_CONTINUE,            0, JSEXN_SYNTAXERR, "continue must be inside loop")
 MSG_DEF(JSMSG_BAD_DESTRUCT_ASS,        0, JSEXN_REFERENCEERR, "invalid destructuring assignment operator")
 MSG_DEF(JSMSG_BAD_DESTRUCT_TARGET,     0, JSEXN_SYNTAXERR, "invalid destructuring target")
 MSG_DEF(JSMSG_BAD_DESTRUCT_PARENS,     0, JSEXN_SYNTAXERR, "destructuring patterns in assignments can't be parenthesized")
@@ -257,16 +259,17 @@ MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT,     2
 MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER,    0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
 MSG_DEF(JSMSG_ILLEGAL_CHARACTER,       0, JSEXN_SYNTAXERR, "illegal character")
 MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
 MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers")
 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")
 MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW,  0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
 MSG_DEF(JSMSG_LINE_BREAK_BEFORE_ARROW, 0, JSEXN_SYNTAXERR, "no line break is allowed before '=>'")
 MSG_DEF(JSMSG_MALFORMED_ESCAPE,        1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
 MSG_DEF(JSMSG_MISSING_BINARY_DIGITS,   0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
 MSG_DEF(JSMSG_MISSING_EXPONENT,        0, JSEXN_SYNTAXERR, "missing exponent")
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -34,19 +34,21 @@
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #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"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsscriptinlines.h"
 
@@ -980,44 +982,59 @@ js::FunctionToString(JSContext* cx, Hand
     if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
         return nullptr;
 
     if (IsAsmJSModule(fun))
         return AsmJSModuleToString(cx, fun, !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);
 
     if (fun->hasScript()) {
         script = fun->nonLazyScript();
         if (script->isGeneratorExp()) {
             if (!out.append("function genexp() {") ||
                 !out.append("\n    [generator expression]\n") ||
                 !out.append("}"))
             {
                 return nullptr;
             }
             return out.finishString();
         }
     }
 
+    if (fun->isAsync()) {
+        if (!out.append("async "))
+            return nullptr;
+    }
+
     bool funIsMethodOrNonArrowLambda = (fun->isLambda() && !fun->isArrow()) || fun->isMethod() ||
                                         fun->isGetter() || fun->isSetter();
 
     // If we're not in pretty mode, put parentheses around lambda functions and methods.
     if (fun->isInterpreted() && !lambdaParen && funIsMethodOrNonArrowLambda &&
         !fun->isSelfHostedBuiltin())
     {
         if (!out.append("("))
             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()) {
         if (!out.append(fun->name()))
             return nullptr;
     }
 
     bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin();
@@ -1653,40 +1670,46 @@ const JSFunctionSpec js::function_method
     JS_FN(js_call_str,       fun_call,       1,0),
     JS_FN("isGenerator",     fun_isGenerator,0,0),
     JS_SELF_HOSTED_FN("bind", "FunctionBind", 2, JSFUN_HAS_REST),
     JS_SYM_FN(hasInstance, fun_symbolHasInstance, 1, JSPROP_READONLY | JSPROP_PERMANENT),
     JS_FS_END
 };
 
 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);
 
     /* Block this call if security callbacks forbid it. */
     Rooted<GlobalObject*> global(cx, &args.callee().global());
     if (!GlobalObject::isRuntimeCodeGenEnabled(cx, global)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_FUNCTION);
         return false;
     }
 
     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;
     unsigned lineno;
     bool mutedErrors;
     uint32_t pcOffset;
     DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
                                          &mutedErrors);
 
     const char* introductionType = "Function";
-    if (generatorKind != NotGenerator)
+    if (isAsync)
+        introductionType = "AsyncFunction";
+    else if (generatorKind != NotGenerator)
         introductionType = "GeneratorFunction";
 
     const char* introducerFilename = filename;
     if (maybeScript && maybeScript->scriptSource()->introducerFilename())
         introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
     CompileOptions options(cx);
     options.setMutedErrors(mutedErrors)
@@ -1770,29 +1793,32 @@ FunctionConstructor(JSContext* cx, unsig
      * NB: (new Function) is not lexically closed by its caller, it's just an
      * anonymous function in the top-level scope that its constructor inhabits.
      * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
      * and so would a call to f from another top-level's script or function.
      */
     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;
     } else {
         if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
             return false;
     }
 
     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;
 
     if (!JSFunction::setTypeForScriptedFunction(cx, fun))
         return false;
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, bodyText))
@@ -1870,34 +1896,49 @@ FunctionConstructor(JSContext* cx, unsig
         fun->setHasRest();
 
     mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
     SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
                                               ? SourceBufferHolder::GiveOwnership
                                               : 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);
     args.rval().setObject(*fun);
     return ok;
 }
 
 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
 JSFunction::isBuiltinFunctionConstructor()
 {
     return maybeNative() == Function || maybeNative() == Generator;
 }
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -296,16 +296,23 @@ class JSFunction : public js::NativeObje
     void setResolvedLength() {
         flags_ |= RESOLVED_LENGTH;
     }
 
     void setResolvedName() {
         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);
 
     JSAtom* name() const { return hasGuessedAtom() ? nullptr : atom_.get(); }
 
     // Because display names (see Debugger.Object.displayName) are already stored
     // on functions and will always contain a valid es6 function name, as described
@@ -464,16 +471,28 @@ class JSFunction : public js::NativeObje
     }
 
     bool isGenerator() const { return generatorKind() != js::NotGenerator; }
 
     bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
 
     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_;
     }
 
     void initScript(JSScript* script_) {
         mutableScript().init(script_);
     }
 
@@ -596,16 +615,19 @@ fun_toStringHelper(JSContext* cx, js::Ha
 namespace js {
 
 extern bool
 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*
 NewNativeFunction(ExclusiveContext* cx, JSNative native, unsigned nargs, HandleAtom atom,
                   gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
                   NewObjectKind newKind = SingletonObject);
 
 // Allocate a new constructor backed by a JSNative.  Note that by default this
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2610,16 +2610,17 @@ JSScript::initFromFunctionBox(ExclusiveC
 
     script->functionHasThisBinding_ = funbox->hasThisBinding();
     script->functionHasExtraBodyVarScope_ = funbox->hasExtraBodyVarScope();
 
     script->funLength_ = funbox->length;
 
     script->isGeneratorExp_ = funbox->isGenexpLambda;
     script->setGeneratorKind(funbox->generatorKind());
+    script->setAsyncKind(funbox->asyncKind());
 
     PositionalFormalParameterIter fi(script);
     while (fi && !fi.closedOver())
         fi++;
     script->funHasAnyAliasedFormal_ = !!fi;
 
     script->setHasInnerFunctions(funbox->hasInnerFunctions());
 }
@@ -3099,16 +3100,18 @@ Rebase(JSScript* dst, JSScript* src, T* 
 {
     size_t off = reinterpret_cast<uint8_t*>(srcp) - src->data;
     return reinterpret_cast<T*>(dst->data + off);
 }
 
 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()) {
         cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
         if (!cloneProto)
             return nullptr;
     }
 
@@ -3267,16 +3270,17 @@ js::detail::CopyScript(JSContext* cx, Ha
     dst->hasSingletons_ = src->hasSingletons();
     dst->treatAsRunOnce_ = src->treatAsRunOnce();
     dst->hasInnerFunctions_ = src->hasInnerFunctions();
     dst->isGeneratorExp_ = src->isGeneratorExp();
     dst->setGeneratorKind(src->generatorKind());
     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);
         dst->consts()->vector = vector;
         for (unsigned i = 0; i < nconsts; ++i)
             MOZ_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom());
     }
     if (nobjects != 0) {
@@ -3999,16 +4003,17 @@ LazyScript::Create(ExclusiveContext* cx,
     union {
         PackedView p;
         uint64_t packedFields;
     };
 
     p.version = version;
     p.shouldDeclareArguments = false;
     p.hasThisBinding = false;
+    p.isAsync = false;
     p.numClosedOverBindings = closedOverBindings.length();
     p.numInnerFunctions = innerFunctions.length();
     p.generatorKindBits = GeneratorKindAsBits(NotGenerator);
     p.strict = false;
     p.bindingsAccessedDynamically = false;
     p.hasDebuggerStatement = false;
     p.hasDirectEval = false;
     p.isLikelyConstructorWrapper = false;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -635,28 +635,40 @@ class ScriptSourceObject : public Native
     static const uint32_t SOURCE_SLOT = 0;
     static const uint32_t ELEMENT_SLOT = 1;
     static const uint32_t ELEMENT_PROPERTY_SLOT = 2;
     static const uint32_t INTRODUCTION_SCRIPT_SLOT = 3;
     static const uint32_t RESERVED_SLOTS = 4;
 };
 
 enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
+enum FunctionAsyncKind { SyncFunction, AsyncFunction };
 
 static inline unsigned
 GeneratorKindAsBits(GeneratorKind generatorKind) {
     return static_cast<unsigned>(generatorKind);
 }
 
 static inline GeneratorKind
 GeneratorKindFromBits(unsigned val) {
     MOZ_ASSERT(val <= StarGenerator);
     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
  * CallNewScriptHook.
  */
 template<XDRMode mode>
 bool
 XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
@@ -983,16 +995,18 @@ class JSScript : public js::gc::TenuredC
     // script.
     bool hasInnerFunctions_:1;
 
     bool needsHomeObject_:1;
 
     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:
 #if JS_BITS_PER_WORD == 32
     // Currently no padding is needed.
 #endif
 
     //
@@ -1271,16 +1285,24 @@ class JSScript : public js::gc::TenuredC
     bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
     void setGeneratorKind(js::GeneratorKind kind) {
         // A script only gets its generator kind set as part of initialization,
         // so it can only transition from not being a generator.
         MOZ_ASSERT(!isGenerator());
         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;
     }
     bool needsHomeObject() const {
         return needsHomeObject_;
     }
 
     bool isDerivedClassConstructor() const {
@@ -1882,17 +1904,18 @@ class LazyScript : public gc::TenuredCel
 
   private:
     struct PackedView {
         // Assorted bits that should really be in ScriptSourceObject.
         uint32_t version : 8;
 
         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;
 
         // N.B. These are booleans but need to be uint32_t to pack correctly on MSVC.
         // If you add another boolean here, make sure to initialze it in
         // LazyScript::CreateRaw().
         uint32_t strict : 1;
@@ -2019,16 +2042,24 @@ class LazyScript : public gc::TenuredCel
         // A script only gets its generator kind set as part of initialization,
         // so it can only transition from NotGenerator.
         MOZ_ASSERT(!isGenerator());
         // Legacy generators cannot currently be lazy.
         MOZ_ASSERT(kind != LegacyGenerator);
         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;
     }
     void setStrict() {
         p_.strict = true;
     }
 
     bool bindingsAccessedDynamically() const {
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -319,16 +319,17 @@ UNIFIED_SOURCES += [
     'proxy/OpaqueCrossCompartmentWrapper.cpp',
     'proxy/Proxy.cpp',
     'proxy/ScriptedProxyHandler.cpp',
     'proxy/SecurityWrapper.cpp',
     'proxy/Wrapper.cpp',
     'threading/Mutex.cpp',
     'vm/ArgumentsObject.cpp',
     'vm/ArrayBufferObject.cpp',
+    'vm/AsyncFunction.cpp',
     'vm/Caches.cpp',
     'vm/CallNonGenericMethod.cpp',
     'vm/CharacterEncoding.cpp',
     'vm/CodeCoverage.cpp',
     'vm/Compression.cpp',
     'vm/DateTime.cpp',
     'vm/Debugger.cpp',
     'vm/DebuggerMemory.cpp',
@@ -744,16 +745,17 @@ GENERATED_FILES += [('selfhosted.out.h',
 selfhosted = GENERATED_FILES[('selfhosted.out.h', 'selfhosted.js')]
 selfhosted.script = 'builtin/embedjs.py:generate_selfhosted'
 selfhosted.inputs = [
     'js.msg',
     'builtin/TypedObjectConstants.h',
     'builtin/SelfHostingDefines.h',
     'builtin/Utilities.js',
     'builtin/Array.js',
+    'builtin/AsyncFunctions.js',
     'builtin/Classes.js',
     'builtin/Date.js',
     'builtin/Error.js',
     'builtin/Function.js',
     'builtin/Generator.js',
     'builtin/Intl.js',
     'builtin/IntlData.js',
     'builtin/IntlTzData.js',
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Object/defineProperties-callable-accessor.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);
--- a/js/src/tests/ecma_6/Symbol/toStringTag.js
+++ b/js/src/tests/ecma_6/Symbol/toStringTag.js
@@ -144,9 +144,12 @@ testDefault(JSON, "JSON");
 testDefault(function* () {}.constructor.prototype, "GeneratorFunction");
 
 // ES6 25.3.1.5 Generator.prototype [ @@toStringTag ]
 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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/BoundNames.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/EarlyErrors.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/arguments_callee.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/constructor.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/identity.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/length.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/methods.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/property.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/semantics.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/shell.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/syntax-arrow.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/syntax-modules.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/syntax.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/toString.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/yield.js
@@ -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);
--- a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js
+++ b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js
@@ -42,16 +42,26 @@ function genFunDecl(style, id, params, b
     return Pattern({ type: "FunctionDeclaration",
                      id: id,
                      params: params,
                      defaults: [],
                      body: 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" });
 }
 function letDecl(decls) {
     return Pattern({ type: "VariableDeclaration", declarations: decls, kind: "let" });
 }
 function constDecl(decls) {
     return Pattern({ type: "VariableDeclaration", declarations: decls, kind: "const" });
@@ -161,21 +171,39 @@ function funExpr(id, args, body, gen) {
 function genFunExpr(style, id, args, body) {
     return Pattern({ type: "FunctionExpression",
                      id: id,
                      params: args,
                      body: 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",
                      meta: meta,
                      property: property });
 }
 function newTarget() {
     return metaProperty(ident("new"), ident("target"));
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/reflect-parse/async.js
@@ -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);
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -4,16 +4,17 @@
  * 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/ArgumentsObject-inl.h"
 
 #include "mozilla/PodOperations.h"
 
 #include "jit/JitFrames.h"
+#include "vm/AsyncFunction.h"
 #include "vm/GlobalObject.h"
 #include "vm/Stack.h"
 
 #include "jsobjinlines.h"
 
 #include "gc/Nursery-inl.h"
 #include "vm/Stack-inl.h"
 
@@ -441,18 +442,23 @@ MappedArgGetter(JSContext* cx, HandleObj
         unsigned arg = unsigned(JSID_TO_INT(id));
         if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg))
             vp.set(argsobj.element(arg));
     } else if (JSID_IS_ATOM(id, cx->names().length)) {
         if (!argsobj.hasOverriddenLength())
             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;
 }
 
 static bool
 MappedArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
                 ObjectOpResult& result)
 {
new file mode 100644
--- /dev/null
+++ b/js/src/vm/AsyncFunction.cpp
@@ -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;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/AsyncFunction.h
@@ -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 */
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -24,16 +24,21 @@
     macro(ArraySpecies, ArraySpecies, "ArraySpecies") \
     macro(ArraySpeciesCreate, ArraySpeciesCreate, "ArraySpeciesCreate") \
     macro(ArrayToLocaleString, ArrayToLocaleString, "ArrayToLocaleString") \
     macro(ArrayType, ArrayType, "ArrayType") \
     macro(ArrayValues, ArrayValues, "ArrayValues") \
     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") \
     macro(Bool64x2, Bool64x2, "Bool64x2") \
     macro(boundWithSpace, boundWithSpace, "bound ") \
     macro(breakdown, breakdown, "breakdown") \
     macro(buffer, buffer, "buffer") \
     macro(builder, builder, "builder") \
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -12,16 +12,17 @@
 
 #include "jscompartment.h"
 #include "jsiter.h"
 
 #include "builtin/ModuleObject.h"
 #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"
 #include "vm/Xdr.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
@@ -1018,17 +1019,19 @@ const Class LexicalEnvironmentObject::cl
     JSCLASS_IS_ANONYMOUS,
     JS_NULL_CLASS_OPS,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     JS_NULL_OBJECT_OPS
 };
 
 /* 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());
     RootedScope scope(cx, callee->nonLazyScript()->maybeNamedLambdaScope());
     MOZ_ASSERT(scope && scope->environmentShape());
     MOZ_ASSERT(scope->environmentShape()->slot() == lambdaSlot());
     MOZ_ASSERT(!scope->environmentShape()->writable());
 
@@ -1040,32 +1043,40 @@ NamedLambdaObject::create(JSContext* cx,
 #endif
 
     LexicalEnvironmentObject* obj =
         LexicalEnvironmentObject::createTemplateObject(cx, scope.as<LexicalScope>(),
                                                        enclosing, heap);
     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*
 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
 NamedLambdaObject::lambdaSlot()
 {
     // Named lambda environments have exactly one name.
     return JSSLOT_FREE(&LexicalEnvironmentObject::class_);
 }
@@ -3360,17 +3371,25 @@ js::InitFunctionEnvironmentObjects(JSCon
 {
     MOZ_ASSERT(frame.isFunctionFrame());
     MOZ_ASSERT(frame.callee()->needsSomeEnvironmentObject());
 
     RootedFunction callee(cx, frame.callee());
 
     // 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);
     }
 
     // If the function has parameter default expressions, there may be an
     // extra environment to hold the parameters.
     if (callee->needsCallObject()) {
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -510,23 +510,26 @@ class LexicalEnvironmentObject : public 
     // For extensible lexical environments, the 'this' value for its
     // scope. Otherwise asserts.
     Value thisValue() const;
 };
 
 class NamedLambdaObject : public LexicalEnvironmentObject
 {
     static NamedLambdaObject* create(JSContext* cx, HandleFunction callee,
+                                     HandleFunction replacement,
                                      HandleObject enclosing, gc::InitialHeap heap);
 
   public:
     static NamedLambdaObject* createTemplateObject(JSContext* cx, HandleFunction callee,
                                                    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();
 };
 
 // A non-syntactic dynamic scope object that captures non-lexical
 // bindings. That is, a scope object that captures both qualified var
 // assignments and unqualified bareword assignments. Its parent is always the
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -255,18 +255,18 @@ static JSObject*
 NewSingletonObjectWithObjectPrototype(JSContext* cx, Handle<GlobalObject*> global)
 {
     RootedObject proto(cx, global->getOrCreateObjectPrototype(cx));
     if (!proto)
         return nullptr;
     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)
         return nullptr;
     return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
 }
 
 /* static */ bool
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -92,16 +92,18 @@ class GlobalObject : public NativeObject
         EMPTY_GLOBAL_SCOPE,
         ITERATOR_PROTO,
         ARRAY_ITERATOR_PROTO,
         STRING_ITERATOR_PROTO,
         LEGACY_GENERATOR_OBJECT_PROTO,
         STAR_GENERATOR_OBJECT_PROTO,
         STAR_GENERATOR_FUNCTION_PROTO,
         STAR_GENERATOR_FUNCTION,
+        ASYNC_FUNCTION_PROTO,
+        ASYNC_FUNCTION,
         MAP_ITERATOR_PROTO,
         SET_ITERATOR_PROTO,
         COLLATOR_PROTO,
         NUMBER_FORMAT_PROTO,
         DATE_TIME_FORMAT_PROTO,
         MODULE_PROTO,
         IMPORT_ENTRY_PROTO,
         EXPORT_ENTRY_PROTO,
@@ -571,16 +573,29 @@ class GlobalObject : public NativeObject
     }
 
     static JSObject* getOrCreateStarGeneratorFunction(JSContext* cx,
                                                       Handle<GlobalObject*> global)
     {
         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)
     {
         return global->getOrCreateObject(cx, MAP_ITERATOR_PROTO, initMapIteratorProto);
     }
 
     static JSObject* getOrCreateSetIteratorPrototype(JSContext* cx,
                                                      Handle<GlobalObject*> global)
@@ -720,16 +735,18 @@ class GlobalObject : public NativeObject
     static bool initIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initArrayIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
 
     // Implemented in vm/GeneratorObject.cpp.
     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);
 
     // Implemented in Intl.cpp.
     static bool initIntlObject(JSContext* cx, Handle<GlobalObject*> global);
 
     // Implemented in builtin/ModuleObject.cpp
@@ -980,16 +997,19 @@ inline JSProtoKey
 StandardProtoKeyOrNull(const JSObject* obj)
 {
     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
     if (key == JSProto_Error)
         return GetExceptionProtoKey(obj->as<ErrorObject>().type());
     return key;
 }
 
+JSObject*
+NewSingletonObjectWithFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global);
+
 } // namespace js
 
 template<>
 inline bool
 JSObject::is<js::GlobalObject>() const
 {
     return !!(getClass()->flags & JSCLASS_IS_GLOBAL);
 }
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3441,19 +3441,20 @@ END_CASE(JSOP_DEFLET)
 CASE(JSOP_DEFFUN)
 {
     /*
      * A top-level function defined in Global or Eval code (see ECMA-262
      * Ed. 3), or else a SpiderMonkey extension: a named function statement in
      * a compound statement (not at the top statement level of global code, or
      * at the top level of a function body).
      */
-    ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
+    ReservedRooted<JSFunction*> fun(&rootFunction0, &REGS.sp[-1].toObject().as<JSFunction>());
     if (!DefFunOperation(cx, script, REGS.fp()->environmentChain(), fun))
         goto error;
+    REGS.sp--;
 }
 END_CASE(JSOP_DEFFUN)
 
 CASE(JSOP_LAMBDA)
 {
     /* Load the specified function object literal. */
     ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
     JSObject* obj = Lambda(cx, fun, REGS.fp()->environmentChain());
@@ -4325,37 +4326,18 @@ js::LambdaArrow(JSContext* cx, HandleFun
     clone->as<JSFunction>().setExtendedSlot(0, newTargetv);
 
     MOZ_ASSERT(fun->global() == clone->global());
     return clone;
 }
 
 bool
 js::DefFunOperation(JSContext* cx, HandleScript script, HandleObject envChain,
-                    HandleFunction funArg)
-{
-    /*
-     * 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());
-    }
-
+                    HandleFunction fun)
+{
     /*
      * 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
      * and functions defined by eval inside let or with blocks.
      */
     RootedObject parent(cx, envChain);
     while (!parent->isQualifiedVarObj())
         parent = parent->enclosingEnvironment();
--- a/js/src/vm/Keywords.h
+++ b/js/src/vm/Keywords.h
@@ -50,16 +50,17 @@
     macro(enum, enum_, TOK_RESERVED) \
     /* Future reserved keywords, but only in strict mode. */ \
     macro(implements, implements, TOK_STRICT_RESERVED) \
     macro(interface, interface, TOK_STRICT_RESERVED) \
     macro(package, package, TOK_STRICT_RESERVED) \
     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 \
      * when strict.  Punt logic to parser. \
      */ \
     macro(yield, yield, TOK_YIELD)
 
 #endif /* vm_Keywords_h */
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1295,20 +1295,20 @@ 1234567890123456789012345678901234567890
     \
     /*
      * Defines the given function on the current scope.
      *
      * This is used for global scripts and also in some cases for function
      * 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
      * if a var binding with the same name exists on the global.
      *   Category: Variables and Scopes
      *   Type: Variables
      *   Operands: uint32_t nameIndex
      *   Stack: =>
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -33,16 +33,17 @@
 #include "builtin/TypedObject.h"
 #include "builtin/WeakSetObject.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #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"
 #include "vm/RegExpObject.h"
 #include "vm/String.h"
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayCommon.h"
 #include "vm/WrapperObject.h"
@@ -1826,16 +1827,33 @@ js::ReportIncompatibleSelfHostedMethod(J
         }
         ++iter;
     }
 
     MOZ_ASSERT_UNREACHABLE("How did we not find a useful self-hosted frame?");
     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.
  */
 static bool
 intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -2225,16 +2243,18 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("MakeDefaultConstructor",  intrinsic_MakeDefaultConstructor,  2,0),
     JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
     JS_FN("_NameForTypedArray",      intrinsic_NameForTypedArray, 1,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 3,0),
     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,
                     IntrinsicSubstringKernel),
     JS_INLINABLE_FN("_DefineDataProperty",              intrinsic_DefineDataProperty,      4,0,
                     IntrinsicDefineDataProperty),
     JS_INLINABLE_FN("ObjectHasPrototype",               intrinsic_ObjectHasPrototype,      2,0,
                     IntrinsicObjectHasPrototype),
@@ -2358,16 +2378,20 @@ static const JSFunctionSpec intrinsic_fu
           CallNonGenericSelfhostedMethod<Is<LegacyGeneratorObject>>, 2, 0),
     JS_FN("CallStarGeneratorMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 2, 0),
 
     JS_FN("IsWeakSet", intrinsic_IsInstanceOfBuiltin<WeakSetObject>, 1,0),
     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),
     JS_FN("TypedObjectBuffer",              TypedObject::GetBuffer, 1, 0),
     JS_FN("TypedObjectByteOffset",          TypedObject::GetByteOffset, 1, 0),
     JS_FN("AttachTypedObject",              js::AttachTypedObject, 3, 0),
     JS_FN("TypedObjectIsAttached",          js::TypedObjectIsAttached, 1, 0),
     JS_FN("TypedObjectTypeDescr",           js::TypedObjectTypeDescr, 1, 0),
--- a/mfbt/LinkedList.h
+++ b/mfbt/LinkedList.h
@@ -63,27 +63,75 @@
 
 #ifndef mozilla_LinkedList_h
 #define mozilla_LinkedList_h
 
 #include "mozilla/Assertions.h"
 #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
    * each linked list node (to keep track of whether |this| is the sentinel
    * node) and a branch on this value in getNext/getPrevious.
    *
    * We could get rid of the extra word of storage by shoving the "is
    * sentinel" bit into one of the pointers, although this would, of course,
@@ -150,42 +198,42 @@ public:
       remove();
     }
   }
 
   /*
    * 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);
   }
 
   /*
    * Insert aElem before this element in the list.  |this| must be part of a
    * linked list when you call setPrevious(); otherwise, this method will
    * assert.
    */
-  void setPrevious(T* aElem)
+  void setPrevious(RawType aElem)
   {
     MOZ_ASSERT(isInList());
     setPreviousUnsafe(aElem);
   }
 
   /*
    * Remove this element from the list which contains it.  If this element is
    * not currently part of a linked list, this method asserts.
@@ -193,16 +241,42 @@ public:
   void remove()
   {
     MOZ_ASSERT(isInList());
 
     mPrev->mNext = mNext;
     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;
   }
 
   /*
    * Identical to remove(), but also asserts in debug builds that this element
    * is in aList.
    */
   void removeFrom(const LinkedList<T>& aList)
   {
@@ -216,83 +290,92 @@ public:
   bool isInList() const
   {
     MOZ_ASSERT((mNext == this) == (mPrev == this));
     return mNext != this;
   }
 
 private:
   friend class LinkedList<T>;
+  friend struct detail::LinkedListElementTraits<T>;
 
   enum class NodeKind {
     Normal,
     Sentinel
   };
 
   explicit LinkedListElement(NodeKind nodeKind)
     : mNext(this),
       mPrev(this),
       mIsSentinel(nodeKind == NodeKind::Sentinel)
   { }
 
   /*
    * 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());
 
     listElem->mNext = this->mNext;
     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());
 
     listElem->mNext = this;
     listElem->mPrev = this->mPrev;
     this->mPrev->mNext = listElem;
     this->mPrev = listElem;
+
+    Traits::enterList(aElem);
   }
 
   /*
    * Adjust mNext and mPrev for implementing move constructor and move
    * assignment.
    */
   void adjustLinkForMove(LinkedListElement<T>&& aOther)
   {
     if (!aOther.isInList()) {
       mNext = this;
       mPrev = this;
       return;
     }
 
+    if (!mIsSentinel) {
+      Traits::enterList(this);
+    }
+
     MOZ_ASSERT(aOther.mNext->mPrev == &aOther);
     MOZ_ASSERT(aOther.mPrev->mNext == &aOther);
 
     /*
      * Initialize |this| with |aOther|'s mPrev/mNext pointers, and adjust those
      * element to point to this one.
      */
     mNext = aOther.mNext;
@@ -302,36 +385,46 @@ private:
     mPrev->mNext = this;
 
     /*
      * Adjust |aOther| so it doesn't think it's in a list.  This makes it
      * safely destructable.
      */
     aOther.mNext = &aOther;
     aOther.mPrev = &aOther;
+
+    if (!mIsSentinel) {
+      Traits::exitList(&aOther);
+    }
   }
 
   LinkedListElement& operator=(const LinkedListElement<T>& aOther) = delete;
   LinkedListElement(const LinkedListElement<T>& aOther) = delete;
 };
 
 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;
     }
 
     const Iterator& operator++() {
       mCurrent = mCurrent->getNext();
       return *this;
     }
 
@@ -358,64 +451,64 @@ public:
                "failing this assertion means this LinkedList's creator is "
                "buggy: it should have removed all this list's elements before "
                "the list's destruction");
   }
 
   /*
    * 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);
   }
 
   /*
    * Add aElem to the back of the list.
    */
-  void insertBack(T* aElem)
+  void insertBack(RawType aElem)
   {
     sentinel.setPreviousUnsafe(aElem);
   }
 
   /*
    * 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;
   }
 
   /*
    * 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;
   }
 
   /*
    * Return true if the list is empty, or false otherwise.
    */
   bool isEmpty() const
@@ -526,20 +619,20 @@ public:
         cur = cur->mNext;
     } while (cur != &sentinel);
 #endif /* ifdef DEBUG */
   }
 
 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;
       }
     }
     MOZ_CRASH("element wasn't found in this list!");
 #endif
   }
 
--- a/mfbt/tests/TestLinkedList.cpp
+++ b/mfbt/tests/TestLinkedList.cpp
@@ -151,16 +151,34 @@ TestMove()
   LinkedList<SomeClass> list3;
   list3 = Move(list2);
   { unsigned int check[] { 1, 2 }; CheckListValues(list3, check); }
   MOZ_RELEASE_ASSERT(list2.isEmpty());
 
   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>;
 };
 
 static void
 TestPrivate()
 {
@@ -172,16 +190,79 @@ TestPrivate()
   size_t count = 0;
   for (PrivateClass* p : list) {
     MOZ_RELEASE_ASSERT(p, "cannot have null elements in list");
     count++;
   }
   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;
 }
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -638,28 +638,29 @@ VerifyCertificate(CERTCertificate* signe
   const VerifyCertificateContext& context =
     *static_cast<const VerifyCertificateContext*>(voidContext);
 
   AppTrustDomain trustDomain(context.builtChain, pinArg);
   if (trustDomain.SetTrustedRoot(context.trustedRoot) != SECSuccess) {
     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));
   }
 
   rv = BuildCertChain(trustDomain, certDER, Now(),
                       EndEntityOrCA::MustBeEndEntity,
                       KeyUsage::digitalSignature,
                       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
     // the signing took place. The choice is either to ignore expiration
     // or to enforce expiration at time of use. The latter leads to the
     // user-hostile result that perfectly good code stops working.
     //
     // Our package format doesn't support timestamps (nor do we have a
--- a/security/certverifier/CTSerialization.cpp
+++ b/security/certverifier/CTSerialization.cpp
@@ -11,16 +11,18 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/Move.h"
 #include "mozilla/TypeTraits.h"
 
 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;
 
 // Members of a V1 SCT
 static const size_t kLogIdLength = 32;
 static const size_t kTimestampLength = 8;
 static const size_t kExtensionsLengthBytes = 2;
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -21,16 +21,18 @@ namespace mozilla { namespace ct {
 // headers and force us to export them in moz.build. Just forward-declare
 // the class here instead.
 class MultiLogCTVerifier;
 
 } } // namespace mozilla::ct
 
 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,
   LargeMinimumSucceeded = 1,
   CompatibilityRisk = 2,
   AlreadyBad = 3,
 };
 
--- a/security/certverifier/OCSPCache.cpp
+++ b/security/certverifier/OCSPCache.cpp
@@ -33,16 +33,18 @@
 #include "secerr.h"
 
 extern mozilla::LazyLogModule gCertVerifierLog;
 
 using namespace mozilla::pkix;
 
 namespace mozilla { namespace psm {
 
+typedef mozilla::pkix::Result Result;
+
 static SECStatus
 DigestLength(UniquePK11Context& context, uint32_t length)
 {
   // Restrict length to 2 bytes because it should be big enough for all
   // inputs this code will actually see and that it is well-defined and
   // type-size-independent.
   if (length >= 65536) {
     return SECFailure;
--- a/security/certverifier/OCSPVerificationTrustDomain.cpp
+++ b/security/certverifier/OCSPVerificationTrustDomain.cpp
@@ -4,16 +4,18 @@
  * 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 "OCSPVerificationTrustDomain.h"
 
 using namespace mozilla;
 using namespace mozilla::pkix;
 
+namespace mozilla { namespace psm {
+
 OCSPVerificationTrustDomain::OCSPVerificationTrustDomain(
   NSSCertDBTrustDomain& certDBTrustDomain)
   : mCertDBTrustDomain(certDBTrustDomain)
 {
 }
 
 Result
 OCSPVerificationTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
@@ -117,8 +119,10 @@ OCSPVerificationTrustDomain::NoteAuxilia
 
 Result
 OCSPVerificationTrustDomain::DigestBuf(
   Input item, DigestAlgorithm digestAlg,
   /*out*/ uint8_t* digestBuf, size_t digestBufLen)
 {
   return mCertDBTrustDomain.DigestBuf(item, digestAlg, digestBuf, digestBufLen);
 }
+
+} } // namespace mozilla::psm
--- a/security/certverifier/OCSPVerificationTrustDomain.h
+++ b/security/certverifier/OCSPVerificationTrustDomain.h
@@ -7,16 +7,18 @@
 #ifndef mozilla_psm__OCSPVerificationTrustDomain_h
 #define mozilla_psm__OCSPVerificationTrustDomain_h
 
 #include "pkix/pkixtypes.h"
 #include "NSSCertDBTrustDomain.h"
 
 namespace mozilla { namespace psm {
 
+typedef mozilla::pkix::Result Result;
+
 class OCSPVerificationTrustDomain : public mozilla::pkix::TrustDomain
 {
 public:
   explicit OCSPVerificationTrustDomain(NSSCertDBTrustDomain& certDBTrustDomain);
 
   virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
                             IssuerChecker& checker,
                             mozilla::pkix::Time time) override;
--- a/security/manager/ssl/ContentSignatureVerifier.cpp
+++ b/security/manager/ssl/ContentSignatureVerifier.cpp
@@ -159,17 +159,17 @@ ContentSignatureVerifier::CreateContextI
   CERTCertListNode* node = CERT_LIST_HEAD(certCertList.get());
   if (!node || !node->cert) {
     return NS_ERROR_FAILURE;
   }
 
   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) {
     return NS_ERROR_FAILURE;
   }
 
 
   // Check the signerCert chain is good
--- a/security/manager/ssl/nsDataSignatureVerifier.cpp
+++ b/security/manager/ssl/nsDataSignatureVerifier.cpp
@@ -252,21 +252,22 @@ VerifyCertificate(CERTCertificate* cert,
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   context->signingCert = xpcomCert;
 
   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));
   }
 
   return NS_OK;
 }
 
 } // namespace
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -184,17 +184,17 @@ struct nsCancelHTTPDownloadEvent : Runna
 
   NS_IMETHOD Run() override {
     mListener->FreeLoadGroup(true);
     mListener = nullptr;
     return NS_OK;
   }
 };
 
-Result
+mozilla::pkix::Result
 nsNSSHttpServerSession::createSessionFcn(const char* host,
                                          uint16_t portnum,
                                  /*out*/ nsNSSHttpServerSession** pSession)
 {
   if (!host || !pSession) {
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
 
@@ -205,17 +205,17 @@ nsNSSHttpServerSession::createSessionFcn
 
   hss->mHost = host;
   hss->mPort = portnum;
 
   *pSession = hss;
   return Success;
 }
 
-Result
+mozilla::pkix::Result
 nsNSSHttpRequestSession::createFcn(const nsNSSHttpServerSession* session,
                                    const char* http_protocol_variant,
                                    const char* path_and_query_string,
                                    const char* http_request_method,
                                    const PRIntervalTime timeout,
                            /*out*/ nsNSSHttpRequestSession** pRequest)
 {
   if (!session || !http_protocol_variant || !path_and_query_string ||
@@ -245,29 +245,29 @@ nsNSSHttpRequestSession::createFcn(const
   rs->mURL.Append(path_and_query_string);
 
   rs->mRequestMethod = http_request_method;
 
   *pRequest = rs;
   return Success;
 }
 
-Result
+mozilla::pkix::Result
 nsNSSHttpRequestSession::setPostDataFcn(const char* http_data,
                                         const uint32_t http_data_len,
                                         const char* http_content_type)
 {
   mHasPostData = true;
   mPostData.Assign(http_data, http_data_len);
   mPostContentType.Assign(http_content_type);
 
   return Success;
 }
 
-Result
+mozilla::pkix::Result
 nsNSSHttpRequestSession::trySendAndReceiveFcn(PRPollDesc** pPollDesc,
                                               uint16_t* http_response_code,
                                               const char** http_response_content_type,
                                               const char** http_response_headers,
                                               const char** http_response_data,
                                               uint32_t* http_response_data_len)
 {
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
@@ -348,17 +348,17 @@ void
 nsNSSHttpRequestSession::Release()
 {
   int32_t newRefCount = --mRefCount;
   if (!newRefCount) {
     delete this;
   }
 }
 
-Result
+mozilla::pkix::Result
 nsNSSHttpRequestSession::internal_send_receive_attempt(bool &retryable_error,
                                                        PRPollDesc **pPollDesc,
                                                        uint16_t *http_response_code,
                                                        const char **http_response_content_type,
                                                        const char **http_response_headers,
                                                        const char **http_response_data,
                                                        uint32_t *http_response_data_len)
 {
--- a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
+++ b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
@@ -12,17 +12,16 @@
 #include "nss.h"
 #include "pkix/pkixtypes.h"
 #include "pkixtestutil.h"
 #include "prerr.h"
 #include "secerr.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
-using namespace mozilla::psm;
 
 template <size_t N>
 inline Input
 LiteralInput(const char(&valueString)[N])
 {
   // Ideally we would use mozilla::BitwiseCast() here rather than
   // reinterpret_cast for better type checking, but the |N - 1| part trips
   // static asserts.
@@ -41,17 +40,17 @@ protected:
     NSS_NoDB_Init(nullptr);
   }
 
   const Time now;
   mozilla::psm::OCSPCache cache;
 };
 
 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.
   // The caller is expecting the validUntil returned with Get
   // to be equal to the passed-in time. Since these values will
   // be different in practice, make thisUpdate less than validUntil.
   Time thisUpdate(time);
   ASSERT_EQ(Success, thisUpdate.SubtractSeconds(10));
--- a/testing/mach_commands.py
+++ b/testing/mach_commands.py
@@ -315,17 +315,17 @@ class Test(MachCommandBase):
                 res = self._mach_context.commands.dispatch(
                     suite['mach_command'], self._mach_context,
                     **suite['kwargs'])
                 if res:
                     status = res
 
         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()):
             if flavor not in TEST_FLAVORS:
                 print(UNKNOWN_FLAVOR % flavor)
                 status = 1
                 continue
 
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -373,27 +373,27 @@ class MachCommands(MachCommandBase):
 
         suites = defaultdict(list)
         unsupported = set()
         for test in tests:
             # Filter out non-mochitests and unsupported flavors.
             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
 
             suites[key].append(test)
 
         # This is a hack to introduce an option in mach to not send
         # filtered tests to the mochitest harness. Mochitest harness will read
         # the master manifest in that case.
--- a/testing/mozbase/manifestparser/manifestparser/manifestparser.py
+++ b/testing/mozbase/manifestparser/manifestparser/manifestparser.py
@@ -147,16 +147,20 @@ class ManifestParser(object):
         sections = read_ini(fp=fp, variables=defaults, strict=self.strict,
                             handle_defaults=self._handle_defaults)
         self.manifest_defaults[filename] = defaults
 
         parent_section_found = False
 
         # 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:'):
                 continue
 
             # read the parent manifest if specified
             if section.startswith('parent:'):
                 parent_section_found = True
@@ -212,16 +216,17 @@ class ManifestParser(object):
                 # When the rootdir is unknown, the relpath needs to be left
                 # unchanged. We use an empty string as rootdir in that case,
                 # which leaves relpath unchanged after slicing.
                 if path.startswith(rootdir):
                     _relpath = path[len(rootdir):]
                 else:
                     _relpath = relpath(path, rootdir)
 
+            test['subsuite'] = subsuite
             test['path'] = path
             test['relpath'] = _relpath
 
             if parentmanifest is not None:
                 # If a test was included by a parent manifest we may need to
                 # indicate that in the test object for the sake of identifying
                 # a test, particularly in the case a test file is included by
                 # multiple manifests.
--- a/testing/mozbase/manifestparser/tests/test_convert_directory.py
+++ b/testing/mozbase/manifestparser/tests/test_convert_directory.py
@@ -55,22 +55,26 @@ class TestDirectoryConversion(unittest.T
         stub = self.create_stub()
         try:
             stub = stub.replace(os.path.sep, "/")
             self.assertTrue(os.path.exists(stub) and os.path.isdir(stub))
 
             # Make a manifest for it
             manifest = convert([stub])
             out_tmpl = """[%(stub)s/bar]
+subsuite = 
 
 [%(stub)s/fleem]
+subsuite = 
 
 [%(stub)s/foo]
+subsuite = 
 
 [%(stub)s/subdir/subfile]
+subsuite = 
 
 """  # noqa
             self.assertEqual(str(manifest), out_tmpl % dict(stub=stub))
         except:
             raise
         finally:
             shutil.rmtree(stub)  # cleanup
 
--- a/testing/mozbase/manifestparser/tests/test_default_overrides.py
+++ b/testing/mozbase/manifestparser/tests/test_default_overrides.py
@@ -2,17 +2,16 @@
 
 # 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/.
 
 import os
 import unittest
 from manifestparser import ManifestParser
-from manifestparser import combine_fields
 
 here = os.path.dirname(os.path.abspath(__file__))
 
 
 class TestDefaultSkipif(unittest.TestCase):
     """Tests applying a skip-if condition in [DEFAULT] and || with the value for the test"""
 
     def test_defaults(self):
@@ -89,27 +88,10 @@ class TestOmitDefaults(unittest.TestCase
         }
         for path, defaults in expected_defaults.items():
             self.assertIn(path, parser.manifest_defaults)
             actual_defaults = parser.manifest_defaults[path]
             for key, value in defaults.items():
                 self.assertIn(key, actual_defaults)
                 self.assertEqual(value, actual_defaults[key])
 
-
-class TestSubsuiteDefaults(unittest.TestCase):
-    """Test that subsuites are handled correctly when managing defaults
-    outside of the manifest parser."""
-    def test_subsuite_defaults(self):
-        manifest = os.path.join(here, 'default-subsuite.ini')
-        parser = ManifestParser(manifests=(manifest,), handle_defaults=False)
-        expected_subsuites = {
-            'test1': 'baz',
-            'test2': 'foo',
-        }
-        defaults = parser.manifest_defaults[manifest]
-        for test in parser.tests:
-            value = combine_fields(defaults, test)
-            self.assertEqual(expected_subsuites[value['name']],
-                             value['subsuite'])
-
 if __name__ == '__main__':
     unittest.main()
--- a/testing/mozbase/manifestparser/tests/test_manifestparser.py
+++ b/testing/mozbase/manifestparser/tests/test_manifestparser.py
@@ -109,20 +109,22 @@ class TestManifestParser(unittest.TestCa
 
         # Write the output to a manifest:
         buffer = StringIO()
         parser.write(fp=buffer, global_kwargs={'foo': 'bar'})
         expected_output = """[DEFAULT]
 foo = bar
 
 [fleem]
+subsuite = 
 
 [include/flowers]
 blue = ocean
 red = roses
+subsuite = 
 yellow = submarine"""  # noqa
 
         self.assertEqual(buffer.getvalue().strip(),
                          expected_output)
 
     def test_invalid_path(self):
         """
         Test invalid path should not throw when not strict
@@ -150,17 +152,17 @@ yellow = submarine"""  # noqa
         # DEFAULT values should be the ones from level 1
         self.assertEqual(parser.get('name', x='level_1'),
                          ['test_3'])
 
         # Write the output to a manifest:
         buffer = StringIO()
         parser.write(fp=buffer, global_kwargs={'x': 'level_1'})
         self.assertEqual(buffer.getvalue().strip(),
-                         '[DEFAULT]\nx = level_1\n\n[test_3]')
+                         '[DEFAULT]\nx = level_1\n\n[test_3]\nsubsuite =')
 
     def test_parent_defaults(self):
         """
         Test downstream variables should overwrite upstream variables
         """
         parent_example = os.path.join(here, 'parent', 'level_1', 'level_2',
                                       'level_3', 'level_3_default.ini')
         parser = ManifestParser(manifests=(parent_example,))
@@ -175,17 +177,17 @@ yellow = submarine"""  # noqa
         # DEFAULT values should be the ones from level 3
         self.assertEqual(parser.get('name', x='level_3'),
                          ['test_3'])
 
         # Write the output to a manifest:
         buffer = StringIO()
         parser.write(fp=buffer, global_kwargs={'x': 'level_3'})
         self.assertEqual(buffer.getvalue().strip(),
-                         '[DEFAULT]\nx = level_3\n\n[test_3]')
+                         '[DEFAULT]\nx = level_3\n\n[test_3]\nsubsuite =')
 
     def test_parent_defaults_include(self):
         parent_example = os.path.join(here, 'parent', 'include', 'manifest.ini')
         parser = ManifestParser(manifests=(parent_example,))
 
         # global defaults should inherit all includes
         self.assertEqual(parser.get('name', top='data'),
                          ['testFirst.js', 'testSecond.js'])
--- a/testing/mozharness/configs/builds/releng_base_android_64_builds.py
+++ b/testing/mozharness/configs/builds/releng_base_android_64_builds.py
@@ -37,16 +37,22 @@ config = {
         ('/builds/mozilla-fennec-geoloc-api.key', '/builds/mozilla-fennec-geoloc-api.key'),
         ('/builds/crash-stats-api.token', '/builds/crash-stats-api.token'),
         ('/usr/local/lib/hgext', '/usr/local/lib/hgext'),
     ],
     'secret_files': [
         {'filename': '/builds/mozilla-fennec-geoloc-api.key',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/mozilla-fennec-geoloc-api.key',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
+        {'filename': '/builds/adjust-sdk.token',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk.token',
+         'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
+        {'filename': '/builds/adjust-sdk-beta.token',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk-beta.token',
+         'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
     ],
     'enable_ccache': True,
     'vcs_share_base': '/builds/hg-shared',
     'objdir': 'obj-firefox',
     'tooltool_script': ["/builds/tooltool.py"],
     'tooltool_bootstrap': "setup.sh",
     'enable_count_ctors': False,
     'enable_talos_sendchange': True,
--- a/testing/web-platform/meta/html/dom/reflection-embedded.html.ini
+++ b/testing/web-platform/meta/html/dom/reflection-embedded.html.ini
@@ -691,92 +691,8 @@
     expected: FAIL
 
   [area.type: IDL set to object "test-toString" followed by IDL get]
     expected: FAIL
 
   [area.type: IDL set to object "test-valueOf" followed by IDL get]
     expected: FAIL
 
-  [area.ping: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f  foo " followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to undefined followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to 7 followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to 1.5 followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to true followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to false followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to object "[object Object\]" followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to NaN followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to Infinity followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to -Infinity followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to "\\0" followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to null followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to object "test-toString" followed by IDL get]
-    expected: FAIL
-
-  [area.ping: setAttribute() to object "test-valueOf" followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f  foo " followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to undefined followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to 7 followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to 1.5 followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to true followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to false followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to object "[object Object\]" followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to NaN followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to Infinity followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to -Infinity followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to "\\0" followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to null followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to object "test-toString" followed by IDL get]
-    expected: FAIL
-
-  [area.ping: IDL set to object "test-valueOf" followed by IDL get]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/dom/reflection-text.html.ini
+++ b/testing/web-platform/meta/html/dom/reflection-text.html.ini
@@ -169,92 +169,8 @@
     expected: FAIL
 
   [wbr.tabIndex: setAttribute() to object "3" followed by getAttribute()]
     expected: FAIL
 
   [wbr.tabIndex: setAttribute() to object "3" followed by IDL get]
     expected: FAIL
 
-  [a.ping: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f  foo " followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to undefined followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to 7 followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to 1.5 followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to true followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to false followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to object "[object Object\]" followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to NaN followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to Infinity followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to -Infinity followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to "\\0" followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to null followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to object "test-toString" followed by IDL get]
-    expected: FAIL
-
-  [a.ping: setAttribute() to object "test-valueOf" followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f  foo " followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to undefined followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to 7 followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to 1.5 followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to true followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to false followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to object "[object Object\]" followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to NaN followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to Infinity followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to -Infinity followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to "\\0" followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to null followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to object "test-toString" followed by IDL get]
-    expected: FAIL
-
-  [a.ping: IDL set to object "test-valueOf" followed by IDL get]
-    expected: FAIL
-
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -1903,20 +1903,26 @@ TelemetryImpl::GetIsOfficialTelemetry(bo
   return NS_OK;
 }
 
 already_AddRefed<nsITelemetry>
 TelemetryImpl::CreateTelemetryInstance()
 {
   MOZ_ASSERT(sTelemetry == nullptr, "CreateTelemetryInstance may only be called once, via GetService()");
 
+  bool useTelemetry = false;
+  if (XRE_IsParentProcess() ||
+      XRE_IsContentProcess() ||
+      XRE_IsGPUProcess())
+  {
+    useTelemetry = true;
+  }
+
   // First, initialize the TelemetryHistogram and TelemetryScalar global states.
-  TelemetryHistogram::InitializeGlobalState(
-                        XRE_IsParentProcess() || XRE_IsContentProcess(),
-                        XRE_IsParentProcess() || XRE_IsContentProcess());
+  TelemetryHistogram::InitializeGlobalState(useTelemetry, useTelemetry);
 
   // Only record scalars from the parent process.
   TelemetryScalar::InitializeGlobalState(XRE_IsParentProcess(), XRE_IsParentProcess());
 
   // Now, create and initialize the Telemetry global state.
   sTelemetry = new TelemetryImpl();
 
   // AddRef for the local reference
@@ -2258,33 +2264,34 @@ TelemetryImpl::RecordThreadHangStats(Tel
 NS_IMPL_ISUPPORTS(TelemetryImpl, nsITelemetry, nsIMemoryReporter)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, TelemetryImpl::CreateTelemetryInstance)
 
 #define NS_TELEMETRY_CID \
   {0xaea477f2, 0xb3a2, 0x469c, {0xaa, 0x29, 0x0a, 0x82, 0xd1, 0x32, 0xb8, 0x29}}
 NS_DEFINE_NAMED_CID(NS_TELEMETRY_CID);
 
 const Module::CIDEntry kTelemetryCIDs[] = {
-  { &kNS_TELEMETRY_CID, false, nullptr, nsITelemetryConstructor },
+  { &kNS_TELEMETRY_CID, false, nullptr, nsITelemetryConstructor, Module::ALLOW_IN_GPU_PROCESS },
   { nullptr }
 };
 
 const Module::ContractIDEntry kTelemetryContracts[] = {
-  { "@mozilla.org/base/telemetry;1", &kNS_TELEMETRY_CID },
+  { "@mozilla.org/base/telemetry;1", &kNS_TELEMETRY_CID, Module::ALLOW_IN_GPU_PROCESS },
   { nullptr }
 };
 
 const Module kTelemetryModule = {
   Module::kVersion,
   kTelemetryCIDs,
   kTelemetryContracts,
   nullptr,
   nullptr,
   nullptr,
-  TelemetryImpl::ShutdownTelemetry
+  TelemetryImpl::ShutdownTelemetry,
+  Module::ALLOW_IN_GPU_PROCESS
 };
 
 NS_IMETHODIMP
 TelemetryImpl::GetFileIOReports(JSContext *cx, JS::MutableHandleValue ret)
 {
   if (sTelemetryIOObserver) {
     JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
     if (!obj) {
@@ -2824,25 +2831,27 @@ AccumulateCategorical(ID id, const nsCSt
 void
 AccumulateTimeDelta(ID aHistogram, TimeStamp start, TimeStamp end)
 {
   Accumulate(aHistogram,
              static_cast<uint32_t>((end - start).ToMilliseconds()));
 }
 
 void
-AccumulateChild(const nsTArray<Accumulation>& aAccumulations)
+AccumulateChild(GeckoProcessType aProcessType,
+                const nsTArray<Accumulation>& aAccumulations)
 {
-  TelemetryHistogram::AccumulateChild(aAccumulations);
+  TelemetryHistogram::AccumulateChild(aProcessType, aAccumulations);
 }
 
 void
-AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations)
+AccumulateChildKeyed(GeckoProcessType aProcessType,
+                     const nsTArray<KeyedAccumulation>& aAccumulations)
 {
-  TelemetryHistogram::AccumulateChildKeyed(aAccumulations);
+  TelemetryHistogram::AccumulateChildKeyed(aProcessType, aAccumulations);
 }
 
 const char*
 GetHistogramName(ID id)
 {
   return TelemetryHistogram::GetHistogramName(id);
 }
 
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -6,16 +6,17 @@
 #ifndef Telemetry_h__
 #define Telemetry_h__
 
 #include "mozilla/GuardObjects.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/StartupTimeline.h"
 #include "nsTArray.h"
 #include "nsStringGlue.h"
+#include "nsXULAppAPI.h"
 
 #include "mozilla/TelemetryHistogramEnums.h"
 #include "mozilla/TelemetryScalarEnums.h"
 
 /******************************************************************************
  * This implements the Telemetry system.
  * It allows recording into histograms as well some more specialized data
  * points and gives access to the data.
@@ -124,28 +125,28 @@ void AccumulateCategorical(ID id, const 
  *
  * @param id - histogram id
  * @param start - start time
  * @param end - end time
  */
 void AccumulateTimeDelta(ID id, TimeStamp start, TimeStamp end = TimeStamp::Now());
 
 /**
- * Accumulate child data into child histograms
+ * Accumulate child process data into histograms for the given process type.
  *
  * @param aAccumulations - accumulation actions to perform
  */
-void AccumulateChild(const nsTArray<Accumulation>& aAccumulations);
+void AccumulateChild(GeckoProcessType aProcessType, const nsTArray<Accumulation>& aAccumulations);
 
 /**
- * Accumulate child data into child keyed histograms
+ * Accumulate child process data into keyed histograms for the given process type.
  *
  * @param aAccumulations - accumulation actions to perform
  */
-void AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations);
+void AccumulateChildKeyed(GeckoProcessType aProcessType, const nsTArray<KeyedAccumulation>& aAccumulations);
 
 /**
  * Enable/disable recording for this histogram at runtime.
  * Recording is enabled by default, unless listed at kRecordingInitiallyDisabledIDs[].
  * id must be a valid telemetry enum, otherwise an assertion is triggered.
  *
  * @param id - histogram id
  * @param enabled - whether or not to enable recording from now on.
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm
+++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm
@@ -148,17 +148,16 @@ const DEFAULT_ENVIRONMENT_PREFS = new Ma
   ["browser.zoom.full", {what: RECORD_PREF_VALUE}],
   ["devtools.chrome.enabled", {what: RECORD_PREF_VALUE}],
   ["devtools.debugger.enabled", {what: RECORD_PREF_VALUE}],
   ["devtools.debugger.remote-enabled", {what: RECORD_PREF_VALUE}],
   ["dom.ipc.plugins.asyncInit.enabled", {what: RECORD_PREF_VALUE}],
   ["dom.ipc.plugins.enabled", {what: RECORD_PREF_VALUE}],
   ["dom.ipc.processCount", {what: RECORD_PREF_VALUE, requiresRestart: true}],
   ["dom.max_script_run_time", {what: RECORD_PREF_VALUE}],
-  ["e10s.rollout.disabledByLongSpinners", {what: RECORD_PREF_VALUE}],
   ["experiments.manifest.uri", {what: RECORD_PREF_VALUE}],
   ["extensions.autoDisableScopes", {what: RECORD_PREF_VALUE}],
   ["extensions.enabledScopes", {what: RECORD_PREF_VALUE}],
   ["extensions.blocklist.enabled", {what: RECORD_PREF_VALUE}],
   ["extensions.blocklist.url", {what: RECORD_PREF_VALUE}],
   ["extensions.strictCompatibility", {what: RECORD_PREF_VALUE}],
   ["extensions.update.enabled", {what: RECORD_PREF_VALUE}],
   ["extensions.update.url", {what: RECORD_PREF_VALUE}],
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp
+++ b/toolkit/components/telemetry/TelemetryHistogram.cpp
@@ -11,16 +11,18 @@
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "nsBaseHashtable.h"
 #include "nsClassHashtable.h"
 #include "nsITelemetry.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ToJSValue.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/StartupTimeline.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Unused.h"
 
 #include "TelemetryCommon.h"
 #include "TelemetryHistogram.h"
@@ -94,17 +96,18 @@ using mozilla::Telemetry::KeyedAccumulat
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE TYPES
 
 #define EXPIRED_ID "__expired__"
 #define SUBSESSION_HISTOGRAM_PREFIX "sub#"
 #define KEYED_HISTOGRAM_NAME_SEPARATOR "#"
-#define CHILD_HISTOGRAM_SUFFIX "#content"
+#define CONTENT_HISTOGRAM_SUFFIX "#content"
+#define GPU_HISTOGRAM_SUFFIX "#gpu"
 
 namespace {
 
 using mozilla::Telemetry::Common::AutoHashtable;
 using mozilla::Telemetry::Common::IsExpiredVersion;
 using mozilla::Telemetry::Common::CanRecordDataset;
 using mozilla::Telemetry::Common::IsInDataset;
 
@@ -335,26 +338,16 @@ HistogramInfo::label_id(const char* labe
       *labelId = i;
       return NS_OK;
     }
   }
 
   return NS_ERROR_FAILURE;
 }
 
-bool
-StringEndsWith(const std::string& name, const std::string& suffix)
-{
-  if (name.size() < suffix.size()) {
-    return false;
-  }
-
-  return name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0;
-}
-
 struct TelemetryShutdownObserver : public nsIObserver
 {
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD Observe(nsISupports*, const char* aTopic, const char16_t*) override
   {
     if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) != 0) {
       return NS_OK;
@@ -450,25 +443,57 @@ internal_HistogramGet(const char *name, 
     break;
   default:
     NS_ASSERTION(false, "Invalid histogram type");
     return NS_ERROR_INVALID_ARG;
   }
   return NS_OK;
 }
 
-CharPtrEntryType*
-internal_GetHistogramMapEntry(const char* name)
+// Read the process type from the given histogram name. The process type, if
+// one exists, is embedded in a suffix.
+GeckoProcessType
+GetProcessFromName(const nsACString& aString)
+{
+  if (StringEndsWith(aString, NS_LITERAL_CSTRING(CONTENT_HISTOGRAM_SUFFIX))) {
+    return GeckoProcessType_Content;
+  }
+  if (StringEndsWith(aString, NS_LITERAL_CSTRING(GPU_HISTOGRAM_SUFFIX))) {
+    return GeckoProcessType_GPU;
+  }
+  return GeckoProcessType_Default;
+}
+
+const char*
+SuffixForProcessType(GeckoProcessType aProcessType)
 {
-  nsDependentCString histogramName(name);
-  NS_NAMED_LITERAL_CSTRING(suffix, CHILD_HISTOGRAM_SUFFIX);
-  if (!StringEndsWith(histogramName, suffix)) {
-    return gHistogramMap.GetEntry(name);
+  switch (aProcessType) {
+    case GeckoProcessType_Default:
+      return nullptr;
+    case GeckoProcessType_Content:
+      return CONTENT_HISTOGRAM_SUFFIX;
+    case GeckoProcessType_GPU:
+      return GPU_HISTOGRAM_SUFFIX;
+    default:
+      MOZ_ASSERT_UNREACHABLE("unknown process type");
+      return nullptr;
   }
-  auto root = Substring(histogramName, 0, histogramName.Length() - suffix.Length());
+}
+
+CharPtrEntryType*
+internal_GetHistogramMapEntry(const char* aName)
+{
+  nsDependentCString name(aName);
+  GeckoProcessType process = GetProcessFromName(name);
+  const char* suffix = SuffixForProcessType(process);
+  if (!suffix) {
+    return gHistogramMap.GetEntry(aName);
+  }
+
+  auto root = Substring(name, 0, name.Length() - strlen(suffix));
   return gHistogramMap.GetEntry(PromiseFlatCString(root).get());
 }
 
 nsresult
 internal_GetHistogramEnumId(const char *name, mozilla::Telemetry::ID *id)
 {
   if (!gInitDone) {
     return NS_ERROR_FAILURE;
@@ -479,36 +504,54 @@ internal_GetHistogramEnumId(const char *
     return NS_ERROR_INVALID_ARG;
   }
   *id = entry->mData;
   return NS_OK;
 }
 
 // O(1) histogram lookup by numeric id
 nsresult
-internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret,
-                              bool child = false)
+internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret, GeckoProcessType aProcessType)
 {
   static Histogram* knownHistograms[mozilla::Telemetry::HistogramCount] = {0};
-  static Histogram* knownChildHistograms[mozilla::Telemetry::HistogramCount] = {0};
-  Histogram *h = child ? knownChildHistograms[id] : knownHistograms[id];
+  static Histogram* knownContentHistograms[mozilla::Telemetry::HistogramCount] = {0};
+  static Histogram* knownGPUHistograms[mozilla::Telemetry::HistogramCount] = {0};
+
+  Histogram** knownList = nullptr;
+
+  switch (aProcessType) {
+  case GeckoProcessType_Default:
+    knownList = knownHistograms;
+    break;
+  case GeckoProcessType_Content:
+    knownList = knownContentHistograms;
+    break;
+  case GeckoProcessType_GPU:
+    knownList = knownGPUHistograms;
+    break;
+  default:
+    MOZ_ASSERT_UNREACHABLE("unknown process type");
+    return NS_ERROR_FAILURE;
+  }
+
+  Histogram* h = knownList[id];
   if (h) {
     *ret = h;
     return NS_OK;
   }
 
   const HistogramInfo &p = gHistograms[id];
   if (p.keyed) {
     return NS_ERROR_FAILURE;
   }
 
   nsCString histogramName;
   histogramName.Append(p.id());
-  if (child) {
-    histogramName.AppendLiteral(CHILD_HISTOGRAM_SUFFIX);
+  if (const char* suffix = SuffixForProcessType(aProcessType)) {
+    histogramName.AppendASCII(suffix);
   }
 
   nsresult rv = internal_HistogramGet(histogramName.get(), p.expiration(),
                                       p.histogramType, p.min, p.max,
                                       p.bucketCount, true, &h);
   if (NS_FAILED(rv))
     return rv;
 
@@ -523,37 +566,33 @@ internal_GetHistogramByEnumId(mozilla::T
       for (int i = 0; i < b.length; ++i) {
         MOZ_ASSERT(gBucketLowerBounds[b.offset + i] == h->ranges(i),
                    "C++/Python bucket mismatch");
       }
     }
   }
 #endif
 
-  if (child) {
-    *ret = knownChildHistograms[id] = h;
-  } else {
-    *ret = knownHistograms[id] = h;
-  }
+  knownList[id] = h;
+  *ret = h;
   return NS_OK;
 }
 
 nsresult
 internal_GetHistogramByName(const nsACString &name, Histogram **ret)
 {
   mozilla::Telemetry::ID id;
   nsresult rv
     = internal_GetHistogramEnumId(PromiseFlatCString(name).get(), &id);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  bool isChild = StringEndsWith(name,
-                                NS_LITERAL_CSTRING(CHILD_HISTOGRAM_SUFFIX));
-  rv = internal_GetHistogramByEnumId(id, ret, isChild);
+  GeckoProcessType process = GetProcessFromName(name);
+  rv = internal_GetHistogramByEnumId(id, ret, process);
   if (NS_FAILED(rv))
     return rv;
 
   return NS_OK;
 }
 
 /**
  * This clones a histogram |existing| with the id |existingId| to a
@@ -590,61 +629,80 @@ internal_CloneHistogram(const nsACString
  * with the name |newName|.
  * For simplicity this is limited to registered histograms.
  */
 Histogram*
 internal_CloneHistogram(const nsACString& newName,
                         mozilla::Telemetry::ID existingId)
 {
   Histogram *existing = nullptr;
-  nsresult rv = internal_GetHistogramByEnumId(existingId, &existing);
+  nsresult rv = internal_GetHistogramByEnumId(existingId, &existing, GeckoProcessType_Default);
   if (NS_FAILED(rv)) {
     return nullptr;
   }
 
   return internal_CloneHistogram(newName, existingId, *existing);
 }
 
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
 
+GeckoProcessType
+GetProcessFromName(const std::string& aString)
+{
+  nsDependentCString string(aString.c_str(), aString.length());
+  return GetProcessFromName(string);
+}
+
 Histogram*
 internal_GetSubsessionHistogram(Histogram& existing)
 {
   mozilla::Telemetry::ID id;
   nsresult rv
     = internal_GetHistogramEnumId(existing.histogram_name().c_str(), &id);
   if (NS_FAILED(rv) || gHistograms[id].keyed) {
     return nullptr;
   }
 
-  bool isChild = StringEndsWith(existing.histogram_name(),
-                                CHILD_HISTOGRAM_SUFFIX);
-
   static Histogram* subsession[mozilla::Telemetry::HistogramCount] = {};
-  static Histogram* subsessionChild[mozilla::Telemetry::HistogramCount] = {};
-  Histogram* cached = isChild ? subsessionChild[id] : subsession[id];
-  if (cached) {
+  static Histogram* subsessionContent[mozilla::Telemetry::HistogramCount] = {};
+  static Histogram* subsessionGPU[mozilla::Telemetry::HistogramCount] = {};
+
+  Histogram** cache = nullptr;
+
+  GeckoProcessType process = GetProcessFromName(existing.histogram_name());
+  switch (process) {
+  case GeckoProcessType_Default:
+    cache = subsession;
+    break;
+  case GeckoProcessType_Content:
+    cache = subsessionContent;
+    break;
+  case GeckoProcessType_GPU:
+    cache = subsessionGPU;
+    break;
+  default:
+    MOZ_ASSERT_UNREACHABLE("unknown process type");
+    return nullptr;
+  }
+
+  if (Histogram* cached = cache[id]) {
     return cached;
   }
 
   NS_NAMED_LITERAL_CSTRING(prefix, SUBSESSION_HISTOGRAM_PREFIX);
   nsDependentCString existingName(gHistograms[id].id());
   if (StringBeginsWith(existingName, prefix)) {
     return nullptr;
   }
 
   nsCString subsessionName(prefix);
   subsessionName.Append(existing.histogram_name().c_str());
 
   Histogram* clone = internal_CloneHistogram(subsessionName, id, existing);
-  if (isChild) {
-    subsessionChild[id] = clone;
-  } else {
-    subsession[id] = clone;
-  }
+  cache[id] = clone;
   return clone;
 }
 #endif
 
 nsresult
 internal_HistogramAdd(Histogram& histogram, int32_t value, uint32_t dataset)
 {
   // Check if we are allowed to record the data.
@@ -1286,17 +1344,17 @@ internal_SetHistogramRecordingEnabled(mo
     const nsDependentCString id(gHistograms[aID].id());
     KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
     if (keyed) {
       keyed->SetRecordingEnabled(aEnabled);
       return;
     }
   } else {
     Histogram *h;
-    nsresult rv = internal_GetHistogramByEnumId(aID, &h);
+    nsresult rv = internal_GetHistogramByEnumId(aID, &h, GeckoProcessType_Default);
     if (NS_SUCCEEDED(rv)) {
       h->SetRecordingEnabled(aEnabled);
       return;
     }
   }
 
   MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found");
 }
@@ -1378,17 +1436,17 @@ void internal_Accumulate(mozilla::Teleme
 {
   bool isValid = internal_IsHistogramEnumId(aHistogram);
   MOZ_ASSERT(isValid, "Accumulation using invalid id");
   if (!internal_CanRecordBase() || !isValid ||
       internal_RemoteAccumulate(aHistogram, aSample)) {
     return;
   }
   Histogram *h;
-  nsresult rv = internal_GetHistogramByEnumId(aHistogram, &h);
+  nsresult rv = internal_GetHistogramByEnumId(aHistogram, &h, GeckoProcessType_Default);
   if (NS_SUCCEEDED(rv)) {
     internal_HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset);
   }
 }
 
 void
 internal_Accumulate(mozilla::Telemetry::ID aID,
                     const nsCString& aKey, uint32_t aSample)
@@ -1432,41 +1490,50 @@ internal_Accumulate(KeyedHistogram& aKey
 
   mozilla::Telemetry::ID id;
   if (NS_SUCCEEDED(aKeyed.GetEnumId(id))) {
     internal_RemoteAccumulate(id, aKey, aSample);
   }
 }
 
 void
-internal_AccumulateChild(mozilla::Telemetry::ID aId, uint32_t aSample)
+internal_AccumulateChild(GeckoProcessType aProcessType, mozilla::Telemetry::ID aId, uint32_t aSample)
 {
   if (!internal_CanRecordBase()) {
     return;
   }
   Histogram* h;
-  nsresult rv = internal_GetHistogramByEnumId(aId, &h, true);
+  nsresult rv = internal_GetHistogramByEnumId(aId, &h, aProcessType);
   if (NS_SUCCEEDED(rv)) {
     internal_HistogramAdd(*h, aSample, gHistograms[aId].dataset);
   } else {
     NS_WARNING("NS_FAILED GetHistogramByEnumId for CHILD");
   }
 }
 
 void
-internal_AccumulateChildKeyed(mozilla::Telemetry::ID aId,
+internal_AccumulateChildKeyed(GeckoProcessType aProcessType, mozilla::Telemetry::ID aId,
                               const nsCString& aKey, uint32_t aSample)
 {
   if (!gInitDone || !internal_CanRecordBase()) {
     return;
   }
+
+  const char* suffix = SuffixForProcessType(aProcessType);
+  if (!suffix) {
+    MOZ_ASSERT_UNREACHABLE("suffix should not be null");
+    return;
+  }
+
   const HistogramInfo& th = gHistograms[aId];
+
   nsCString id;
   id.Append(th.id());
-  id.AppendLiteral(CHILD_HISTOGRAM_SUFFIX);
+  id.AppendASCII(suffix);
+
   KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
   MOZ_ASSERT(keyed);
   keyed->Add(aKey, aSample);
 }
 
 } // namespace
 
 
@@ -2023,19 +2090,26 @@ void TelemetryHistogram::InitializeGloba
     const nsDependentCString id(h.id());
     const nsDependentCString expiration(h.expiration());
     gKeyedHistograms.Put(id, new KeyedHistogram(id, expiration, h.histogramType,
                                                 h.min, h.max, h.bucketCount, h.dataset));
     if (XRE_IsParentProcess()) {
       // We must create registered child keyed histograms as well or else the
       // same code in TelemetrySession.jsm that fails without parent keyed
       // histograms will fail without child keyed histograms.
-      nsCString childId(id);
-      childId.AppendLiteral(CHILD_HISTOGRAM_SUFFIX);
-      gKeyedHistograms.Put(childId,
+      nsCString contentId(id);
+      contentId.AppendLiteral(CONTENT_HISTOGRAM_SUFFIX);
+      gKeyedHistograms.Put(contentId,
+                           new KeyedHistogram(id, expiration, h.histogramType,
+                                              h.min, h.max, h.bucketCount, h.dataset));
+
+
+      nsCString gpuId(id);
+      gpuId.AppendLiteral(GPU_HISTOGRAM_SUFFIX);
+      gKeyedHistograms.Put(gpuId,
                            new KeyedHistogram(id, expiration, h.histogramType,
                                               h.min, h.max, h.bucketCount, h.dataset));
     }
   }
 
   // Some Telemetry histograms depend on the value of C++ constants and hardcode
   // their values in Histograms.json.
   // We add static asserts here for those values to match so that future changes
@@ -2207,48 +2281,51 @@ TelemetryHistogram::AccumulateCategorica
   uint32_t labelId = 0;
   if (NS_FAILED(gHistograms[aId].label_id(label.get(), &labelId))) {
     return;
   }
   internal_Accumulate(aId, labelId);
 }
 
 void
-TelemetryHistogram::AccumulateChild(const nsTArray<Accumulation>& aAccumulations)
+TelemetryHistogram::AccumulateChild(GeckoProcessType aProcessType,
+                                    const nsTArray<Accumulation>& aAccumulations)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   if (!internal_CanRecordBase()) {
     return;
   }
   for (uint32_t i = 0; i < aAccumulations.Length(); ++i) {
     bool isValid = internal_IsHistogramEnumId(aAccumulations[i].mId);
     MOZ_ASSERT(isValid);
     if (!isValid) {
       continue;
     }
-    internal_AccumulateChild(aAccumulations[i].mId, aAccumulations[i].mSample);
+    internal_AccumulateChild(aProcessType, aAccumulations[i].mId, aAccumulations[i].mSample);
   }
 }
 
 void
-TelemetryHistogram::AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations)
+TelemetryHistogram::AccumulateChildKeyed(GeckoProcessType aProcessType,
+                                         const nsTArray<KeyedAccumulation>& aAccumulations)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   if (!internal_CanRecordBase()) {
     return;
   }
   for (uint32_t i = 0; i < aAccumulations.Length(); ++i) {
     bool isValid = internal_IsHistogramEnumId(aAccumulations[i].mId);
     MOZ_ASSERT(isValid);
     if (!isValid) {
       continue;
     }
-    internal_AccumulateChildKeyed(aAccumulations[i].mId,
+    internal_AccumulateChildKeyed(aProcessType,
+                                  aAccumulations[i].mId,
                                   aAccumulations[i].mKey,
                                   aAccumulations[i].mSample);
   }
 }
 
 nsresult
 TelemetryHistogram::GetHistogramById(const nsACString &name, JSContext *cx,
                                      JS::MutableHandle<JS::Value> ret)
@@ -2322,31 +2399,46 @@ TelemetryHistogram::CreateHistogramSnaps
                                              bool clearSubsession)
 {
   // Runs without protection from |gTelemetryHistogramMutex|
   JS::Rooted<JSObject*> root_obj(cx, JS_NewPlainObject(cx));
   if (!root_obj)
     return NS_ERROR_FAILURE;
   ret.setObject(*root_obj);
 
+  // Include the GPU process in histogram snapshots only if we actually tried
+  // to launch a process for it.
+  bool includeGPUProcess = false;
+  if (auto gpm = mozilla::gfx::GPUProcessManager::Get()) {
+    includeGPUProcess = gpm->AttemptedGPUProcess();
+  }
+
   // Ensure that all the HISTOGRAM_FLAG & HISTOGRAM_COUNT histograms have
   // been created, so that their values are snapshotted.
   for (size_t i = 0; i < mozilla::Telemetry::HistogramCount; ++i) {
     if (gHistograms[i].keyed) {
       continue;
     }
     const uint32_t type = gHistograms[i].histogramType;
     if (type == nsITelemetry::HISTOGRAM_FLAG ||
         type == nsITelemetry::HISTOGRAM_COUNT) {
       Histogram *h;
-      mozilla::DebugOnly<nsresult> rv
-         = internal_GetHistogramByEnumId(mozilla::Telemetry::ID(i), &h);
+      mozilla::DebugOnly<nsresult> rv;
+      mozilla::Telemetry::ID id = mozilla::Telemetry::ID(i);
+
+      rv = internal_GetHistogramByEnumId(id, &h, GeckoProcessType_Default);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
-      rv = internal_GetHistogramByEnumId(mozilla::Telemetry::ID(i), &h, true);
+
+      rv = internal_GetHistogramByEnumId(id, &h, GeckoProcessType_Content);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+      if (includeGPUProcess) {
+        rv = internal_GetHistogramByEnumId(id, &h, GeckoProcessType_GPU);
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+      }
     }
   }
 
   StatisticsRecorder::Histograms hs;
   StatisticsRecorder::GetHistograms(&hs);
 
   // We identify corrupt histograms first, rather than interspersing it
   // in the loop below, to ensure that our corruption statistics don't
@@ -2625,23 +2717,42 @@ TelemetryHistogram::IPCTimerFired(nsITim
     if (gAccumulations) {
       accumulationsToSend.SwapElements(*gAccumulations);
     }
     if (gKeyedAccumulations) {
       keyedAccumulationsToSend.SwapElements(*gKeyedAccumulations);
     }
   }
 
-  mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton();
-  mozilla::Unused << NS_WARN_IF(!contentChild);
-  if (contentChild) {
-    if (accumulationsToSend.Length()) {
-      mozilla::Unused <<
-        NS_WARN_IF(!contentChild->SendAccumulateChildHistogram(accumulationsToSend));
+  switch (XRE_GetProcessType()) {
+    case GeckoProcessType_Content: {
+      mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton();
+      mozilla::Unused << NS_WARN_IF(!contentChild);
+      if (contentChild) {
+        if (accumulationsToSend.Length()) {
+          mozilla::Unused <<
+            NS_WARN_IF(!contentChild->SendAccumulateChildHistogram(accumulationsToSend));
+        }
+        if (keyedAccumulationsToSend.Length()) {
+          mozilla::Unused <<
+            NS_WARN_IF(!contentChild->SendAccumulateChildKeyedHistogram(keyedAccumulationsToSend));
+        }
+      }
+      break;
     }
-    if (keyedAccumulationsToSend.Length()) {
-      mozilla::Unused <<
-        NS_WARN_IF(!contentChild->SendAccumulateChildKeyedHistogram(keyedAccumulationsToSend));
+    case GeckoProcessType_GPU: {
+      if (mozilla::gfx::GPUParent* gpu = mozilla::gfx::GPUParent::GetSingleton()) {
+        if (accumulationsToSend.Length()) {
+          mozilla::Unused << gpu->SendAccumulateChildHistogram(accumulationsToSend);
+        }
+        if (keyedAccumulationsToSend.Length()) {
+          mozilla::Unused << gpu->SendAccumulateChildKeyedHistogram(keyedAccumulationsToSend);
+        }
+      }
+      break;
     }
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unsupported process type");
+      break;
   }
 
   gIPCTimerArmed = false;
 }
--- a/toolkit/components/telemetry/TelemetryHistogram.h
+++ b/toolkit/components/telemetry/TelemetryHistogram.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TelemetryHistogram_h__
 #define TelemetryHistogram_h__
 
 #include "mozilla/TelemetryHistogramEnums.h"
 
 #include "mozilla/TelemetryComms.h"
+#include "nsXULAppAPI.h"
 
 // This module is internal to Telemetry.  It encapsulates Telemetry's
 // histogram accumulation and storage logic.  It should only be used by
 // Telemetry.cpp.  These functions should not be used anywhere else.
 // For the public interface to Telemetry functionality, see Telemetry.h.
 
 namespace TelemetryHistogram {
 
@@ -39,18 +40,20 @@ nsresult SetHistogramRecordingEnabled(co
 void Accumulate(mozilla::Telemetry::ID aHistogram, uint32_t aSample);
 void Accumulate(mozilla::Telemetry::ID aID, const nsCString& aKey,
                                             uint32_t aSample);
 void Accumulate(const char* name, uint32_t sample);
 void Accumulate(const char* name, const nsCString& key, uint32_t sample);
 
 void AccumulateCategorical(mozilla::Telemetry::ID aId, const nsCString& aLabel);
 
-void AccumulateChild(const nsTArray<mozilla::Telemetry::Accumulation>& aAccumulations);
-void AccumulateChildKeyed(const nsTArray<mozilla::Telemetry::KeyedAccumulation>& aAccumulations);
+void AccumulateChild(GeckoProcessType aProcessType,
+                     const nsTArray<mozilla::Telemetry::Accumulation>& aAccumulations);
+void AccumulateChildKeyed(GeckoProcessType aProcessType,
+                          const nsTArray<mozilla::Telemetry::KeyedAccumulation>& aAccumulations);
 
 nsresult
 GetHistogramById(const nsACString &name, JSContext *cx,
                  JS::MutableHandle<JS::Value> ret);
 
 nsresult
 GetKeyedHistogramById(const nsACString &name, JSContext *cx,
                       JS::MutableHandle<JS::Value> ret);
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -39,16 +39,17 @@ const REASON_GATHER_PAYLOAD = "gather-pa
 const REASON_GATHER_SUBSESSION_PAYLOAD = "gather-subsession-payload";
 const REASON_TEST_PING = "test-ping";
 const REASON_ENVIRONMENT_CHANGE = "environment-change";
 const REASON_SHUTDOWN = "shutdown";
 
 const HISTOGRAM_SUFFIXES = {
   PARENT: "",
   CONTENT: "#content",
+  GPU: "#gpu",
 }
 
 const ENVIRONMENT_CHANGE_LISTENER = "TelemetrySession::onEnvironmentChange";
 
 const MS_IN_ONE_HOUR  = 60 * 60 * 1000;
 const MIN_SUBSESSION_LENGTH_MS = Preferences.get("toolkit.telemetry.minSubsessionLength", 10 * 60) * 1000;
 
 const LOGGER_NAME = "Toolkit.Telemetry";
@@ -1310,16 +1311,26 @@ var Impl = {
         keyedScalars: protect(() => this.getScalars(isSubsession, clearSubsession, true)),
       },
       content: {
         histograms: histograms[HISTOGRAM_SUFFIXES.CONTENT],
         keyedHistograms: keyedHistograms[HISTOGRAM_SUFFIXES.CONTENT],
       },
     };
 
+    // Only include the GPU process if we've accumulated data for it.
+    if (HISTOGRAM_SUFFIXES.GPU in histograms ||
+        HISTOGRAM_SUFFIXES.GPU in keyedHistograms)
+    {
+      payloadObj.processes.gpu = {
+        histograms: histograms[HISTOGRAM_SUFFIXES.GPU],
+        keyedHistograms: keyedHistograms[HISTOGRAM_SUFFIXES.GPU],
+      };
+    }
+
     payloadObj.info = info;
 
     // Add extended set measurements for chrome process.
     if (Telemetry.canRecordExtended) {
       payloadObj.slowSQL = protect(() => Telemetry.slowSQL);
       payloadObj.fileIOReports = protect(() => Telemetry.fileIOReports);
       payloadObj.lateWrites = protect(() => Telemetry.lateWrites);
 
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -797,16 +797,17 @@ NS_InitMinimalXPCOM()
 
   // Global cycle collector initialization.
   if (!nsCycleCollector_init()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   AbstractThread::InitStatics();
   SharedThreadPool::InitStatics();
+  mozilla::Telemetry::Init();
   mozilla::HangMonitor::Startup();
   mozilla::BackgroundHangMonitor::Startup();
 
   return NS_OK;
 }
 
 //
 // NS_ShutdownXPCOM()