Merge mozilla-inbound to mozilla-central. a=merge
authorOana Pop Rus <opoprus@mozilla.com>
Tue, 08 Jan 2019 12:13:02 +0200
changeset 512847 60aa2498320da118cb73c2375c201ecf4f47567b
parent 512797 4ad5b70956abbf9c183b1494108273fdbe9ad3a1 (current diff)
parent 512846 3131c1c9f3c5ea1c0f9c163ac560a0296c7b8f8e (diff)
child 512860 eb1db8a519dfc5c5c2347c912583ebfb26862b8c
child 512887 fcbace68967f0a0497456428b34050b5ecc3474b
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
60aa2498320d / 66.0a1 / 20190108101613 / files
nightly linux64
60aa2498320d / 66.0a1 / 20190108101613 / files
nightly mac
60aa2498320d / 66.0a1 / 20190108101613 / files
nightly win32
60aa2498320d / 66.0a1 / 20190108101613 / files
nightly win64
60aa2498320d / 66.0a1 / 20190108101613 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge
dom/base/nsGlobalWindowOuter.cpp
dom/svg/nsSVGBoolean.cpp
dom/svg/nsSVGBoolean.h
dom/svg/nsSVGInteger.cpp
dom/svg/nsSVGInteger.h
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/vr/ipc/VRLayerChild.cpp
js/src/shell/js.cpp
--- a/browser/components/preferences/browserLanguages.js
+++ b/browser/components/preferences/browserLanguages.js
@@ -22,40 +22,47 @@ ChromeUtils.defineModuleGetter(this, "Se
  * requested locales must be installed and enabled. Available locales could be
  * installed and enabled, or fetched from the AMO language tools API.
  *
  * If a langpack is disabled, there is no way to determine what locale it is for and
  * it will only be listed as available if that locale is also available on AMO and
  * the user has opted to search for more languages.
  */
 
-async function installFromUrl(url, hash) {
+async function installFromUrl(url, hash, callback) {
+  let telemetryInfo = {
+    source: "about:preferences",
+  };
   let install = await AddonManager.getInstallForURL(
-    url, "application/x-xpinstall", hash);
+    url, "application/x-xpinstall", hash, null, null, null, null, telemetryInfo);
+  if (callback) {
+    callback(install.installId.toString());
+  }
   await install.install();
   return install.addon;
 }
 
 async function dictionaryIdsForLocale(locale) {
   let entries = await RemoteSettings("language-dictionaries").get({
     filters: {id: locale},
   });
   if (entries.length > 0) {
     return entries[0].dictionaries;
   }
   return [];
 }
 
 class OrderedListBox {
-  constructor({richlistbox, upButton, downButton, removeButton, onRemove}) {
+  constructor({richlistbox, upButton, downButton, removeButton, onRemove, onReorder}) {
     this.richlistbox = richlistbox;
     this.upButton = upButton;
     this.downButton = downButton;
     this.removeButton = removeButton;
     this.onRemove = onRemove;
+    this.onReorder = onReorder;
 
     this.items = [];
 
     this.richlistbox.addEventListener("select", () => this.setButtonState());
     this.upButton.addEventListener("command", () => this.moveUp());
     this.downButton.addEventListener("command", () => this.moveDown());
     this.removeButton.addEventListener("command", () => this.removeItem());
   }
@@ -82,16 +89,18 @@ class OrderedListBox {
     let prevItem = items[selectedIndex - 1];
     items[selectedIndex - 1] = items[selectedIndex];
     items[selectedIndex] = prevItem;
     let prevEl = document.getElementById(prevItem.id);
     let selectedEl = document.getElementById(selectedItem.id);
     this.richlistbox.insertBefore(selectedEl, prevEl);
     this.richlistbox.ensureElementIsVisible(selectedEl);
     this.setButtonState();
+
+    this.onReorder();
   }
 
   moveDown() {
     let {selectedIndex} = this.richlistbox;
     if (selectedIndex == this.items.length - 1) {
       return;
     }
     let {items} = this;
@@ -99,16 +108,18 @@ class OrderedListBox {
     let nextItem = items[selectedIndex + 1];
     items[selectedIndex + 1] = items[selectedIndex];
     items[selectedIndex] = nextItem;
     let nextEl = document.getElementById(nextItem.id);
     let selectedEl = document.getElementById(selectedItem.id);
     this.richlistbox.insertBefore(nextEl, selectedEl);
     this.richlistbox.ensureElementIsVisible(selectedEl);
     this.setButtonState();
+
+    this.onReorder();
   }
 
   removeItem() {
     let {selectedIndex} = this.richlistbox;
 
     if (selectedIndex == -1) {
       return;
     }
@@ -300,33 +311,42 @@ function compareItems(a, b) {
   // One of them is a label, put it first.
   } else if (a.value) {
     return 1;
   }
   return -1;
 }
 
 var gBrowserLanguagesDialog = {
+  telemetryId: null,
+  accepted: false,
   _availableLocales: null,
   _selectedLocales: null,
   selectedLocales: null,
 
   get downloadEnabled() {
     // Downloading langpacks isn't always supported, check the pref.
     return Services.prefs.getBoolPref("intl.multilingual.downloadEnabled");
   },
 
+  recordTelemetry(method, extra = null) {
+    Services.telemetry.recordEvent(
+      "intl.ui.browserLanguage", method, "dialog", this.telemetryId, extra);
+  },
+
   beforeAccept() {
     this.selected = this.getSelectedLocales();
+    this.accepted = true;
     return true;
   },
 
   async onLoad() {
     // Maintain the previously selected locales even if we cancel out.
-    let {selected, search} = window.arguments[0] || {};
+    let {telemetryId, selected, search} = window.arguments[0];
+    this.telemetryId = telemetryId;
     this.selectedLocales = selected;
 
     // This is a list of available locales that the user selected. It's more
     // restricted than the Intl notion of `requested` as it only contains
     // locale codes for which we have matching locales available.
     // The first time this dialog is opened, populate with appLocalesAsBCP47.
     let selectedLocales = this.selectedLocales || Services.locale.appLocalesAsBCP47;
     let selectedLocaleSet = new Set(selectedLocales);
@@ -347,29 +367,33 @@ var gBrowserLanguagesDialog = {
 
   initSelectedLocales(selectedLocales) {
     this._selectedLocales = new OrderedListBox({
       richlistbox: document.getElementById("selectedLocales"),
       upButton: document.getElementById("up"),
       downButton: document.getElementById("down"),
       removeButton: document.getElementById("remove"),
       onRemove: (item) => this.selectedLocaleRemoved(item),
+      onReorder: () => this.recordTelemetry("reorder"),
     });
     this._selectedLocales.setItems(getLocaleDisplayInfo(selectedLocales));
   },
 
   async initAvailableLocales(available, search) {
     this._availableLocales = new SortedItemSelectList({
       menulist: document.getElementById("availableLocales"),
       button: document.getElementById("add"),
       compareFn: compareItems,
       onSelect: (item) => this.availableLanguageSelected(item),
       onChange: (item) => {
         this.hideError();
         if (item.value == "search") {
+          // Record the search event here so we don't track the search from
+          // the main preferences pane twice.
+          this.recordTelemetry("search");
           this.loadLocalesFromAMO();
         }
       },
     });
 
     // Populate the list with the installed locales even if the user is
     // searching in case the download fails.
     await this.loadLocalesFromInstalled(available);
@@ -445,18 +469,20 @@ var gBrowserLanguagesDialog = {
         value: "search",
       });
     }
     this._availableLocales.setItems(items);
   },
 
   async availableLanguageSelected(item) {
     if (Services.locale.availableLocales.includes(item.value)) {
+      this.recordTelemetry("add");
       this.requestLocalLanguage(item);
     } else if (this.availableLangpacks.has(item.value)) {
+      // Telemetry is tracked in requestRemoteLanguage.
       await this.requestRemoteLanguage(item);
     } else {
       this.showError();
     }
   },
 
   requestLocalLanguage(item, available) {
     this._selectedLocales.addItem(item);
@@ -474,17 +500,18 @@ var gBrowserLanguagesDialog = {
   async requestRemoteLanguage(item) {
     this._availableLocales.disableWithMessageId(
       "browser-languages-downloading");
 
     let {url, hash} = this.availableLangpacks.get(item.value);
     let addon;
 
     try {
-      addon = await installFromUrl(url, hash);
+      addon = await installFromUrl(
+        url, hash, (installId) => this.recordTelemetry("add", {installId}));
     } catch (e) {
       this.showError();
       return;
     }
 
     // If the add-on was previously installed, it might be disabled still.
     if (addon.userDisabled) {
       await addon.enable();
@@ -530,16 +557,18 @@ var gBrowserLanguagesDialog = {
     document.getElementById("warning-message").hidden = true;
   },
 
   getSelectedLocales() {
     return this._selectedLocales.items.map(item => item.value);
   },
 
   async selectedLocaleRemoved(item) {
+    this.recordTelemetry("remove");
+
     this._availableLocales.addItem(item);
 
     // If the item we added is at the top of the list, it needs the label.
     if (this._availableLocales.items[0] == item) {
       this._availableLocales.addItem(await this.createInstalledLabel());
     }
   },
 
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -751,16 +751,25 @@ var gMainPane = {
         warnOnQuitCheckbox.removeAttribute("disabled");
       } else {
         warnOnQuitCheckbox.setAttribute("disabled", "true");
       }
     }
   },
 
   initBrowserLocale() {
+    // Enable telemetry.
+    Services.telemetry.setEventRecordingEnabled("intl.ui.browserLanguage", true);
+
+    // This will register the "command" listener.
+    let menulist = document.getElementById("defaultBrowserLanguage");
+    new SelectionChangedMenulist(menulist, event => {
+      gMainPane.onBrowserLanguageChange(event);
+    });
+
     gMainPane.setBrowserLocales(Services.locale.appLocaleAsBCP47);
   },
 
   /**
    * Update the available list of locales and select the locale that the user
    * is "selecting". This could be the currently requested locale or a locale
    * that the user would like to switch to after confirmation.
    */
@@ -789,21 +798,16 @@ var gMainPane = {
     }
 
     let menulist = document.getElementById("defaultBrowserLanguage");
     let menupopup = menulist.querySelector("menupopup");
     menupopup.textContent = "";
     menupopup.appendChild(fragment);
     menulist.value = selected;
 
-    // This will register the "command" listener.
-    new SelectionChangedMenulist(menulist, event => {
-      gMainPane.onBrowserLanguageChange(event);
-    });
-
     document.getElementById("browserLanguagesBox").hidden = false;
   },
 
   /* Show the confirmation message bar to allow a restart into the new locales. */
   async showConfirmLanguageChangeMessageBar(locales) {
     let messageBar = document.getElementById("confirmBrowserLanguage");
 
     // Get the bundle for the new locale.
@@ -862,16 +866,19 @@ var gMainPane = {
   confirmBrowserLanguageChange(event) {
     let localesString = (event.target.getAttribute("locales") || "").trim();
     if (!localesString || localesString.length == 0) {
       return;
     }
     let locales = localesString.split(",");
     Services.locale.requestedLocales = locales;
 
+    // Record the change in telemetry before we restart.
+    gMainPane.recordBrowserLanguagesTelemetry("apply");
+
     // Restart with the new locale.
     let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
     Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
     if (!cancelQuit.data) {
       Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
     }
   },
 
@@ -882,16 +889,19 @@ var gMainPane = {
     if (locale == "search") {
       gMainPane.showBrowserLanguages({search: true});
       return;
     } else if (locale == Services.locale.appLocaleAsBCP47) {
       this.hideConfirmLanguageChangeMessageBar();
       return;
     }
 
+    // Note the change in telemetry.
+    gMainPane.recordBrowserLanguagesTelemetry("reorder");
+
     let locales = Array.from(new Set([
       locale,
       ...Services.locale.requestedLocales,
     ]).values());
     this.showConfirmLanguageChangeMessageBar(locales);
   },
 
   onBrowserRestoreSessionChange(event) {
@@ -1019,28 +1029,39 @@ var gMainPane = {
 
   /**
    * Shows a dialog in which the preferred language for web content may be set.
    */
   showLanguages() {
     gSubDialog.open("chrome://browser/content/preferences/languages.xul");
   },
 
+  recordBrowserLanguagesTelemetry(method, value = null) {
+    Services.telemetry.recordEvent("intl.ui.browserLanguage", method, "main", value);
+  },
+
   showBrowserLanguages({search}) {
-    let opts = {selected: gMainPane.selectedLocales, search};
+    // Record the telemetry event with an id to associate related actions.
+    let telemetryId = parseInt(Services.telemetry.msSinceProcessStart(), 10).toString();
+    let method = search ? "search" : "manage";
+    gMainPane.recordBrowserLanguagesTelemetry(method, telemetryId);
+
+    let opts = {selected: gMainPane.selectedLocales, search, telemetryId};
     gSubDialog.open(
       "chrome://browser/content/preferences/browserLanguages.xul",
       null, opts, this.browserLanguagesClosed);
   },
 
   /* Show or hide the confirm change message bar based on the updated ordering. */
   browserLanguagesClosed() {
-    let selected = this.gBrowserLanguagesDialog.selected;
+    let {accepted, selected} = this.gBrowserLanguagesDialog;
     let active = Services.locale.appLocalesAsBCP47;
 
+    this.gBrowserLanguagesDialog.recordTelemetry(accepted ? "accept" : "cancel");
+
     // Prepare for changing the locales if they are different than the current locales.
     if (selected && selected.join(",") != active.join(",")) {
       gMainPane.showConfirmLanguageChangeMessageBar(selected);
       gMainPane.setBrowserLocales(selected[0]);
       return;
     }
 
     // They matched, so we can reset the UI.
--- a/browser/components/preferences/in-content/tests/browser_browser_languages_subdialog.js
+++ b/browser/components/preferences/in-content/tests/browser_browser_languages_subdialog.js
@@ -4,16 +4,17 @@
 ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 
 AddonTestUtils.initMochitest(this);
 
 const BROWSER_LANGUAGES_URL = "chrome://browser/content/preferences/browserLanguages.xul";
 const DICTIONARY_ID_PL = "pl@dictionaries.addons.mozilla.org";
+const TELEMETRY_CATEGORY = "intl.ui.browserLanguage";
 
 function langpackId(locale) {
   return `langpack-${locale}@firefox.mozilla.org`;
 }
 
 function getManifestData(locale, version = "2.0") {
   return {
     langpack_id: locale,
@@ -144,16 +145,36 @@ function assertAvailableLocales(list, lo
   let listLocales = items
     .filter(item => item.value && item.value != "search");
   is(listLocales.length, locales.length, "The right number of locales are available");
   is(listLocales.map(item => item.value).sort(),
      locales.sort().join(","), "The available locales match");
   is(items[0].getAttribute("class"), "label-item", "The first row is a label");
 }
 
+function getDialogId(dialogDoc) {
+  return dialogDoc.ownerGlobal.arguments[0].telemetryId;
+}
+
+function assertTelemetryRecorded(events) {
+  let snapshot = Services.telemetry.snapshotEvents(
+    Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
+
+  // Make sure we got some data.
+  ok(snapshot.parent && snapshot.parent.length > 0, "Got parent telemetry events in the snapshot");
+
+  // Only look at the related events after stripping the timestamp and category.
+  let relatedEvents = snapshot.parent
+    .filter(([timestamp, category]) => category == TELEMETRY_CATEGORY)
+    .map(relatedEvent => relatedEvent.slice(2, 6));
+
+  // Events are now an array of: method, object[, value[, extra]] as expected.
+  Assert.deepEqual(relatedEvents, events, "The events are recorded correctly");
+}
+
 function selectLocale(localeCode, available, dialogDoc) {
   let [locale] = Array.from(available.firstElementChild.children)
     .filter(item => item.value == localeCode);
   available.selectedItem = locale;
   dialogDoc.getElementById("add").doCommand();
 }
 
 async function openDialog(doc, search = false) {
@@ -243,18 +264,33 @@ add_task(async function testDisabledBrow
     target => selected.itemCount == 3);
   assertLocaleOrder(selected, "pl,en-US,he");
 
   // Find pl again since it's been upgraded.
   pl = await AddonManager.getAddonByID(langpackId("pl"));
   is(pl.userDisabled, false, "pl is now enabled");
   is(pl.version, "2.0", "pl is upgraded to version 2.0");
 
+  let dialogId = getDialogId(dialogDoc);
+  ok(dialogId, "There's a dialogId");
+  let {installId} = pl.install;
+  ok(installId, "There's an installId");
+
   await Promise.all(addons.map(addon => addon.uninstall()));
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  // FIXME: Also here
+  assertTelemetryRecorded([
+    ["manage", "main", dialogId],
+    ["search", "dialog", dialogId],
+    ["add", "dialog", dialogId, {installId}],
+
+    // Cancel is recorded when the tab is closed.
+    ["cancel", "dialog", dialogId],
+  ]);
 });
 
 add_task(async function testReorderingBrowserLanguages() {
   await SpecialPowers.pushPrefEnv({
     set: [
       ["intl.multilingual.enabled", true],
       ["intl.multilingual.downloadEnabled", true],
       ["intl.locale.requested", "en-US,pl,he,de"],
@@ -272,16 +308,17 @@ add_task(async function testReorderingBr
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
 
   let doc = gBrowser.contentDocument;
   let messageBar = doc.getElementById("confirmBrowserLanguage");
   is(messageBar.hidden, true, "The message bar is hidden at first");
 
   // Open the dialog.
   let {dialog, dialogDoc, selected} = await openDialog(doc);
+  let firstDialogId = getDialogId(dialogDoc);
 
   // The initial order is set by the pref, filtered by available.
   assertLocaleOrder(selected, "en-US,pl,he");
 
   // Moving pl down changes the order.
   selected.selectedItem = selected.querySelector("[value='pl']");
   dialogDoc.getElementById("down").doCommand();
   assertLocaleOrder(selected, "en-US,he,pl");
@@ -293,16 +330,17 @@ add_task(async function testReorderingBr
   is(messageBar.hidden, false, "The message bar is now visible");
   is(messageBar.querySelector("button").getAttribute("locales"), "en-US,he,pl",
      "The locales are set on the message bar button");
 
   // Open the dialog again.
   let newDialog = await openDialog(doc);
   dialog = newDialog.dialog;
   dialogDoc = newDialog.dialogDoc;
+  let secondDialogId = getDialogId(dialogDoc);
   selected = newDialog.selected;
 
   // The initial order comes from the previous settings.
   assertLocaleOrder(selected, "en-US,he,pl");
 
   // Select pl in the list.
   selected.selectedItem = selected.querySelector("[value='pl']");
   // Move pl back up.
@@ -310,19 +348,32 @@ add_task(async function testReorderingBr
   assertLocaleOrder(selected, "en-US,pl,he");
 
   // Accepting the change hides the confirm message bar.
   dialogClosed = BrowserTestUtils.waitForEvent(dialogDoc.documentElement, "dialogclosing");
   dialog.acceptDialog();
   await dialogClosed;
   is(messageBar.hidden, true, "The message bar is hidden again");
 
+  ok(firstDialogId, "There was an id on the first dialog");
+  ok(secondDialogId, "There was an id on the second dialog");
+  ok(firstDialogId != secondDialogId, "The dialog ids are different");
+  ok(firstDialogId < secondDialogId, "The second dialog id is larger than the first");
+
   await Promise.all(addons.map(addon => addon.uninstall()));
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  assertTelemetryRecorded([
+    ["manage", "main", firstDialogId],
+    ["reorder", "dialog", firstDialogId],
+    ["accept", "dialog", firstDialogId],
+    ["manage", "main", secondDialogId],
+    ["reorder", "dialog", secondDialogId],
+    ["accept", "dialog", secondDialogId],
+  ]);
 });
 
 add_task(async function testAddAndRemoveSelectedLanguages() {
   await SpecialPowers.pushPrefEnv({
     set: [
       ["intl.multilingual.enabled", true],
       ["intl.multilingual.downloadEnabled", true],
       ["intl.locale.requested", "en-US"],
@@ -339,16 +390,17 @@ add_task(async function testAddAndRemove
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
 
   let doc = gBrowser.contentDocument;
   let messageBar = doc.getElementById("confirmBrowserLanguage");
   is(messageBar.hidden, true, "The message bar is hidden at first");
 
   // Open the dialog.
   let {dialog, dialogDoc, available, selected} = await openDialog(doc);
+  let dialogId = getDialogId(dialogDoc);
 
   // The initial order is set by the pref.
   assertLocaleOrder(selected, "en-US");
   assertAvailableLocales(available, ["fr", "pl", "he"]);
 
   // Add pl and fr to selected.
   selectLocale("pl", available, dialogDoc);
   selectLocale("fr", available, dialogDoc);
@@ -377,18 +429,31 @@ add_task(async function testAddAndRemove
     {attributes: true, attributeFilter: ["hidden"]},
     target => !target.hidden);
 
   is(messageBar.hidden, false, "The message bar is now visible");
   is(messageBar.querySelector("button").getAttribute("locales"), "he,en-US",
     "The locales are set on the message bar button");
 
   await Promise.all(addons.map(addon => addon.uninstall()));
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  assertTelemetryRecorded([
+    ["manage", "main", dialogId],
+
+    // Install id is not recorded since it was already installed.
+    ["add", "dialog", dialogId],
+    ["add", "dialog", dialogId],
+
+    ["remove", "dialog", dialogId],
+    ["remove", "dialog", dialogId],
+
+    ["add", "dialog", dialogId],
+    ["accept", "dialog", dialogId],
+  ]);
 });
 
 add_task(async function testInstallFromAMO() {
   let langpacks = await AddonManager.getAddonsByTypes(["locale"]);
   is(langpacks.length, 0, "There are no langpacks installed");
 
   let langpacksFile = await createLanguageToolsFile();
   let langpacksUrl = Services.io.newFileURI(langpacksFile).spec;
@@ -409,16 +474,17 @@ add_task(async function testInstallFromA
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
 
   let doc = gBrowser.contentDocument;
   let messageBar = doc.getElementById("confirmBrowserLanguage");
   is(messageBar.hidden, true, "The message bar is hidden at first");
 
   // Open the dialog.
   let {dialog, dialogDoc, available, selected} = await openDialog(doc, true);
+  let firstDialogId = getDialogId(dialogDoc);
 
   // Make sure the message bar is still hidden.
   is(messageBar.hidden, true, "The message bar is still hidden after searching");
 
   if (available.itemCount == 1) {
     await waitForMutation(
       available.firstElementChild,
       {childList: true},
@@ -440,16 +506,22 @@ add_task(async function testInstallFromA
 
   // Wait for the langpack to install and be added to the list.
   let selectedLocales = dialogDoc.getElementById("selectedLocales");
   await waitForMutation(
     selectedLocales,
     {childList: true},
     target => selectedLocales.itemCount == 2);
 
+  let langpack = await AddonManager.getAddonByID(langpackId("pl"));
+  Assert.deepEqual(
+    langpack.installTelemetryInfo,
+    {source: "about:preferences"},
+    "The source is set to preferences");
+
   // Verify the list is correct.
   assertLocaleOrder(selected, "pl,en-US");
   assertAvailableLocales(available, ["fr", "he"]);
   is(Services.locale.availableLocales.sort().join(","),
      "en-US,pl", "Polish is now installed");
 
   await BrowserTestUtils.waitForCondition(async () => {
     let newDicts = await AddonManager.getAddonsByTypes(["dictionary"]);
@@ -467,20 +539,22 @@ add_task(async function testInstallFromA
   assertLocaleOrder(selected, "en-US,pl");
 
   // Test that disabling the langpack removes it from the list.
   let dialogClosed = BrowserTestUtils.waitForEvent(dialogDoc.documentElement, "dialogclosing");
   dialog.acceptDialog();
   await dialogClosed;
 
   // Disable the Polish langpack.
-  let langpack = await AddonManager.getAddonByID("langpack-pl@firefox.mozilla.org");
+  langpack = await AddonManager.getAddonByID("langpack-pl@firefox.mozilla.org");
+  let installId = langpack.install.installId;
   await langpack.disable();
 
   ({dialogDoc, available, selected} = await openDialog(doc, true));
+  let secondDialogId = getDialogId(dialogDoc);
 
   // Wait for the available langpacks to load.
   if (available.itemCount == 1) {
     await waitForMutation(
       available.firstElementChild,
       {childList: true},
       target => available.itemCount > 1);
   }
@@ -488,16 +562,32 @@ add_task(async function testInstallFromA
   assertAvailableLocales(available, ["fr", "he", "pl"]);
 
   // Uninstall the langpack and dictionary.
   let installs = await AddonManager.getAddonsByTypes(["locale", "dictionary"]);
   is(installs.length, 2, "There is one langpack and one dictionary installed");
   await Promise.all(installs.map(item => item.uninstall()));
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  ok(installId, "The langpack has an installId");
+  // FIXME: Most are here
+  assertTelemetryRecorded([
+    // First dialog installs a locale and accepts.
+    ["search", "main", firstDialogId],
+    // It has an installId since it was downloaded.
+    ["add", "dialog", firstDialogId, {installId}],
+    // It got moved down to avoid errors with finding translations.
+    ["reorder", "dialog", firstDialogId],
+    ["accept", "dialog", firstDialogId],
+
+    // The second dialog just checks the state and is closed with the tab.
+    ["search", "main", secondDialogId],
+    ["cancel", "dialog", secondDialogId],
+  ]);
 });
 
 let hasSearchOption = popup => Array.from(popup.children).some(el => el.value == "search");
 
 add_task(async function testDownloadEnabled() {
   await SpecialPowers.pushPrefEnv({
     set: [
       ["intl.multilingual.enabled", true],
@@ -532,8 +622,60 @@ add_task(async function testDownloadDisa
   let defaultMenulist = doc.getElementById("defaultBrowserLanguage");
   ok(!hasSearchOption(defaultMenulist.firstChild), "There's no search option in the General pane");
 
   let { available } = await openDialog(doc, false);
   ok(!hasSearchOption(available.firstChild), "There's no search option in the dialog");
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
+
+add_task(async function testReorderMainPane() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["intl.multilingual.enabled", true],
+      ["intl.multilingual.downloadEnabled", false],
+      ["intl.locale.requested", "en-US"],
+      ["extensions.langpacks.signatures.required", false],
+    ],
+  });
+
+  // Clear the telemetry from other tests.
+  Services.telemetry.clearEvents();
+
+  let langpacks = await createTestLangpacks();
+  let addons = await Promise.all(langpacks.map(async ([locale, file]) => {
+    let install = await AddonTestUtils.promiseInstallFile(file);
+    return install.addon;
+  }));
+
+  await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+  let doc = gBrowser.contentDocument;
+
+  let messageBar = doc.getElementById("confirmBrowserLanguage");
+  is(messageBar.hidden, true, "The message bar is hidden at first");
+
+  let available = doc.getElementById("defaultBrowserLanguage");
+  let availableLocales = Array.from(available.firstElementChild.children);
+  let availableCodes = availableLocales.map(item => item.value).sort().join(",");
+  is(availableCodes, "en-US,fr,he,pl",
+     "All of the available locales are listed");
+
+  is(available.selectedItem.value, "en-US", "English is selected");
+
+  let hebrew = availableLocales[availableLocales.findIndex(item => item.value == "he")];
+  hebrew.click();
+  available.firstElementChild.hidePopup();
+
+  await BrowserTestUtils.waitForCondition(
+    () => !messageBar.hidden, "Wait for message bar to show");
+
+  is(messageBar.hidden, false, "The message bar is now shown");
+  is(messageBar.querySelector("button").getAttribute("locales"), "he,en-US",
+     "The locales are set on the message bar button");
+
+  await Promise.all(addons.map(addon => addon.uninstall()));
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  assertTelemetryRecorded([
+    ["reorder", "main"],
+  ]);
+});
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1374,69 +1374,16 @@ nsDocShell::GetHasMixedDisplayContentBlo
     bool* aHasMixedDisplayContentBlocked) {
   RefPtr<Document> doc(GetDocument());
   *aHasMixedDisplayContentBlocked =
       doc && doc->GetHasMixedDisplayContentBlocked();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::GetHasTrackingContentBlocked(bool* aHasTrackingContentBlocked) {
-  RefPtr<Document> doc(GetDocument());
-  *aHasTrackingContentBlocked = doc && doc->GetHasTrackingContentBlocked();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::GetHasTrackingContentLoaded(bool* aHasTrackingContentLoaded) {
-  RefPtr<Document> doc(GetDocument());
-  *aHasTrackingContentLoaded = doc && doc->GetHasTrackingContentLoaded();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::GetHasCookiesBlockedByPermission(
-    bool* aHasCookiesBlockedByPermission) {
-  RefPtr<Document> doc(GetDocument());
-  *aHasCookiesBlockedByPermission =
-      doc && doc->GetHasCookiesBlockedByPermission();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::GetHasCookiesBlockedDueToTrackers(
-    bool* aHasCookiesBlockedDueToTrackers) {
-  RefPtr<Document> doc(GetDocument());
-  *aHasCookiesBlockedDueToTrackers = doc && doc->GetHasTrackingCookiesBlocked();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::GetHasAllCookiesBeenBlocked(bool* aHasAllCookiesBeenBlocked) {
-  RefPtr<Document> doc(GetDocument());
-  *aHasAllCookiesBeenBlocked = doc && doc->GetHasAllCookiesBlocked();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::GetHasForeignCookiesBeenBlocked(
-    bool* aHasForeignCookiesBeenBlocked) {
-  RefPtr<Document> doc(GetDocument());
-  *aHasForeignCookiesBeenBlocked = doc && doc->GetHasForeignCookiesBlocked();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::GetHasCookiesLoaded(bool* aHasCookiesLoaded) {
-  RefPtr<Document> doc(GetDocument());
-  *aHasCookiesLoaded = doc && doc->GetHasCookiesLoaded();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDocShell::GetAllowPlugins(bool* aAllowPlugins) {
   NS_ENSURE_ARG_POINTER(aAllowPlugins);
 
   *aAllowPlugins = mAllowPlugins;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -494,59 +494,16 @@ interface nsIDocShell : nsIDocShellTreeI
 
    /**
    * This attribute determines whether a document has Mixed Display Content
    * that has been blocked from loading. Similar behavior to
    * hasMixedActiveContentBlocked.
    */
   [infallible] readonly attribute boolean hasMixedDisplayContentBlocked;
 
-   /**
-   * This attribute determines whether a document has Tracking Content
-   * that has been blocked from loading.
-   */
-   [infallible] readonly attribute boolean hasTrackingContentBlocked;
-
-   /**
-   * This attribute determines whether Tracking Content is loaded on the
-   * document. When it is true, tracking content was not blocked and has
-   * loaded (or is about to load) on the page.
-   */
-  [infallible] readonly attribute boolean hasTrackingContentLoaded;
-
-   /**
-   * This attribute determines whether a document seen cookies or storage
-   * blocked due to a site permission being denied.
-   */
-   [infallible] readonly attribute boolean hasCookiesBlockedByPermission;
-
-   /**
-   * This attribute determines whether a document seen cookies or storage
-   * blocked due to a the request being made by a tracker.
-   */
-   [infallible] readonly attribute boolean hasCookiesBlockedDueToTrackers;
-
-   /**
-   * This attribute determines whether a document seen cookies or storage
-   * blocked due to cookie behavior settings blocking all cookies.
-   */
-   [infallible] readonly attribute boolean hasAllCookiesBeenBlocked;
-
-   /**
-   * This attribute determines whether a document seen cookies or storage
-   * blocked due to cookie behavior settings blocking all third-party cookies.
-   */
-   [infallible] readonly attribute boolean hasForeignCookiesBeenBlocked;
-
-   /**
-   * This attribute determines whether a document seen cookies or storage
-   * attempts ever whether they've been allowed or blocked.
-   */
-   [infallible] readonly attribute boolean hasCookiesLoaded;
-
   /**
    * Disconnects this docshell's editor from its window, and stores the
    * editor data in the open document's session history entry.  This
    * should be called only during page transitions.
    */
   [noscript, notxpcom] void DetachEditorFromWindow();
 
   /**
--- a/dom/asmjscache/AsmJSCache.cpp
+++ b/dom/asmjscache/AsmJSCache.cpp
@@ -3,16 +3,17 @@
 /* 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 "AsmJSCache.h"
 
 #include <stdio.h>
 
+#include "js/BuildId.h"  // JS::BuildIdCharVector
 #include "js/RootingAPI.h"
 #include "jsfriendapi.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryParent.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -69,16 +69,17 @@
 #include "mozilla/intl/LocaleService.h"
 #include "WindowDestroyedEvent.h"
 #include "nsDocShellLoadState.h"
 #include "mozilla/dom/WindowGlobalChild.h"
 
 // Helper Classes
 #include "nsJSUtils.h"
 #include "jsapi.h"
+#include "js/PropertySpec.h"
 #include "js/Wrapper.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsReadableUtils.h"
 #include "nsJSEnvironment.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Sprintf.h"
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -41,18 +41,19 @@
 #include <process.h>
 #define getpid _getpid
 #else
 #include <unistd.h>  // for getpid()
 #endif
 #include "xpcpublic.h"
 
 #include "jsapi.h"
+#include "js/PropertySpec.h"
+#include "js/SliceBudget.h"
 #include "js/Wrapper.h"
-#include "js/SliceBudget.h"
 #include "nsIArray.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "WrapperFactory.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/MainThreadIdlePeriod.h"
 #include "mozilla/StaticPrefs.h"
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5185,17 +5185,17 @@ def getJSToNativeConversionInfo(type, de
 
         dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
         if len(dateObjectMemberTypes) > 0:
             assert len(dateObjectMemberTypes) == 1
             memberType = dateObjectMemberTypes[0]
             name = getUnionMemberName(memberType)
             dateObject = CGGeneric("%s.SetTo%s(cx, ${val});\n"
                                    "done = true;\n" % (unionArgumentObj, name))
-            dateObject = CGIfWrapper(dateObject, "JS_ObjectIsDate(cx, argObj)")
+            dateObject = CGIfWrapper(dateObject, "JS::ObjectIsDate(cx, argObj)")
             names.append(name)
         else:
             dateObject = None
 
         callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
         if len(callbackMemberTypes) > 0:
             assert len(callbackMemberTypes) == 1
             memberType = callbackMemberTypes[0]
@@ -6202,17 +6202,17 @@ def getJSToNativeConversionInfo(type, de
         else:
             notDate = failureCode
 
         conversion = fill(
             """
             JS::Rooted<JSObject*> possibleDateObject(cx, &$${val}.toObject());
             { // scope for isDate
               bool isDate;
-              if (!JS_ObjectIsDate(cx, possibleDateObject, &isDate)) {
+              if (!JS::ObjectIsDate(cx, possibleDateObject, &isDate)) {
                 $*{exceptionCode}
               }
               if (!isDate) {
                 $*{notDate}
               }
               if (!${dateVal}.SetTimeStamp(cx, possibleDateObject)) {
                 $*{exceptionCode}
               }
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -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/. */
 
 #ifndef mozilla_dom_DOMJSClass_h
 #define mozilla_dom_DOMJSClass_h
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/PropertySpec.h"
 #include "js/Wrapper.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 
 #include "mozilla/dom/PrototypeList.h"  // auto-generated
 
 #include "mozilla/dom/JSSlots.h"
--- a/dom/svg/SVGAnimatedBoolean.h
+++ b/dom/svg/SVGAnimatedBoolean.h
@@ -2,45 +2,45 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 mozilla_dom_SVGAnimatedBoolean_h
 #define mozilla_dom_SVGAnimatedBoolean_h
 
+#include "SVGBoolean.h"
 #include "nsWrapperCache.h"
-#include "SVGElement.h"
 #include "mozilla/Attributes.h"
-#include "nsSVGBoolean.h"
+#include "mozilla/dom/SVGElement.h"
 
 namespace mozilla {
 namespace dom {
 
 class SVGAnimatedBoolean final : public nsWrapperCache {
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGAnimatedBoolean)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGAnimatedBoolean)
 
-  SVGAnimatedBoolean(nsSVGBoolean* aVal, SVGElement* aSVGElement)
+  SVGAnimatedBoolean(SVGBoolean* aVal, SVGElement* aSVGElement)
       : mVal(aVal), mSVGElement(aSVGElement) {}
 
   // WebIDL
   SVGElement* GetParentObject() const { return mSVGElement; }
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
   bool BaseVal() const { return mVal->GetBaseValue(); }
   void SetBaseVal(bool aValue) { mVal->SetBaseValue(aValue, mSVGElement); }
   bool AnimVal() const {
     mSVGElement->FlushAnimations();
     return mVal->GetAnimValue();
   }
 
  protected:
   ~SVGAnimatedBoolean();
 
-  nsSVGBoolean* mVal;  // kept alive because it belongs to content
+  SVGBoolean* mVal;  // kept alive because it belongs to content
   RefPtr<SVGElement> mSVGElement;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_SVGAnimatedBoolean_h
rename from dom/svg/nsSVGBoolean.cpp
rename to dom/svg/SVGBoolean.cpp
--- a/dom/svg/nsSVGBoolean.cpp
+++ b/dom/svg/SVGBoolean.cpp
@@ -1,30 +1,31 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "nsSVGBoolean.h"
+#include "SVGBoolean.h"
 
 #include "nsError.h"
 #include "nsSVGAttrTearoffTable.h"
 #include "nsSMILValue.h"
 #include "SMILBoolType.h"
 #include "SVGAnimatedBoolean.h"
 
-using namespace mozilla;
 using namespace mozilla::dom;
 
+namespace mozilla {
+
 /* Implementation */
 
-static inline nsSVGAttrTearoffTable<nsSVGBoolean, SVGAnimatedBoolean>&
+static inline nsSVGAttrTearoffTable<SVGBoolean, SVGAnimatedBoolean>&
 SVGAnimatedBooleanTearoffTable() {
-  static nsSVGAttrTearoffTable<nsSVGBoolean, SVGAnimatedBoolean>
+  static nsSVGAttrTearoffTable<SVGBoolean, SVGAnimatedBoolean>
       sSVGAnimatedBooleanTearoffTable;
   return sSVGAnimatedBooleanTearoffTable;
 }
 
 static bool GetValueFromString(const nsAString& aValueAsString, bool& aValue) {
   if (aValueAsString.EqualsLiteral("true")) {
     aValue = true;
     return true;
@@ -43,18 +44,18 @@ static nsresult GetValueFromAtom(const n
   }
   if (aValueAsAtom == nsGkAtoms::_false) {
     *aValue = false;
     return NS_OK;
   }
   return NS_ERROR_DOM_SYNTAX_ERR;
 }
 
-nsresult nsSVGBoolean::SetBaseValueAtom(const nsAtom* aValue,
-                                        SVGElement* aSVGElement) {
+nsresult SVGBoolean::SetBaseValueAtom(const nsAtom* aValue,
+                                      SVGElement* aSVGElement) {
   bool val = false;
 
   nsresult rv = GetValueFromAtom(aValue, &val);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   mBaseVal = val;
@@ -65,93 +66,95 @@ nsresult nsSVGBoolean::SetBaseValueAtom(
   }
 
   // We don't need to call DidChange* here - we're only called by
   // SVGElement::ParseAttribute under Element::SetAttr,
   // which takes care of notifying.
   return NS_OK;
 }
 
-nsAtom* nsSVGBoolean::GetBaseValueAtom() const {
+nsAtom* SVGBoolean::GetBaseValueAtom() const {
   return mBaseVal ? nsGkAtoms::_true : nsGkAtoms::_false;
 }
 
-void nsSVGBoolean::SetBaseValue(bool aValue, SVGElement* aSVGElement) {
+void SVGBoolean::SetBaseValue(bool aValue, SVGElement* aSVGElement) {
   if (aValue == mBaseVal) {
     return;
   }
 
   mBaseVal = aValue;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   } else {
     aSVGElement->AnimationNeedsResample();
   }
   aSVGElement->DidChangeBoolean(mAttrEnum);
 }
 
-void nsSVGBoolean::SetAnimValue(bool aValue, SVGElement* aSVGElement) {
+void SVGBoolean::SetAnimValue(bool aValue, SVGElement* aSVGElement) {
   if (mIsAnimated && mAnimVal == aValue) {
     return;
   }
   mAnimVal = aValue;
   mIsAnimated = true;
   aSVGElement->DidAnimateBoolean(mAttrEnum);
 }
 
-already_AddRefed<SVGAnimatedBoolean> nsSVGBoolean::ToDOMAnimatedBoolean(
+already_AddRefed<SVGAnimatedBoolean> SVGBoolean::ToDOMAnimatedBoolean(
     SVGElement* aSVGElement) {
   RefPtr<SVGAnimatedBoolean> domAnimatedBoolean =
       SVGAnimatedBooleanTearoffTable().GetTearoff(this);
   if (!domAnimatedBoolean) {
     domAnimatedBoolean = new SVGAnimatedBoolean(this, aSVGElement);
     SVGAnimatedBooleanTearoffTable().AddTearoff(this, domAnimatedBoolean);
   }
 
   return domAnimatedBoolean.forget();
 }
 
 SVGAnimatedBoolean::~SVGAnimatedBoolean() {
   SVGAnimatedBooleanTearoffTable().RemoveTearoff(mVal);
 }
 
-UniquePtr<nsISMILAttr> nsSVGBoolean::ToSMILAttr(SVGElement* aSVGElement) {
+UniquePtr<nsISMILAttr> SVGBoolean::ToSMILAttr(SVGElement* aSVGElement) {
   return MakeUnique<SMILBool>(this, aSVGElement);
 }
 
-nsresult nsSVGBoolean::SMILBool::ValueFromString(
+nsresult SVGBoolean::SMILBool::ValueFromString(
     const nsAString& aStr, const SVGAnimationElement* /*aSrcElement*/,
     nsSMILValue& aValue, bool& aPreventCachingOfSandwich) const {
   bool value;
   if (!GetValueFromString(aStr, value)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   nsSMILValue val(SMILBoolType::Singleton());
   val.mU.mBool = value;
   aValue = val;
   aPreventCachingOfSandwich = false;
 
   return NS_OK;
 }
 
-nsSMILValue nsSVGBoolean::SMILBool::GetBaseValue() const {
+nsSMILValue SVGBoolean::SMILBool::GetBaseValue() const {
   nsSMILValue val(SMILBoolType::Singleton());
   val.mU.mBool = mVal->mBaseVal;
   return val;
 }
 
-void nsSVGBoolean::SMILBool::ClearAnimValue() {
+void SVGBoolean::SMILBool::ClearAnimValue() {
   if (mVal->mIsAnimated) {
     mVal->mIsAnimated = false;
     mVal->mAnimVal = mVal->mBaseVal;
     mSVGElement->DidAnimateBoolean(mVal->mAttrEnum);
   }
 }
 
-nsresult nsSVGBoolean::SMILBool::SetAnimValue(const nsSMILValue& aValue) {
+nsresult SVGBoolean::SMILBool::SetAnimValue(const nsSMILValue& aValue) {
   NS_ASSERTION(aValue.mType == SMILBoolType::Singleton(),
                "Unexpected type to assign animated value");
   if (aValue.mType == SMILBoolType::Singleton()) {
     mVal->SetAnimValue(uint16_t(aValue.mU.mBool), mSVGElement);
   }
   return NS_OK;
 }
+
+}  // namespace mozilla
rename from dom/svg/nsSVGBoolean.h
rename to dom/svg/SVGBoolean.h
--- a/dom/svg/nsSVGBoolean.h
+++ b/dom/svg/SVGBoolean.h
@@ -17,19 +17,18 @@ class nsAtom;
 class nsSMILValue;
 
 namespace mozilla {
 namespace dom {
 class SVGAnimationElement;
 class SVGAnimatedBoolean;
 class SVGElement;
 }  // namespace dom
-}  // namespace mozilla
 
-class nsSVGBoolean {
+class SVGBoolean {
  public:
   typedef mozilla::dom::SVGElement SVGElement;
 
   void Init(uint8_t aAttrEnum = 0xff, bool aValue = false) {
     mAnimVal = mBaseVal = aValue;
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
   }
@@ -51,28 +50,31 @@ class nsSVGBoolean {
   bool mAnimVal;
   bool mBaseVal;
   bool mIsAnimated;
   uint8_t mAttrEnum;  // element specified tracking for attribute
 
  public:
   struct SMILBool : public nsISMILAttr {
    public:
-    SMILBool(nsSVGBoolean* aVal, SVGElement* aSVGElement)
+    SMILBool(SVGBoolean* aVal, SVGElement* aSVGElement)
         : mVal(aVal), mSVGElement(aSVGElement) {}
 
     // These will stay alive because a nsISMILAttr only lives as long
     // as the Compositing step, and DOM elements don't get a chance to
     // die during that.
-    nsSVGBoolean* mVal;
+    SVGBoolean* mVal;
     SVGElement* mSVGElement;
 
     // nsISMILAttr methods
     virtual nsresult ValueFromString(
         const nsAString& aStr,
         const mozilla::dom::SVGAnimationElement* aSrcElement,
         nsSMILValue& aValue, bool& aPreventCachingOfSandwich) const override;
     virtual nsSMILValue GetBaseValue() const override;
     virtual void ClearAnimValue() override;
     virtual nsresult SetAnimValue(const nsSMILValue& aValue) override;
   };
 };
+
+}  // namespace mozilla
+
 #endif  //__NS_SVGBOOLEAN_H__
--- a/dom/svg/SVGElement.cpp
+++ b/dom/svg/SVGElement.cpp
@@ -39,19 +39,19 @@
 #include "nsQueryObject.h"
 #include "nsLayoutUtils.h"
 #include "SVGAngle.h"
 #include "SVGAnimatedNumberList.h"
 #include "SVGAnimatedLengthList.h"
 #include "SVGAnimatedPointList.h"
 #include "SVGAnimatedPathSegList.h"
 #include "SVGAnimatedTransformList.h"
-#include "nsSVGBoolean.h"
+#include "SVGBoolean.h"
 #include "SVGEnum.h"
-#include "nsSVGInteger.h"
+#include "SVGInteger.h"
 #include "SVGIntegerPair.h"
 #include "nsSVGLength2.h"
 #include "SVGMotionSMILAttr.h"
 #include "nsSVGNumber2.h"
 #include "SVGNumberPair.h"
 #include "SVGString.h"
 #include "SVGViewBox.h"
 #include <stdarg.h>
@@ -420,17 +420,17 @@ bool SVGElement::ParseAttribute(int32_t 
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
-      // Check for nsSVGInteger attribute
+      // Check for SVGInteger attribute
       IntegerAttributesInfo integerInfo = GetIntegerInfo();
       for (i = 0; i < integerInfo.mIntegerCount; i++) {
         if (aAttribute == integerInfo.mIntegerInfo[i].mName) {
           rv = integerInfo.mIntegers[i].SetBaseValueString(aValue, this);
           if (NS_FAILED(rv)) {
             integerInfo.Reset(i);
           } else {
             aResult.SetTo(integerInfo.mIntegers[i].GetBaseValue(), &aValue);
@@ -475,17 +475,17 @@ bool SVGElement::ParseAttribute(int32_t 
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
-      // Check for nsSVGBoolean attribute
+      // Check for SVGBoolean attribute
       BooleanAttributesInfo booleanInfo = GetBooleanInfo();
       for (i = 0; i < booleanInfo.mBooleanCount; i++) {
         if (aAttribute == booleanInfo.mBooleanInfo[i].mName) {
           nsAtom* valAtom = NS_GetStaticAtom(aValue);
           rv = valAtom
                    ? booleanInfo.mBooleans[i].SetBaseValueAtom(valAtom, this)
                    : NS_ERROR_DOM_SYNTAX_ERR;
           if (NS_FAILED(rv)) {
--- a/dom/svg/SVGElement.h
+++ b/dom/svg/SVGElement.h
@@ -22,36 +22,36 @@
 #include "nsChangeHint.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsError.h"
 #include "nsISupportsImpl.h"
 #include "nsStyledElement.h"
 #include "gfxMatrix.h"
 
-class nsSVGBoolean;
-class nsSVGInteger;
 class nsSVGLength2;
 class nsSVGNumber2;
 
 nsresult NS_NewSVGElement(mozilla::dom::Element** aResult,
                           already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 class DeclarationBlock;
 
 class SVGAngle;
 class SVGAnimatedNumberList;
 class SVGAnimatedPathSegList;
 class SVGAnimatedPointList;
 class SVGAnimatedPreserveAspectRatio;
 class SVGAnimatedTransformList;
 class SVGAnimatedLengthList;
+class SVGBoolean;
 class SVGEnum;
 class SVGUserUnitList;
+class SVGInteger;
 class SVGIntegerPair;
 class SVGNumberList;
 class SVGNumberPair;
 class SVGString;
 class SVGStringList;
 class DOMSVGStringList;
 class SVGViewBox;
 
@@ -404,21 +404,21 @@ class SVGElement : public SVGElementBase
   };
 
   struct IntegerInfo {
     nsStaticAtom* const mName;
     const int32_t mDefaultValue;
   };
 
   struct IntegerAttributesInfo {
-    nsSVGInteger* const mIntegers;
+    SVGInteger* const mIntegers;
     const IntegerInfo* const mIntegerInfo;
     const uint32_t mIntegerCount;
 
-    IntegerAttributesInfo(nsSVGInteger* aIntegers, IntegerInfo* aIntegerInfo,
+    IntegerAttributesInfo(SVGInteger* aIntegers, IntegerInfo* aIntegerInfo,
                           uint32_t aIntegerCount)
         : mIntegers(aIntegers),
           mIntegerInfo(aIntegerInfo),
           mIntegerCount(aIntegerCount) {}
 
     void Reset(uint8_t aAttrEnum);
   };
 
@@ -462,21 +462,21 @@ class SVGElement : public SVGElementBase
   };
 
   struct BooleanInfo {
     nsStaticAtom* const mName;
     const bool mDefaultValue;
   };
 
   struct BooleanAttributesInfo {
-    nsSVGBoolean* const mBooleans;
+    SVGBoolean* const mBooleans;
     const BooleanInfo* const mBooleanInfo;
     const uint32_t mBooleanCount;
 
-    BooleanAttributesInfo(nsSVGBoolean* aBooleans, BooleanInfo* aBooleanInfo,
+    BooleanAttributesInfo(SVGBoolean* aBooleans, BooleanInfo* aBooleanInfo,
                           uint32_t aBooleanCount)
         : mBooleans(aBooleans),
           mBooleanInfo(aBooleanInfo),
           mBooleanCount(aBooleanCount) {}
 
     void Reset(uint8_t aAttrEnum);
   };
 
--- a/dom/svg/SVGFEConvolveMatrixElement.h
+++ b/dom/svg/SVGFEConvolveMatrixElement.h
@@ -3,20 +3,20 @@
 /* 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 mozilla_dom_SVGFEConvolveMatrixElement_h
 #define mozilla_dom_SVGFEConvolveMatrixElement_h
 
 #include "SVGAnimatedNumberList.h"
-#include "nsSVGBoolean.h"
+#include "SVGBoolean.h"
 #include "SVGEnum.h"
 #include "SVGFilters.h"
-#include "nsSVGInteger.h"
+#include "SVGInteger.h"
 #include "SVGIntegerPair.h"
 #include "nsSVGNumber2.h"
 #include "SVGString.h"
 
 nsresult NS_NewSVGFEConvolveMatrixElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
@@ -81,25 +81,25 @@ class SVGFEConvolveMatrixElement : publi
   nsSVGNumber2 mNumberAttributes[2];
   static NumberInfo sNumberInfo[2];
 
   enum { KERNEL_UNIT_LENGTH };
   SVGNumberPair mNumberPairAttributes[1];
   static NumberPairInfo sNumberPairInfo[1];
 
   enum { TARGET_X, TARGET_Y };
-  nsSVGInteger mIntegerAttributes[2];
+  SVGInteger mIntegerAttributes[2];
   static IntegerInfo sIntegerInfo[2];
 
   enum { ORDER };
   SVGIntegerPair mIntegerPairAttributes[1];
   static IntegerPairInfo sIntegerPairInfo[1];
 
   enum { PRESERVEALPHA };
-  nsSVGBoolean mBooleanAttributes[1];
+  SVGBoolean mBooleanAttributes[1];
   static BooleanInfo sBooleanInfo[1];
 
   enum { EDGEMODE };
   SVGEnum mEnumAttributes[1];
   static SVGEnumMapping sEdgeModeMap[];
   static EnumInfo sEnumInfo[1];
 
   enum { RESULT, IN1 };
--- a/dom/svg/SVGFETurbulenceElement.h
+++ b/dom/svg/SVGFETurbulenceElement.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SVGFETurbulenceElement_h
 #define mozilla_dom_SVGFETurbulenceElement_h
 
 #include "SVGEnum.h"
 #include "SVGFilters.h"
 #include "nsSVGNumber2.h"
-#include "nsSVGInteger.h"
+#include "SVGInteger.h"
 #include "SVGString.h"
 
 nsresult NS_NewSVGFETurbulenceElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
@@ -67,17 +67,17 @@ class SVGFETurbulenceElement : public SV
   nsSVGNumber2 mNumberAttributes[1];
   static NumberInfo sNumberInfo[1];
 
   enum { BASE_FREQ };
   SVGNumberPair mNumberPairAttributes[1];
   static NumberPairInfo sNumberPairInfo[1];
 
   enum { OCTAVES };
-  nsSVGInteger mIntegerAttributes[1];
+  SVGInteger mIntegerAttributes[1];
   static IntegerInfo sIntegerInfo[1];
 
   enum { TYPE, STITCHTILES };
   SVGEnum mEnumAttributes[2];
   static SVGEnumMapping sTypeMap[];
   static SVGEnumMapping sStitchTilesMap[];
   static EnumInfo sEnumInfo[2];
 
rename from dom/svg/nsSVGInteger.cpp
rename to dom/svg/SVGInteger.cpp
--- a/dom/svg/nsSVGInteger.cpp
+++ b/dom/svg/SVGInteger.cpp
@@ -1,54 +1,55 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "nsSVGInteger.h"
+#include "SVGInteger.h"
 
 #include "nsError.h"
 #include "nsSVGAttrTearoffTable.h"
 #include "nsSMILValue.h"
 #include "SMILIntegerType.h"
 #include "SVGContentUtils.h"
 
-using namespace mozilla;
 using namespace mozilla::dom;
 
+namespace mozilla {
+
 /* Implementation */
 
-static nsSVGAttrTearoffTable<nsSVGInteger, nsSVGInteger::DOMAnimatedInteger>
+static nsSVGAttrTearoffTable<SVGInteger, SVGInteger::DOMAnimatedInteger>
     sSVGAnimatedIntegerTearoffTable;
 
-nsresult nsSVGInteger::SetBaseValueString(const nsAString &aValueAsString,
-                                          SVGElement *aSVGElement) {
+nsresult SVGInteger::SetBaseValueString(const nsAString &aValueAsString,
+                                        SVGElement *aSVGElement) {
   int32_t value;
 
   if (!SVGContentUtils::ParseInteger(aValueAsString, value)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   mIsBaseSet = true;
   mBaseVal = value;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   } else {
     aSVGElement->AnimationNeedsResample();
   }
   return NS_OK;
 }
 
-void nsSVGInteger::GetBaseValueString(nsAString &aValueAsString) {
+void SVGInteger::GetBaseValueString(nsAString &aValueAsString) {
   aValueAsString.Truncate();
   aValueAsString.AppendInt(mBaseVal);
 }
 
-void nsSVGInteger::SetBaseValue(int aValue, SVGElement *aSVGElement) {
+void SVGInteger::SetBaseValue(int aValue, SVGElement *aSVGElement) {
   // We can't just rely on SetParsedAttrValue (as called by DidChangeInteger)
   // detecting redundant changes since it will compare false if the existing
   // attribute value has an associated serialized version (a string value) even
   // if the integers match due to the way integers are stored in nsAttrValue.
   if (aValue == mBaseVal && mIsBaseSet) {
     return;
   }
 
@@ -57,75 +58,77 @@ void nsSVGInteger::SetBaseValue(int aVal
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   } else {
     aSVGElement->AnimationNeedsResample();
   }
   aSVGElement->DidChangeInteger(mAttrEnum);
 }
 
-void nsSVGInteger::SetAnimValue(int aValue, SVGElement *aSVGElement) {
+void SVGInteger::SetAnimValue(int aValue, SVGElement *aSVGElement) {
   if (mIsAnimated && aValue == mAnimVal) {
     return;
   }
   mAnimVal = aValue;
   mIsAnimated = true;
   aSVGElement->DidAnimateInteger(mAttrEnum);
 }
 
-already_AddRefed<SVGAnimatedInteger> nsSVGInteger::ToDOMAnimatedInteger(
+already_AddRefed<SVGAnimatedInteger> SVGInteger::ToDOMAnimatedInteger(
     SVGElement *aSVGElement) {
   RefPtr<DOMAnimatedInteger> domAnimatedInteger =
       sSVGAnimatedIntegerTearoffTable.GetTearoff(this);
   if (!domAnimatedInteger) {
     domAnimatedInteger = new DOMAnimatedInteger(this, aSVGElement);
     sSVGAnimatedIntegerTearoffTable.AddTearoff(this, domAnimatedInteger);
   }
 
   return domAnimatedInteger.forget();
 }
 
-nsSVGInteger::DOMAnimatedInteger::~DOMAnimatedInteger() {
+SVGInteger::DOMAnimatedInteger::~DOMAnimatedInteger() {
   sSVGAnimatedIntegerTearoffTable.RemoveTearoff(mVal);
 }
 
-UniquePtr<nsISMILAttr> nsSVGInteger::ToSMILAttr(SVGElement *aSVGElement) {
+UniquePtr<nsISMILAttr> SVGInteger::ToSMILAttr(SVGElement *aSVGElement) {
   return MakeUnique<SMILInteger>(this, aSVGElement);
 }
 
-nsresult nsSVGInteger::SMILInteger::ValueFromString(
+nsresult SVGInteger::SMILInteger::ValueFromString(
     const nsAString &aStr, const dom::SVGAnimationElement * /*aSrcElement*/,
     nsSMILValue &aValue, bool &aPreventCachingOfSandwich) const {
   int32_t val;
 
   if (!SVGContentUtils::ParseInteger(aStr, val)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   nsSMILValue smilVal(SMILIntegerType::Singleton());
   smilVal.mU.mInt = val;
   aValue = smilVal;
   aPreventCachingOfSandwich = false;
   return NS_OK;
 }
 
-nsSMILValue nsSVGInteger::SMILInteger::GetBaseValue() const {
+nsSMILValue SVGInteger::SMILInteger::GetBaseValue() const {
   nsSMILValue val(SMILIntegerType::Singleton());
   val.mU.mInt = mVal->mBaseVal;
   return val;
 }
 
-void nsSVGInteger::SMILInteger::ClearAnimValue() {
+void SVGInteger::SMILInteger::ClearAnimValue() {
   if (mVal->mIsAnimated) {
     mVal->mIsAnimated = false;
     mVal->mAnimVal = mVal->mBaseVal;
     mSVGElement->DidAnimateInteger(mVal->mAttrEnum);
   }
 }
 
-nsresult nsSVGInteger::SMILInteger::SetAnimValue(const nsSMILValue &aValue) {
+nsresult SVGInteger::SMILInteger::SetAnimValue(const nsSMILValue &aValue) {
   NS_ASSERTION(aValue.mType == SMILIntegerType::Singleton(),
                "Unexpected type to assign animated value");
   if (aValue.mType == SMILIntegerType::Singleton()) {
     mVal->SetAnimValue(int(aValue.mU.mInt), mSVGElement);
   }
   return NS_OK;
 }
+
+}  // namespace mozilla
rename from dom/svg/nsSVGInteger.h
rename to dom/svg/SVGInteger.h
--- a/dom/svg/nsSVGInteger.h
+++ b/dom/svg/SVGInteger.h
@@ -16,19 +16,18 @@
 #include "mozilla/UniquePtr.h"
 
 class nsSMILValue;
 
 namespace mozilla {
 namespace dom {
 class SVGAnimationElement;
 }  // namespace dom
-}  // namespace mozilla
 
-class nsSVGInteger {
+class SVGInteger {
  public:
   typedef mozilla::dom::SVGElement SVGElement;
 
   void Init(uint8_t aAttrEnum = 0xff, int32_t aValue = 0) {
     mAnimVal = mBaseVal = aValue;
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
     mIsBaseSet = false;
@@ -58,50 +57,52 @@ class nsSVGInteger {
   int32_t mAnimVal;
   int32_t mBaseVal;
   uint8_t mAttrEnum;  // element specified tracking for attribute
   bool mIsAnimated;
   bool mIsBaseSet;
 
  public:
   struct DOMAnimatedInteger final : public mozilla::dom::SVGAnimatedInteger {
-    DOMAnimatedInteger(nsSVGInteger* aVal, SVGElement* aSVGElement)
+    DOMAnimatedInteger(SVGInteger* aVal, SVGElement* aSVGElement)
         : mozilla::dom::SVGAnimatedInteger(aSVGElement), mVal(aVal) {}
     virtual ~DOMAnimatedInteger();
 
-    nsSVGInteger* mVal;  // kept alive because it belongs to content
+    SVGInteger* mVal;  // kept alive because it belongs to content
 
     virtual int32_t BaseVal() override { return mVal->GetBaseValue(); }
     virtual void SetBaseVal(int32_t aValue) override {
       mVal->SetBaseValue(aValue, mSVGElement);
     }
 
     // Script may have modified animation parameters or timeline -- DOM getters
     // need to flush any resample requests to reflect these modifications.
     virtual int32_t AnimVal() override {
       mSVGElement->FlushAnimations();
       return mVal->GetAnimValue();
     }
   };
 
   struct SMILInteger : public nsISMILAttr {
    public:
-    SMILInteger(nsSVGInteger* aVal, SVGElement* aSVGElement)
+    SMILInteger(SVGInteger* aVal, SVGElement* aSVGElement)
         : mVal(aVal), mSVGElement(aSVGElement) {}
 
     // These will stay alive because a nsISMILAttr only lives as long
     // as the Compositing step, and DOM elements don't get a chance to
     // die during that.
-    nsSVGInteger* mVal;
+    SVGInteger* mVal;
     SVGElement* mSVGElement;
 
     // nsISMILAttr methods
     virtual nsresult ValueFromString(
         const nsAString& aStr,
         const mozilla::dom::SVGAnimationElement* aSrcElement,
         nsSMILValue& aValue, bool& aPreventCachingOfSandwich) const override;
     virtual nsSMILValue GetBaseValue() const override;
     virtual void ClearAnimValue() override;
     virtual nsresult SetAnimValue(const nsSMILValue& aValue) override;
   };
 };
 
+}  // namespace mozilla
+
 #endif  //__NS_SVGINTEGER_H__
--- a/dom/svg/moz.build
+++ b/dom/svg/moz.build
@@ -118,18 +118,16 @@ UNIFIED_SOURCES += [
     'DOMSVGPathSeg.cpp',
     'DOMSVGPathSegList.cpp',
     'DOMSVGPoint.cpp',
     'DOMSVGPointList.cpp',
     'DOMSVGStringList.cpp',
     'DOMSVGTransform.cpp',
     'DOMSVGTransformList.cpp',
     'nsISVGPoint.cpp',
-    'nsSVGBoolean.cpp',
-    'nsSVGInteger.cpp',
     'nsSVGLength2.cpp',
     'nsSVGNumber2.cpp',
     'SVGAElement.cpp',
     'SVGAngle.cpp',
     'SVGAnimatedAngle.cpp',
     'SVGAnimatedBoolean.cpp',
     'SVGAnimatedEnumeration.cpp',
     'SVGAnimatedInteger.cpp',
@@ -143,16 +141,17 @@ UNIFIED_SOURCES += [
     'SVGAnimatedRect.cpp',
     'SVGAnimatedString.cpp',
     'SVGAnimatedTransformList.cpp',
     'SVGAnimateElement.cpp',
     'SVGAnimateMotionElement.cpp',
     'SVGAnimateTransformElement.cpp',
     'SVGAnimationElement.cpp',
     'SVGAttrValueWrapper.cpp',
+    'SVGBoolean.cpp',
     'SVGCircleElement.cpp',
     'SVGClass.cpp',
     'SVGClipPathElement.cpp',
     'SVGContentUtils.cpp',
     'SVGDataParser.cpp',
     'SVGDefsElement.cpp',
     'SVGDescElement.cpp',
     'SVGDocument.cpp',
@@ -185,16 +184,17 @@ UNIFIED_SOURCES += [
     'SVGFilters.cpp',
     'SVGForeignObjectElement.cpp',
     'SVGFragmentIdentifier.cpp',
     'SVGGElement.cpp',
     'SVGGeometryElement.cpp',
     'SVGGradientElement.cpp',
     'SVGGraphicsElement.cpp',
     'SVGImageElement.cpp',
+    'SVGInteger.cpp',
     'SVGIntegerPair.cpp',
     'SVGIntegerPairSMILType.cpp',
     'SVGLength.cpp',
     'SVGLengthList.cpp',
     'SVGLengthListSMILType.cpp',
     'SVGLineElement.cpp',
     'SVGMarkerElement.cpp',
     'SVGMaskElement.cpp',
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-52956
+52975
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -6885,16 +6885,17 @@ Kronecker/M
 Kropotkin/M
 Kruger/M
 Krugerrand/M
 Krupp/M
 Krystal/M
 Krystyna/M
 Kshatriya/M
 Kuala/M
+Kubernetes/M
 Kublai/M
 Kubrick/M
 Kuhn/M
 Kuibyshev/M
 Kulthumm/M
 Kunming/M
 Kuomintang/M
 Kurd/M
@@ -14215,16 +14216,17 @@ airworthiness/M
 airworthy/P
 airy/PTR
 aisle/MS
 aitch/MS
 ajar
 aka
 akimbo
 akin
+al
 alabaster/M
 alack
 alacrity/M
 alarm/GMDS
 alarming/Y
 alarmist/SM
 alas
 alb/SM
@@ -20446,17 +20448,17 @@ comprehensibly/I
 comprehension/IM
 comprehensions
 comprehensive/PMYS
 comprehensiveness/M
 compress's
 compress/CGDS
 compressed/U
 compressible
-compression/CM
+compression/CMS
 compressor/SM
 comprise/GDS
 compromise/MGDS
 comptroller/MS
 compulsion/MS
 compulsive/YP
 compulsiveness/M
 compulsorily
@@ -22040,16 +22042,19 @@ cyst/MS
 cysteine/SM
 cystic
 cysticerci
 cysticercoid/S
 cysticercoses
 cysticercosis
 cysticercus
 cystitis
+cystoscope
+cystoscopic
+cystoscopy
 cytokine/SM
 cytologist/SM
 cytology/M
 cytoplasm/M
 cytoplasmic
 cytosine/M
 czar/MS
 czarina/SM
@@ -23490,16 +23495,18 @@ divisor/SM
 divorce/DSLMG
 divorcee/MS
 divorcement/MS
 divorcée/MS
 divot/SM
 divulge/GDS
 divvy/DSMG
 dixieland/M
+dizygotic
+dizygous
 dizzily
 dizziness/M
 dizzy/DRSPTG
 djellaba/MS
 djellabahs
 djinn
 do/SJMRHZG
 doable
@@ -25238,16 +25245,17 @@ estimation/M
 estimator/SM
 estoppel
 estrange/LDSG
 estrangement/MS
 estrogen/M
 estrous
 estrus/MS
 estuary/SM
+et
 eta/SM
 etc
 etch/DRSZGJ
 etcher/M
 etching/M
 eternal/YP
 eternalness/M
 eternity/SM
@@ -35586,16 +35594,17 @@ monastery/SM
 monastic/MS
 monastical/Y
 monasticism/M
 monaural
 monetarily
 monetarism/M
 monetarist/MS
 monetary
+monetization
 monetize/CGDS
 money/SMD
 moneybag/MS
 moneybox/S
 moneygrubber/SM
 moneygrubbing/M
 moneylender/SM
 moneymaker/SM
@@ -35624,16 +35633,17 @@ monochrome/MS
 monocle/DSM
 monoclonal
 monocotyledon/SM
 monocotyledonous
 monocular
 monodic
 monodist/SM
 monody/SM
+monofilament
 monogamist/MS
 monogamous/Y
 monogamy/M
 monogram/SM
 monogrammed
 monogramming
 monograph/M
 monographs
@@ -35667,16 +35677,18 @@ monotheistic
 monotone/MS
 monotonic
 monotonically
 monotonous/PY
 monotonousness/M
 monotony/M
 monounsaturated
 monoxide/MS
+monozygotic
+monozygous
 monseigneur/M
 monsieur/M
 monsignor/SM
 monsoon/SM
 monsoonal
 monster/SM
 monstrance/ASM
 monstrosity/SM
@@ -37203,16 +37215,17 @@ nursing/M
 nursling/SM
 nurture/DRSMZG
 nurturer/M
 nut/SM
 nutcase/S
 nutcracker/MS
 nuthatch/MS
 nuthouse/S
+nutjob/S
 nutmeat/SM
 nutmeg/SM
 nutpick/SM
 nutria/SM
 nutrient/MS
 nutriment/MS
 nutrition/M
 nutritional/Y
@@ -38658,16 +38671,17 @@ parlous
 parmigiana
 parmigiano
 parochial/Y
 parochialism/M
 parodist/SM
 parody/GDSM
 parole/MGDS
 parolee/MS
+paronychia
 parotid
 paroxysm/SM
 paroxysmal
 parquet/MDSG
 parquetry/M
 parred
 parricidal
 parricide/MS
@@ -40187,16 +40201,18 @@ polyhedral
 polyhedron/SM
 polymath/M
 polymaths
 polymer/SM
 polymeric
 polymerization/M
 polymerize/GDS
 polymorphic
+polymorphically
+polymorphism
 polymorphous
 polynomial/MS
 polynucleotide/SM
 polyp/MS
 polypeptide/SM
 polyphonic
 polyphony/M
 polypropylene/M
@@ -48896,16 +48912,17 @@ thirty/HSM
 this
 thistle/MS
 thistledown/M
 thither
 tho
 thole/SM
 thong/SM
 thoracic
+thoracotomy
 thorax/MS
 thorium/M
 thorn/SM
 thorniness/M
 thorny/PRT
 thorough/RYPT
 thoroughbred/MS
 thoroughfare/MS
@@ -50271,16 +50288,17 @@ ugliness/M
 ugly/RTP
 uh
 uhf
 ukase/SM
 ukulele/SM
 ulcer/SM
 ulcerate/XDSGN
 ulceration/M
+ulcerative
 ulcerous
 ulna/M
 ulnae
 ulnar
 ulster/MS
 ult
 ulterior
 ultimate/MY
@@ -50898,16 +50916,17 @@ usurer/SM
 usurious
 usurp/SDRZG
 usurpation/M
 usurper/M
 usury/M
 utensil/SM
 uteri
 uterine
+utero
 uterus/M
 utilitarian/MS
 utilitarianism/M
 utility/SM
 utilization/M
 utilize/GBDS
 utmost/M
 utopia/SM
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -285,18 +285,16 @@ void WebRenderLayerManager::EndTransacti
     aBackground->AddWebRenderCommands(builder);
     if (dumpEnabled) {
       printf_stderr("(no display list; background only)\n");
       builderDumpIndex =
           builder.Dump(/*indent*/ 1, Some(builderDumpIndex), Nothing());
     }
   }
 
-  mStateManager.DiscardCompositorAnimations();
-
   mWidget->AddWindowOverlayWebRenderCommands(WrBridge(), builder,
                                              resourceUpdates);
   mWindowOverlayChanged = false;
   if (dumpEnabled) {
     printf_stderr("(window overlay)\n");
     Unused << builder.Dump(/*indent*/ 1, Some(builderDumpIndex), Nothing());
   }
 
@@ -359,16 +357,21 @@ void WebRenderLayerManager::EndTransacti
     WrBridge()->EndTransaction(contentSize, dl, resourceUpdates,
                                size.ToUnknownSize(), mLatestTransactionId,
                                mScrollData, containsSVGGroup,
                                mTransactionIdAllocator->GetVsyncId(),
                                mTransactionIdAllocator->GetVsyncStart(),
                                refreshStart, mTransactionStart, mURL);
   }
 
+  // Discard animations after calling WrBridge()->EndTransaction().
+  // It updates mWrEpoch in WebRenderBridgeParent. The updated mWrEpoch is
+  // necessary for deleting animations at the correct time.
+  mStateManager.DiscardCompositorAnimations();
+
   mTransactionStart = TimeStamp();
 
   MakeSnapshotIfRequired(size);
   mNeedsComposite = false;
 }
 
 void WebRenderLayerManager::SetFocusTarget(const FocusTarget& aFocusTarget) {
   mFocusTarget = aFocusTarget;
--- a/gfx/vr/ipc/VRLayerChild.cpp
+++ b/gfx/vr/ipc/VRLayerChild.cpp
@@ -30,23 +30,17 @@ VRLayerChild::~VRLayerChild() {
 }
 
 void VRLayerChild::Initialize(dom::HTMLCanvasElement* aCanvasElement,
                               const gfx::Rect& aLeftEyeRect,
                               const gfx::Rect& aRightEyeRect) {
   MOZ_ASSERT(aCanvasElement);
   mLeftEyeRect = aLeftEyeRect;
   mRightEyeRect = aRightEyeRect;
-  if (mCanvasElement == nullptr) {
-    mCanvasElement = aCanvasElement;
-    VRManagerChild* vrmc = VRManagerChild::Get();
-    vrmc->RunFrameRequestCallbacks();
-  } else {
-    mCanvasElement = aCanvasElement;
-  }
+  mCanvasElement = aCanvasElement;
 }
 
 void VRLayerChild::SubmitFrame(const VRDisplayInfo& aDisplayInfo) {
   uint64_t frameId = aDisplayInfo.GetFrameId();
 
   // aFrameId will not increment unless the previuosly submitted
   // frame was received by the VR thread and submitted to the VR
   // compositor.  We early-exit here in the event that SubmitFrame
--- a/ipc/mscom/StructStream.h
+++ b/ipc/mscom/StructStream.h
@@ -47,20 +47,22 @@ class MOZ_NON_TEMPORARY_CLASS StructToSt
         mEncodedLen(0) {
     mStatus =
         ::MesEncodeDynBufferHandleCreate(&mBuffer, &mEncodedLen, &mHandle);
     if (mStatus != RPC_S_OK) {
       return;
     }
 
     MOZ_SEH_TRY { aEncodeFnPtr(mHandle, &aSrcStruct); }
+#ifdef HAVE_SEH_EXCEPTIONS
     MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
       mStatus = ::RpcExceptionCode();
       return;
     }
+#endif
 
     if (!mBuffer || !mEncodedLen) {
       mStatus = RPC_X_NO_MEMORY;
       return;
     }
   }
 
   ~StructToStream() {
@@ -205,20 +207,22 @@ class MOZ_NON_TEMPORARY_CLASS StructFrom
       return false;
     }
 
     // NB: Deserialization will fail with BSTRs unless the destination data
     //     is zeroed out!
     ZeroMemory(aDestStruct, sizeof(StructT));
 
     MOZ_SEH_TRY { aDecodeFnPtr(mHandle, aDestStruct); }
+#ifdef HAVE_SEH_EXCEPTIONS
     MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
       mStatus = ::RpcExceptionCode();
       return false;
     }
+#endif
 
     return true;
   }
 
   StructFromStream(const StructFromStream&) = delete;
   StructFromStream(StructFromStream&&) = delete;
   StructFromStream& operator=(const StructFromStream&) = delete;
   StructFromStream& operator=(StructFromStream&&) = delete;
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -13,16 +13,17 @@
 #include <unistd.h> /* for isatty() */
 #endif
 
 #include "base/basictypes.h"
 
 #include "jsapi.h"
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
+#include "js/PropertySpec.h"
 #include "js/SourceText.h"
 
 #include "xpcpublic.h"
 
 #include "XPCShellEnvironment.h"
 
 #include "mozilla/XPCOM.h"
 
new file mode 100644
--- /dev/null
+++ b/js/public/BuildId.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/**
+ * Embedding-provided build ID information, used by SpiderMonkey to tag cached
+ * compilation data so that cached data can be reused when possible, or
+ * discarded and regenerated if necessary.
+ */
+
+#ifndef js_BuildId_h
+#define js_BuildId_h
+
+#include "mozilla/Attributes.h"  // MOZ_MUST_USE
+
+#include "jstypes.h"  // JS_PUBLIC_API
+
+#include "js/Vector.h"  // js::Vector
+
+namespace js {
+
+class SystemAllocPolicy;
+
+}  // namespace js
+
+namespace JS {
+
+/** Vector of characters used for holding build ids. */
+using BuildIdCharVector = js::Vector<char, 0, js::SystemAllocPolicy>;
+
+/**
+ * Return the buildId (represented as a sequence of characters) associated with
+ * the currently-executing build. If the JS engine is embedded such that a
+ * single cache entry can be observed by different compiled versions of the JS
+ * engine, it is critical that the buildId shall change for each new build of
+ * the JS engine.
+ */
+using BuildIdOp = bool (*)(BuildIdCharVector* buildId);
+
+/**
+ * Embedder hook to set the buildId-generating function.
+ */
+extern JS_PUBLIC_API void SetProcessBuildIdOp(BuildIdOp buildIdOp);
+
+/**
+ * Some cached data is, in addition to being build-specific, CPU-specific: the
+ * cached data depends on CPU features like a particular level of SSE support.
+ *
+ * This function produces a buildId that includes:
+ *
+ *   * the buildId defined by the embedder-provided BuildIdOp set by
+ *     JS::SetProcessBuildIdOp, and
+ *   * CPU feature information for the current CPU.
+ *
+ * Embedders may use this function to tag cached data whose validity depends
+ * on having consistent buildId *and* on the CPU supporting features identical
+ * to those in play when the cached data was computed.
+ */
+extern MOZ_MUST_USE JS_PUBLIC_API bool GetOptimizedEncodingBuildId(
+    BuildIdCharVector* buildId);
+
+}  // namespace JS
+
+#endif /* js_BuildId_h */
--- a/js/public/CallArgs.h
+++ b/js/public/CallArgs.h
@@ -68,17 +68,17 @@
 #include "mozilla/TypeTraits.h"
 
 #include "jstypes.h"
 
 #include "js/RootingAPI.h"
 #include "js/Value.h"
 
 /* Typedef for native functions called by the JS VM. */
-typedef bool (*JSNative)(JSContext* cx, unsigned argc, JS::Value* vp);
+using JSNative = bool (*)(JSContext* cx, unsigned argc, JS::Value* vp);
 
 namespace JS {
 
 extern JS_PUBLIC_DATA const HandleValue UndefinedHandleValue;
 
 namespace detail {
 
 /*
--- a/js/public/Date.h
+++ b/js/public/Date.h
@@ -24,21 +24,25 @@
  * double type is a superset of the above set, so it *may* (but need not)
  * represent a date.  Use ECMAScript's |TimeClip| method to produce a date from
  * a double.
  *
  * Date *objects* are simply wrappers around |TimeClip|'d numbers, with a bunch
  * of accessor methods to the various aspects of the represented date.
  */
 
-#include "mozilla/FloatingPoint.h"
-#include "mozilla/MathAlgorithms.h"
+#include "mozilla/FloatingPoint.h"  // mozilla::{IsFinite,IsNaN}, mozilla::UnspecifiedNaN
+#include "mozilla/MathAlgorithms.h"  // mozilla::Abs
 
-#include "js/Conversions.h"
-#include "js/Value.h"
+#include "js/Conversions.h"  // JS::ToInteger
+#include "js/RootingAPI.h"   // JS::Handle
+#include "js/Value.h"        // JS::CanonicalizeNaN, JS::DoubleValue, JS::Value
+
+struct JSContext;
+class JSObject;
 
 namespace JS {
 
 /**
  * Re-query the system to determine the current time zone adjustment from UTC,
  * including any component due to DST.  If the time zone has changed, this will
  * cause all Date object non-UTC methods and formatting functions to produce
  * appropriately adjusted results.
@@ -105,24 +109,49 @@ inline ClippedTime TimeClip(double time)
 
   // Step 3.
   return ClippedTime(ToInteger(time) + (+0.0));
 }
 
 // Produce a double Value from the given time.  Because times may be NaN,
 // prefer using this to manual canonicalization.
 inline Value TimeValue(ClippedTime time) {
-  return DoubleValue(JS::CanonicalizeNaN(time.toDouble()));
+  return DoubleValue(CanonicalizeNaN(time.toDouble()));
 }
 
 // Create a new Date object whose [[DateValue]] internal slot contains the
 // clipped |time|.  (Users who must represent times outside that range must use
 // another representation.)
 extern JS_PUBLIC_API JSObject* NewDateObject(JSContext* cx, ClippedTime time);
 
+/**
+ * Create a new Date object for a year/month/day-of-month/hour/minute/second.
+ *
+ * The created date is initialized with the time value
+ *
+ *   TimeClip(UTC(MakeDate(MakeDay(year, mon, mday),
+ *                MakeTime(hour, min, sec, 0.0))))
+ *
+ * where each function/operation is as specified in ECMAScript.
+ */
+extern JS_PUBLIC_API JSObject* NewDateObject(JSContext* cx, int year, int mon,
+                                             int mday, int hour, int min,
+                                             int sec);
+
+/**
+ * On success, returns true, setting |*isDate| to true if |obj| is a Date
+ * object or a wrapper around one, or to false if not.  Returns false on
+ * failure.
+ *
+ * This method returns true with |*isDate == false| when passed an ES6 proxy
+ * whose target is a Date, or when passed a revoked proxy.
+ */
+extern JS_PUBLIC_API bool ObjectIsDate(JSContext* cx, Handle<JSObject*> obj,
+                                       bool* isDate);
+
 // Year is a year, month is 0-11, day is 1-based.  The return value is a number
 // of milliseconds since the epoch.
 //
 // Consistent with the MakeDate algorithm defined in ECMAScript, this value is
 // *not* clipped!  Use JS::TimeClip if you need a clipped date.
 JS_PUBLIC_API double MakeDate(double year, unsigned month, unsigned day);
 
 // Year is a year, month is 0-11, day is 1-based, and time is in milliseconds.
new file mode 100644
--- /dev/null
+++ b/js/public/Equality.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* Equality operations. */
+
+#ifndef js_Equality_h
+#define js_Equality_h
+
+#include "jstypes.h"  // JS_PUBLIC_API
+
+#include "js/RootingAPI.h"  // JS::Handle
+#include "js/Value.h"       // JS::Value
+
+struct JSContext;
+
+namespace JS {
+
+/**
+ * Store |v1 === v2| to |*equal| -- strict equality, which performs no
+ * conversions on |v1| or |v2| before comparing.
+ *
+ * This operation can fail only if an internal error occurs (e.g. OOM while
+ * linearizing a string value).
+ */
+extern JS_PUBLIC_API bool StrictlyEqual(JSContext* cx, JS::Handle<JS::Value> v1,
+                                        JS::Handle<JS::Value> v2, bool* equal);
+
+/**
+ * Store |v1 == v2| to |*equal| -- loose equality, which may perform
+ * user-modifiable conversions on |v1| or |v2|.
+ *
+ * This operation can fail if a user-modifiable conversion fails *or* if an
+ * internal error occurs. (e.g. OOM while linearizing a string value).
+ */
+extern JS_PUBLIC_API bool LooselyEqual(JSContext* cx, JS::Handle<JS::Value> v1,
+                                       JS::Handle<JS::Value> v2, bool* equal);
+
+/**
+ * Stores |SameValue(v1, v2)| to |*equal| -- using the SameValue operation
+ * defined in ECMAScript, initially exposed to script as |Object.is|.  SameValue
+ * behaves identically to strict equality, except that it equates two NaN values
+ * and does not equate differently-signed zeroes.  It performs no conversions on
+ * |v1| or |v2| before comparing.
+ *
+ * This operation can fail only if an internal error occurs (e.g. OOM while
+ * linearizing a string value).
+ */
+extern JS_PUBLIC_API bool SameValue(JSContext* cx, JS::Handle<JS::Value> v1,
+                                    JS::Handle<JS::Value> v2, bool* same);
+
+}  // namespace JS
+
+#endif /* js_Equality_h */
new file mode 100644
--- /dev/null
+++ b/js/public/PropertyDescriptor.h
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* Property descriptors and flags. */
+
+#ifndef js_PropertyDescriptor_h
+#define js_PropertyDescriptor_h
+
+#include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_ASSERT_IF
+
+#include <stdint.h>  // uint8_t
+
+#include "jstypes.h"  // JS_BROKEN_GCC_ATTRIBUTE_WARNING, JS_PUBLIC_API
+
+#include "js/Class.h"       // JS{Getter,Setter}Op
+#include "js/RootingAPI.h"  // JS::Handle, js::{,Mutable}WrappedPtrOperations
+#include "js/Value.h"       // JS::Value
+
+struct JSContext;
+class JSObject;
+
+#ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wattributes"
+#endif  // JS_BROKEN_GCC_ATTRIBUTE_WARNING
+
+class JS_PUBLIC_API JSTracer;
+
+#ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING
+#pragma GCC diagnostic pop
+#endif  // JS_BROKEN_GCC_ATTRIBUTE_WARNING
+
+/* Property attributes, set in JSPropertySpec and passed to API functions.
+ *
+ * The data structure in which some of these values are stored only uses a
+ * uint8_t to store the relevant information.  Proceed with caution if trying to
+ * reorder or change the the first byte worth of flags.
+ */
+
+/** The property is visible in for/in loops. */
+static constexpr uint8_t JSPROP_ENUMERATE = 0x01;
+
+/**
+ * The property is non-writable.  This flag is only valid when neither
+ * JSPROP_GETTER nor JSPROP_SETTER is set.
+ */
+static constexpr uint8_t JSPROP_READONLY = 0x02;
+
+/**
+ * The property is non-configurable: it can't be deleted, and if it's an
+ * accessor descriptor, its getter and setter can't be changed.
+ */
+static constexpr uint8_t JSPROP_PERMANENT = 0x04;
+
+/* (0x08 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
+
+/** The property has a getter function. */
+static constexpr uint8_t JSPROP_GETTER = 0x10;
+
+/** The property has a setter function. */
+static constexpr uint8_t JSPROP_SETTER = 0x20;
+
+/* (0x40 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
+
+/** A bit for internal JS engine use only. */
+static constexpr uint8_t JSPROP_INTERNAL_USE_BIT = 0x80;
+
+/* (0x1000 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
+
+/**
+ * Resolve hooks and enumerate hooks must pass this flag when calling
+ * JS_Define* APIs to reify lazily-defined properties.
+ *
+ * JSPROP_RESOLVING is used only with property-defining APIs. It tells the
+ * engine to skip the resolve hook when performing the lookup at the beginning
+ * of property definition. This keeps the resolve hook from accidentally
+ * triggering itself: unchecked recursion.
+ *
+ * For enumerate hooks, triggering the resolve hook would be merely silly, not
+ * fatal, except in some cases involving non-configurable properties.
+ */
+static constexpr unsigned JSPROP_RESOLVING = 0x2000;
+
+/**
+ * When redefining an existing property, ignore the value of the
+ * JSPROP_ENUMERATE flag.  This flag is ignored in other situations.
+ */
+static constexpr unsigned JSPROP_IGNORE_ENUMERATE = 0x4000;
+
+/**
+ * When redefining an existing property, ignore the value of the JSPROP_READONLY
+ * flag.  This flag is ignored in other situations.
+ */
+static constexpr unsigned JSPROP_IGNORE_READONLY = 0x8000;
+
+/**
+ * When redefining an existing property, ignore the value of the
+ * JSPROP_PERMANENT flag.  This flag is ignored in other situations.
+ */
+static constexpr unsigned JSPROP_IGNORE_PERMANENT = 0x10000;
+
+/**
+ * When redefining an existing property, ignore the Value in the descriptor.
+ * This flag is ignored in other situations.
+ */
+static constexpr unsigned JSPROP_IGNORE_VALUE = 0x20000;
+
+/* (higher flags are unused; add to JSPROP_FLAGS_MASK if ever defined) */
+
+static constexpr unsigned JSPROP_FLAGS_MASK =
+    JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_GETTER |
+    JSPROP_SETTER | JSPROP_INTERNAL_USE_BIT | JSPROP_RESOLVING |
+    JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT |
+    JSPROP_IGNORE_VALUE;
+
+namespace JS {
+
+/**
+ * A structure that represents a property on an object, or the absence of a
+ * property.  Use {,Mutable}Handle<PropertyDescriptor> to interact with
+ * instances of this structure rather than interacting directly with member
+ * fields.
+ */
+struct JS_PUBLIC_API PropertyDescriptor {
+  JSObject* obj = nullptr;
+  unsigned attrs = 0;
+  JSGetterOp getter = nullptr;
+  JSSetterOp setter = nullptr;
+  Value value;
+
+  PropertyDescriptor() = default;
+
+  static void trace(PropertyDescriptor* self, JSTracer* trc) {
+    self->trace(trc);
+  }
+  void trace(JSTracer* trc);
+};
+
+}  // namespace JS
+
+namespace js {
+
+template <typename Wrapper>
+class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
+  const JS::PropertyDescriptor& desc() const {
+    return static_cast<const Wrapper*>(this)->get();
+  }
+
+  bool has(unsigned bit) const {
+    MOZ_ASSERT(bit != 0);
+    MOZ_ASSERT((bit & (bit - 1)) == 0);  // only a single bit
+    return (desc().attrs & bit) != 0;
+  }
+
+  bool hasAny(unsigned bits) const { return (desc().attrs & bits) != 0; }
+
+  bool hasAll(unsigned bits) const { return (desc().attrs & bits) == bits; }
+
+ public:
+  // Descriptors with JSGetterOp/JSSetterOp are considered data
+  // descriptors. It's complicated.
+  bool isAccessorDescriptor() const {
+    return hasAny(JSPROP_GETTER | JSPROP_SETTER);
+  }
+  bool isGenericDescriptor() const {
+    return (desc().attrs & (JSPROP_GETTER | JSPROP_SETTER |
+                            JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE)) ==
+           (JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
+  }
+  bool isDataDescriptor() const {
+    return !isAccessorDescriptor() && !isGenericDescriptor();
+  }
+
+  bool hasConfigurable() const { return !has(JSPROP_IGNORE_PERMANENT); }
+  bool configurable() const {
+    MOZ_ASSERT(hasConfigurable());
+    return !has(JSPROP_PERMANENT);
+  }
+
+  bool hasEnumerable() const { return !has(JSPROP_IGNORE_ENUMERATE); }
+  bool enumerable() const {
+    MOZ_ASSERT(hasEnumerable());
+    return has(JSPROP_ENUMERATE);
+  }
+
+  bool hasValue() const {
+    return !isAccessorDescriptor() && !has(JSPROP_IGNORE_VALUE);
+  }
+  JS::HandleValue value() const {
+    return JS::Handle<JS::Value>::fromMarkedLocation(&desc().value);
+  }
+
+  bool hasWritable() const {
+    return !isAccessorDescriptor() && !has(JSPROP_IGNORE_READONLY);
+  }
+  bool writable() const {
+    MOZ_ASSERT(hasWritable());
+    return !has(JSPROP_READONLY);
+  }
+
+  bool hasGetterObject() const { return has(JSPROP_GETTER); }
+  JS::Handle<JSObject*> getterObject() const {
+    MOZ_ASSERT(hasGetterObject());
+    return JS::Handle<JSObject*>::fromMarkedLocation(
+        reinterpret_cast<JSObject* const*>(&desc().getter));
+  }
+  bool hasSetterObject() const { return has(JSPROP_SETTER); }
+  JS::Handle<JSObject*> setterObject() const {
+    MOZ_ASSERT(hasSetterObject());
+    return JS::Handle<JSObject*>::fromMarkedLocation(
+        reinterpret_cast<JSObject* const*>(&desc().setter));
+  }
+
+  bool hasGetterOrSetter() const { return desc().getter || desc().setter; }
+
+  JS::Handle<JSObject*> object() const {
+    return JS::Handle<JSObject*>::fromMarkedLocation(&desc().obj);
+  }
+  unsigned attributes() const { return desc().attrs; }
+  JSGetterOp getter() const { return desc().getter; }
+  JSSetterOp setter() const { return desc().setter; }
+
+  void assertValid() const {
+#ifdef DEBUG
+    MOZ_ASSERT(
+        (attributes() &
+         ~(JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE | JSPROP_PERMANENT |
+           JSPROP_IGNORE_PERMANENT | JSPROP_READONLY | JSPROP_IGNORE_READONLY |
+           JSPROP_IGNORE_VALUE | JSPROP_GETTER | JSPROP_SETTER |
+           JSPROP_RESOLVING | JSPROP_INTERNAL_USE_BIT)) == 0);
+    MOZ_ASSERT(!hasAll(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE));
+    MOZ_ASSERT(!hasAll(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT));
+    if (isAccessorDescriptor()) {
+      MOZ_ASSERT(!has(JSPROP_READONLY));
+      MOZ_ASSERT(!has(JSPROP_IGNORE_READONLY));
+      MOZ_ASSERT(!has(JSPROP_IGNORE_VALUE));
+      MOZ_ASSERT(!has(JSPROP_INTERNAL_USE_BIT));
+      MOZ_ASSERT(value().isUndefined());
+      MOZ_ASSERT_IF(!has(JSPROP_GETTER), !getter());
+      MOZ_ASSERT_IF(!has(JSPROP_SETTER), !setter());
+    } else {
+      MOZ_ASSERT(!hasAll(JSPROP_IGNORE_READONLY | JSPROP_READONLY));
+      MOZ_ASSERT_IF(has(JSPROP_IGNORE_VALUE), value().isUndefined());
+    }
+
+    MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_ENUMERATE));
+    MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_PERMANENT));
+    MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_READONLY));
+    MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_VALUE));
+#endif
+  }
+
+  void assertComplete() const {
+#ifdef DEBUG
+    assertValid();
+    MOZ_ASSERT(
+        (attributes() & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT |
+                          JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER |
+                          JSPROP_RESOLVING | JSPROP_INTERNAL_USE_BIT)) == 0);
+    MOZ_ASSERT_IF(isAccessorDescriptor(),
+                  has(JSPROP_GETTER) && has(JSPROP_SETTER));
+#endif
+  }
+
+  void assertCompleteIfFound() const {
+#ifdef DEBUG
+    if (object()) {
+      assertComplete();
+    }
+#endif
+  }
+};
+
+template <typename Wrapper>
+class MutableWrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
+    : public js::WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
+  JS::PropertyDescriptor& desc() { return static_cast<Wrapper*>(this)->get(); }
+
+ public:
+  void clear() {
+    object().set(nullptr);
+    setAttributes(0);
+    setGetter(nullptr);
+    setSetter(nullptr);
+    value().setUndefined();
+  }
+
+  void initFields(JS::Handle<JSObject*> obj, JS::Handle<JS::Value> v,
+                  unsigned attrs, JSGetterOp getterOp, JSSetterOp setterOp) {
+    object().set(obj);
+    value().set(v);
+    setAttributes(attrs);
+    setGetter(getterOp);
+    setSetter(setterOp);
+  }
+
+  void assign(JS::PropertyDescriptor& other) {
+    object().set(other.obj);
+    setAttributes(other.attrs);
+    setGetter(other.getter);
+    setSetter(other.setter);
+    value().set(other.value);
+  }
+
+  void setDataDescriptor(JS::Handle<JS::Value> v, unsigned attrs) {
+    MOZ_ASSERT((attrs & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT |
+                          JSPROP_READONLY | JSPROP_IGNORE_ENUMERATE |
+                          JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_READONLY)) ==
+               0);
+    object().set(nullptr);
+    setAttributes(attrs);
+    setGetter(nullptr);
+    setSetter(nullptr);
+    value().set(v);
+  }
+
+  JS::MutableHandle<JSObject*> object() {
+    return JS::MutableHandle<JSObject*>::fromMarkedLocation(&desc().obj);
+  }
+  unsigned& attributesRef() { return desc().attrs; }
+  JSGetterOp& getter() { return desc().getter; }
+  JSSetterOp& setter() { return desc().setter; }
+  JS::MutableHandle<JS::Value> value() {
+    return JS::MutableHandle<JS::Value>::fromMarkedLocation(&desc().value);
+  }
+  void setValue(JS::Handle<JS::Value> v) {
+    MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+    attributesRef() &= ~JSPROP_IGNORE_VALUE;
+    value().set(v);
+  }
+
+  void setConfigurable(bool configurable) {
+    setAttributes(
+        (desc().attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) |
+        (configurable ? 0 : JSPROP_PERMANENT));
+  }
+  void setEnumerable(bool enumerable) {
+    setAttributes(
+        (desc().attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) |
+        (enumerable ? JSPROP_ENUMERATE : 0));
+  }
+  void setWritable(bool writable) {
+    MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+    setAttributes((desc().attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) |
+                  (writable ? 0 : JSPROP_READONLY));
+  }
+  void setAttributes(unsigned attrs) { desc().attrs = attrs; }
+
+  void setGetter(JSGetterOp op) { desc().getter = op; }
+  void setSetter(JSSetterOp op) { desc().setter = op; }
+  void setGetterObject(JSObject* obj) {
+    desc().getter = reinterpret_cast<JSGetterOp>(obj);
+    desc().attrs &=
+        ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
+    desc().attrs |= JSPROP_GETTER;
+  }
+  void setSetterObject(JSObject* obj) {
+    desc().setter = reinterpret_cast<JSSetterOp>(obj);
+    desc().attrs &=
+        ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
+    desc().attrs |= JSPROP_SETTER;
+  }
+
+  JS::MutableHandle<JSObject*> getterObject() {
+    MOZ_ASSERT(this->hasGetterObject());
+    return JS::MutableHandle<JSObject*>::fromMarkedLocation(
+        reinterpret_cast<JSObject**>(&desc().getter));
+  }
+  JS::MutableHandle<JSObject*> setterObject() {
+    MOZ_ASSERT(this->hasSetterObject());
+    return JS::MutableHandle<JSObject*>::fromMarkedLocation(
+        reinterpret_cast<JSObject**>(&desc().setter));
+  }
+};
+
+}  // namespace js
+
+namespace JS {
+
+extern JS_PUBLIC_API bool ObjectToCompletePropertyDescriptor(
+    JSContext* cx, Handle<JSObject*> obj, Handle<Value> descriptor,
+    MutableHandle<PropertyDescriptor> desc);
+
+/*
+ * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc).
+ *
+ * If desc.object() is null, then vp is set to undefined.
+ */
+extern JS_PUBLIC_API bool FromPropertyDescriptor(
+    JSContext* cx, Handle<PropertyDescriptor> desc, MutableHandle<Value> vp);
+
+}  // namespace JS
+
+#endif /* js_PropertyDescriptor_h */
new file mode 100644
--- /dev/null
+++ b/js/public/PropertySpec.h
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* Property descriptors and flags. */
+
+#ifndef js_PropertySpec_h
+#define js_PropertySpec_h
+
+#include "mozilla/Assertions.h"  // MOZ_ASSERT{,_IF}
+
+#include <stddef.h>     // size_t
+#include <stdint.h>     // uint8_t, uint16_t, int32_t, uint32_t, uintptr_t
+#include <type_traits>  // std::enable_if
+
+#include "jstypes.h"  // JS_PUBLIC_API
+
+#include "js/CallArgs.h"            // JSNative
+#include "js/PropertyDescriptor.h"  // JSPROP_*
+#include "js/RootingAPI.h"          // JS::MutableHandle
+#include "js/Value.h"               // JS::Value
+
+struct JSContext;
+struct JSJitInfo;
+
+/**
+ * Wrapper to relace JSNative for JSPropertySpecs and JSFunctionSpecs. This will
+ * allow us to pass one JSJitInfo per function with the property/function spec,
+ * without additional field overhead.
+ */
+struct JSNativeWrapper {
+  JSNative op;
+  const JSJitInfo* info;
+};
+
+/**
+ * Macro static initializers which make it easy to pass no JSJitInfo as part of
+ * a JSPropertySpec or JSFunctionSpec.
+ */
+#define JSNATIVE_WRAPPER(native) \
+  {                              \
+    { native, nullptr }          \
+  }
+
+/**
+ * Description of a property. JS_DefineProperties and JS_InitClass take arrays
+ * of these and define many properties at once. JS_PSG, JS_PSGS and JS_PS_END
+ * are helper macros for defining such arrays.
+ */
+struct JSPropertySpec {
+  struct SelfHostedWrapper {
+    void* unused;
+    const char* funname;
+  };
+
+  struct ValueWrapper {
+    uintptr_t type;
+    union {
+      const char* string;
+      int32_t int32;
+    };
+  };
+
+  const char* name;
+  uint8_t flags;
+  union {
+    struct {
+      union {
+        JSNativeWrapper native;
+        SelfHostedWrapper selfHosted;
+      } getter;
+      union {
+        JSNativeWrapper native;
+        SelfHostedWrapper selfHosted;
+      } setter;
+    } accessors;
+    ValueWrapper value;
+  };
+
+  bool isAccessor() const { return !(flags & JSPROP_INTERNAL_USE_BIT); }
+  JS_PUBLIC_API bool getValue(JSContext* cx,
+                              JS::MutableHandle<JS::Value> value) const;
+
+  bool isSelfHosted() const {
+    MOZ_ASSERT(isAccessor());
+
+#ifdef DEBUG
+    // Verify that our accessors match our JSPROP_GETTER flag.
+    if (flags & JSPROP_GETTER) {
+      checkAccessorsAreSelfHosted();
+    } else {
+      checkAccessorsAreNative();
+    }
+#endif
+    return (flags & JSPROP_GETTER);
+  }
+
+  static_assert(sizeof(SelfHostedWrapper) == sizeof(JSNativeWrapper),
+                "JSPropertySpec::getter/setter must be compact");
+  static_assert(offsetof(SelfHostedWrapper, funname) ==
+                    offsetof(JSNativeWrapper, info),
+                "JS_SELF_HOSTED* macros below require that "
+                "SelfHostedWrapper::funname overlay "
+                "JSNativeWrapper::info");
+
+ private:
+  void checkAccessorsAreNative() const {
+    MOZ_ASSERT(accessors.getter.native.op);
+    // We may not have a setter at all.  So all we can assert here, for the
+    // native case is that if we have a jitinfo for the setter then we have
+    // a setter op too.  This is good enough to make sure we don't have a
+    // SelfHostedWrapper for the setter.
+    MOZ_ASSERT_IF(accessors.setter.native.info, accessors.setter.native.op);
+  }
+
+  void checkAccessorsAreSelfHosted() const {
+    MOZ_ASSERT(!accessors.getter.selfHosted.unused);
+    MOZ_ASSERT(!accessors.setter.selfHosted.unused);
+  }
+};
+
+namespace JS {
+namespace detail {
+
+/* NEVER DEFINED, DON'T USE.  For use by JS_CAST_STRING_TO only. */
+template <size_t N>
+inline int CheckIsCharacterLiteral(const char (&arr)[N]);
+
+/* NEVER DEFINED, DON'T USE.  For use by JS_CAST_INT32_TO only. */
+inline int CheckIsInt32(int32_t value);
+
+}  // namespace detail
+}  // namespace JS
+
+#define JS_CAST_STRING_TO(s, To)                                      \
+  (static_cast<void>(sizeof(JS::detail::CheckIsCharacterLiteral(s))), \
+   reinterpret_cast<To>(s))
+
+#define JS_CAST_INT32_TO(s, To)                            \
+  (static_cast<void>(sizeof(JS::detail::CheckIsInt32(s))), \
+   reinterpret_cast<To>(s))
+
+#define JS_CHECK_ACCESSOR_FLAGS(flags)                                         \
+  (static_cast<std::enable_if<((flags) & ~(JSPROP_ENUMERATE |                  \
+                                           JSPROP_PERMANENT)) == 0>::type>(0), \
+   (flags))
+
+#define JS_PS_ACCESSOR_SPEC(name, getter, setter, flags, extraFlags) \
+  {                                                                  \
+    name, uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | extraFlags), {    \
+      { getter, setter }                                             \
+    }                                                                \
+  }
+#define JS_PS_VALUE_SPEC(name, value, flags)          \
+  {                                                   \
+    name, uint8_t(flags | JSPROP_INTERNAL_USE_BIT), { \
+      { value, JSNATIVE_WRAPPER(nullptr) }            \
+    }                                                 \
+  }
+
+#define SELFHOSTED_WRAPPER(name)                           \
+  {                                                        \
+    { nullptr, JS_CAST_STRING_TO(name, const JSJitInfo*) } \
+  }
+#define STRINGVALUE_WRAPPER(value)                   \
+  {                                                  \
+    {                                                \
+      reinterpret_cast<JSNative>(JSVAL_TYPE_STRING), \
+          JS_CAST_STRING_TO(value, const JSJitInfo*) \
+    }                                                \
+  }
+#define INT32VALUE_WRAPPER(value)                   \
+  {                                                 \
+    {                                               \
+      reinterpret_cast<JSNative>(JSVAL_TYPE_INT32), \
+          JS_CAST_INT32_TO(value, const JSJitInfo*) \
+    }                                               \
+  }
+
+/*
+ * JSPropertySpec uses JSNativeWrapper.  These macros encapsulate the definition
+ * of JSNative-backed JSPropertySpecs, by defining the JSNativeWrappers for
+ * them.
+ */
+#define JS_PSG(name, getter, flags)                   \
+  JS_PS_ACCESSOR_SPEC(name, JSNATIVE_WRAPPER(getter), \
+                      JSNATIVE_WRAPPER(nullptr), flags, 0)
+#define JS_PSGS(name, getter, setter, flags)          \
+  JS_PS_ACCESSOR_SPEC(name, JSNATIVE_WRAPPER(getter), \
+                      JSNATIVE_WRAPPER(setter), flags, 0)
+#define JS_SYM_GET(symbol, getter, flags)                                    \
+  JS_PS_ACCESSOR_SPEC(                                                       \
+      reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
+      JSNATIVE_WRAPPER(getter), JSNATIVE_WRAPPER(nullptr), flags, 0)
+#define JS_SELF_HOSTED_GET(name, getterName, flags)         \
+  JS_PS_ACCESSOR_SPEC(name, SELFHOSTED_WRAPPER(getterName), \
+                      JSNATIVE_WRAPPER(nullptr), flags, JSPROP_GETTER)
+#define JS_SELF_HOSTED_GETSET(name, getterName, setterName, flags) \
+  JS_PS_ACCESSOR_SPEC(name, SELFHOSTED_WRAPPER(getterName),        \
+                      SELFHOSTED_WRAPPER(setterName), flags,       \
+                      JSPROP_GETTER | JSPROP_SETTER)
+#define JS_SELF_HOSTED_SYM_GET(symbol, getterName, flags)                    \
+  JS_PS_ACCESSOR_SPEC(                                                       \
+      reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
+      SELFHOSTED_WRAPPER(getterName), JSNATIVE_WRAPPER(nullptr), flags,      \
+      JSPROP_GETTER)
+#define JS_STRING_PS(name, string, flags) \
+  JS_PS_VALUE_SPEC(name, STRINGVALUE_WRAPPER(string), flags)
+#define JS_STRING_SYM_PS(symbol, string, flags)                              \
+  JS_PS_VALUE_SPEC(                                                          \
+      reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
+      STRINGVALUE_WRAPPER(string), flags)
+#define JS_INT32_PS(name, value, flags) \
+  JS_PS_VALUE_SPEC(name, INT32VALUE_WRAPPER(value), flags)
+#define JS_PS_END                                         \
+  JS_PS_ACCESSOR_SPEC(nullptr, JSNATIVE_WRAPPER(nullptr), \
+                      JSNATIVE_WRAPPER(nullptr), 0, 0)
+
+/**
+ * To define a native function, set call to a JSNativeWrapper. To define a
+ * self-hosted function, set selfHostedName to the name of a function
+ * compiled during JSRuntime::initSelfHosting.
+ */
+struct JSFunctionSpec {
+  const char* name;
+  JSNativeWrapper call;
+  uint16_t nargs;
+  uint16_t flags;
+  const char* selfHostedName;
+};
+
+/*
+ * Terminating sentinel initializer to put at the end of a JSFunctionSpec array
+ * that's passed to JS_DefineFunctions or JS_InitClass.
+ */
+#define JS_FS_END JS_FN(nullptr, nullptr, 0, 0)
+
+/*
+ * Initializer macros for a JSFunctionSpec array element. JS_FNINFO allows the
+ * simple adding of JSJitInfos. JS_SELF_HOSTED_FN declares a self-hosted
+ * function. JS_INLINABLE_FN allows specifying an InlinableNative enum value for
+ * natives inlined or specialized by the JIT. Finally JS_FNSPEC has slots for
+ * all the fields.
+ *
+ * The _SYM variants allow defining a function with a symbol key rather than a
+ * string key. For example, use JS_SYM_FN(iterator, ...) to define an
+ * @@iterator method.
+ */
+#define JS_FN(name, call, nargs, flags) \
+  JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr)
+#define JS_INLINABLE_FN(name, call, nargs, flags, native) \
+  JS_FNSPEC(name, call, &js::jit::JitInfo_##native, nargs, flags, nullptr)
+#define JS_SYM_FN(symbol, call, nargs, flags) \
+  JS_SYM_FNSPEC(symbol, call, nullptr, nargs, flags, nullptr)
+#define JS_FNINFO(name, call, info, nargs, flags) \
+  JS_FNSPEC(name, call, info, nargs, flags, nullptr)
+#define JS_SELF_HOSTED_FN(name, selfHostedName, nargs, flags) \
+  JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName)
+#define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags) \
+  JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName)
+#define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName)      \
+  JS_FNSPEC(                                                                 \
+      reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
+      call, info, nargs, flags, selfHostedName)
+#define JS_FNSPEC(name, call, info, nargs, flags, selfHostedName) \
+  { name, {call, info}, nargs, flags, selfHostedName }
+
+#endif  // js_PropertySpec_h
--- a/js/rust/build.rs
+++ b/js/rust/build.rs
@@ -382,29 +382,29 @@ const WHITELIST_FUNCTIONS: &'static [&'s
     "JS::NewPromiseObject",
     "JS_NewStringCopyN",
     "JS_NewUCStringCopyN",
     "JS_NewUint16Array",
     "JS_NewUint32Array",
     "JS_NewUint8Array",
     "JS_NewUint8ClampedArray",
     "js::ObjectClassName",
-    "JS_ObjectIsDate",
+    "JS::ObjectIsDate",
     "JS_ParseJSON",
     "JS_ReadBytes",
     "JS_ReadStructuredClone",
     "JS_ReadUint32Pair",
     "JS_RemoveExtraGCRootsTracer",
     "js::RemoveRawValueRoot",
     "JS_ReportErrorASCII",
     "JS_ReportErrorNumberUTF8",
     "JS_RequestInterruptCallback",
     "JS_ResolveStandardClass",
     "js::RunJobs",
-    "JS_SameValue",
+    "JS::SameValue",
     "js::SetDOMCallbacks",
     "js::SetDOMProxyInformation",
     "JS::SetEnqueuePromiseJobCallback",
     "js::SetFunctionNativeReserved",
     "JS_SetGCCallback",
     "JS::SetGCSliceCallback",
     "JS_SetGCParameter",
     "JS_SetGCZeal",
--- a/js/rust/etc/wrapper.hpp
+++ b/js/rust/etc/wrapper.hpp
@@ -11,18 +11,20 @@
 
 typedef uint32_t HashNumber;
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Conversions.h"
+#include "js/Date.h"
 #include "js/Initialization.h"
 #include "js/MemoryMetrics.h"
+#include "js/PropertySpec.h"
 #include "js/SourceText.h"
 #include "js/StructuredClone.h"
 
 // Replacements for types that are too difficult for rust-bindgen.
 
 /// <div rustbindgen replaces="JS::detail::MaybeWrapped" />
 template <typename T>
 using replaces_MaybeWrapped = T;
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -20,16 +20,17 @@
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/Sort.h"
 #include "gc/Heap.h"
 #include "jit/InlinableNatives.h"
 #include "js/Class.h"
 #include "js/Conversions.h"
+#include "js/PropertySpec.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -56,16 +56,17 @@
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsnum.h"
 
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
 #include "js/Class.h"
+#include "js/PropertySpec.h"
 #include "vm/GlobalObject.h"
 #include "vm/Time.h"
 #include "vm/TypedArrayObject.h"
 #include "wasm/WasmInstance.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
--- a/js/src/builtin/BigInt.cpp
+++ b/js/src/builtin/BigInt.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/BigInt.h"
 
 #include "jsapi.h"
 
 #include "builtin/TypedObject.h"
 #include "gc/Tracer.h"
+#include "js/PropertySpec.h"
 #include "js/TracingAPI.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/BigIntType.h"
 #include "vm/SelfHosting.h"
 #include "vm/TaggedProto.h"
 
 #include "vm/JSObject-inl.h"
 
--- a/js/src/builtin/Boolean.cpp
+++ b/js/src/builtin/Boolean.cpp
@@ -9,16 +9,17 @@
  */
 
 #include "builtin/Boolean-inl.h"
 
 #include "jsapi.h"
 #include "jstypes.h"
 
 #include "jit/InlinableNatives.h"
+#include "js/PropertySpec.h"
 #include "util/StringBuffer.h"
 #ifdef ENABLE_BIGINT
 #include "vm/BigIntType.h"
 #endif
 #include "vm/GlobalObject.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
--- a/js/src/builtin/DataViewObject.cpp
+++ b/js/src/builtin/DataViewObject.cpp
@@ -13,16 +13,17 @@
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsnum.h"
 
 #include "builtin/Array.h"
 #include "jit/AtomicOperations.h"
 #include "js/Conversions.h"
+#include "js/PropertySpec.h"
 #include "js/Wrapper.h"
 #include "util/Windows.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/SharedMem.h"
--- a/js/src/builtin/JSON.cpp
+++ b/js/src/builtin/JSON.cpp
@@ -14,16 +14,17 @@
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "builtin/Array.h"
 #ifdef ENABLE_BIGINT
 #include "builtin/BigInt.h"
 #endif
 #include "builtin/String.h"
+#include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 #include "util/StringBuffer.h"
 #include "vm/Interpreter.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/JSONParser.h"
 
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -3,17 +3,19 @@
  * 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 "builtin/MapObject.h"
 
 #include "ds/OrderedHashTable.h"
 #include "gc/FreeOp.h"
+#include "js/PropertySpec.h"
 #include "js/Utility.h"
+#include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/SymbolType.h"
 
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -10,18 +10,20 @@
 
 #include "builtin/Promise.h"
 #include "builtin/SelfHostingDefines.h"
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "gc/Tracer.h"
+#include "js/PropertySpec.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
+#include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/SelfHosting.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
 
 using namespace js;
 
 static_assert(MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -11,19 +11,21 @@
 #ifdef ENABLE_BIGINT
 #include "builtin/BigInt.h"
 #endif
 #include "builtin/Eval.h"
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/String.h"
 #include "frontend/BytecodeCompiler.h"
 #include "jit/InlinableNatives.h"
+#include "js/PropertySpec.h"
 #include "js/UniquePtr.h"
 #include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
+#include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/JSContext.h"
 #include "vm/RegExpObject.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
--- a/js/src/builtin/Profilers.cpp
+++ b/js/src/builtin/Profilers.cpp
@@ -24,16 +24,17 @@
 #endif
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #endif
 
 #include "js/CharacterEncoding.h"
+#include "js/PropertySpec.h"
 #include "js/Utility.h"
 #include "util/Text.h"
 #include "vm/Probes.h"
 
 #include "vm/JSContext-inl.h"
 
 using namespace js;
 
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/TimeStamp.h"
 
 #include "jsexn.h"
 #include "jsfriendapi.h"
 
 #include "gc/Heap.h"
 #include "js/Debug.h"
+#include "js/PropertySpec.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/Debugger.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
 
--- a/js/src/builtin/Reflect.cpp
+++ b/js/src/builtin/Reflect.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 "builtin/Reflect.h"
 
 #include "builtin/Array.h"
 
 #include "jit/InlinableNatives.h"
+#include "js/PropertySpec.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/JSContext.h"
 #include "vm/Stack.h"
 
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -7,16 +7,17 @@
 #include "builtin/RegExp.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/TypeTraits.h"
 
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
 #include "jit/InlinableNatives.h"
+#include "js/PropertySpec.h"
 #include "util/StringBuffer.h"
 #include "util/Unicode.h"
 #include "vm/JSContext.h"
 #include "vm/RegExpStatics.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.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 "builtin/Stream.h"
 
 #include "js/Stream.h"
 
 #include "gc/Heap.h"
+#include "js/PropertySpec.h"
 #include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/Compartment-inl.h"
 #include "vm/List-inl.h"
 #include "vm/NativeObject-inl.h"
 
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -29,16 +29,17 @@
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/RegExp.h"
 #include "jit/InlinableNatives.h"
 #include "js/Conversions.h"
 #if !EXPOSE_INTL_API
 #include "js/LocaleSensitive.h"
 #endif
+#include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 #include "js/UniquePtr.h"
 #if ENABLE_INTL_API
 #include "unicode/uchar.h"
 #include "unicode/unorm2.h"
 #endif
 #include "util/StringBuffer.h"
 #include "util/Unicode.h"
--- a/js/src/builtin/Symbol.cpp
+++ b/js/src/builtin/Symbol.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * 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 "builtin/Symbol.h"
 
 #include "js/Symbol.h"
+#include "js/PropertySpec.h"
 #include "util/StringBuffer.h"
 #include "vm/SymbolType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using JS::Symbol;
 using namespace js;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -42,16 +42,17 @@
 #include "jit/InlinableNatives.h"
 #include "jit/JitRealm.h"
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/LocaleSensitive.h"
+#include "js/PropertySpec.h"
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 
 #include "jsutil.h"
 
 #include "gc/Marking.h"
 #include "js/CharacterEncoding.h"
+#include "js/PropertySpec.h"
 #include "js/Vector.h"
 #include "util/StringBuffer.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSFunction.h"
 #include "vm/Realm.h"
 #include "vm/SelfHosting.h"
 #include "vm/StringType.h"
 #include "vm/TypedArrayObject.h"
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/WeakMapObject-inl.h"
 
 #include "jsapi.h"
 
 #include "builtin/WeakSetObject.h"
 #include "gc/FreeOp.h"
+#include "js/PropertySpec.h"
 #include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 
 /* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::is(HandleValue v) {
--- a/js/src/builtin/WeakSetObject.cpp
+++ b/js/src/builtin/WeakSetObject.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 "builtin/WeakSetObject.h"
 
 #include "jsapi.h"
 
 #include "builtin/MapObject.h"
+#include "js/PropertySpec.h"
 #include "vm/GlobalObject.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 
 #include "builtin/WeakMapObject-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/JSObject-inl.h"
--- a/js/src/builtin/intl/Collator.cpp
+++ b/js/src/builtin/intl/Collator.cpp
@@ -13,16 +13,17 @@
 #include "jsapi.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "gc/FreeOp.h"
 #include "js/CharacterEncoding.h"
+#include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
--- a/js/src/builtin/intl/DateTimeFormat.cpp
+++ b/js/src/builtin/intl/DateTimeFormat.cpp
@@ -15,16 +15,17 @@
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "builtin/intl/TimeZoneDataGenerated.h"
 #include "gc/FreeOp.h"
 #include "js/CharacterEncoding.h"
+#include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 #include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -19,16 +19,17 @@
 #include "builtin/intl/DateTimeFormat.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
 #include "builtin/intl/RelativeTimeFormat.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "js/CharacterEncoding.h"
 #include "js/Class.h"
+#include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 
--- a/js/src/builtin/intl/NumberFormat.cpp
+++ b/js/src/builtin/intl/NumberFormat.cpp
@@ -16,16 +16,17 @@
 #include <stdint.h>
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "js/CharacterEncoding.h"
+#include "js/PropertySpec.h"
 #include "js/RootingAPI.h"
 #include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
 #include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 #include "vm/Stack.h"
 
 #include "vm/JSObject-inl.h"
--- a/js/src/builtin/intl/PluralRules.cpp
+++ b/js/src/builtin/intl/PluralRules.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
 #include "js/CharacterEncoding.h"
+#include "js/PropertySpec.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp
+++ b/js/src/builtin/intl/RelativeTimeFormat.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/FloatingPoint.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
 #include "js/CharacterEncoding.h"
+#include "js/PropertySpec.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::IsNegativeZero;
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -35,16 +35,17 @@
 #include "jsnum.h"
 
 #include "builtin/TypedObject.h"
 #include "ctypes/Library.h"
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "js/CharacterEncoding.h"
+#include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
 #include "util/Unicode.h"
 #include "util/Windows.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -7,16 +7,17 @@
 #include "ctypes/Library.h"
 
 #include "prerror.h"
 #include "prlink.h"
 
 #include "ctypes/CTypes.h"
 #include "js/CharacterEncoding.h"
 #include "js/MemoryFunctions.h"
+#include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 
 using JS::AutoStableStringChars;
 
 namespace js {
 namespace ctypes {
 
 /*******************************************************************************
--- a/js/src/frontend/BinASTParser.cpp
+++ b/js/src/frontend/BinASTParser.cpp
@@ -4114,16 +4114,18 @@ JS::Result<ParseNode*> BinASTParser<Tok>
     case UnaryOperator::Void:
       pnk = ParseNodeKind::VoidExpr;
       break;
     case UnaryOperator::Delete: {
       switch (operand->getKind()) {
         case ParseNodeKind::Name:
           operand->setOp(JSOP_DELNAME);
           pnk = ParseNodeKind::DeleteNameExpr;
+          BINJS_TRY(this->strictModeError(JSMSG_DEPRECATED_DELETE_OPERAND));
+          parseContext_->sc()->setBindingsAccessedDynamically();
           break;
         case ParseNodeKind::DotExpr:
           pnk = ParseNodeKind::DeletePropExpr;
           break;
         case ParseNodeKind::ElemExpr:
           pnk = ParseNodeKind::DeleteElemExpr;
           break;
         default:
--- a/js/src/frontend/BinASTParserPerTokenizer.cpp
+++ b/js/src/frontend/BinASTParserPerTokenizer.cpp
@@ -756,63 +756,31 @@ BinASTParserPerTokenizer<Tok>::raiseErro
 }
 
 template <typename Tok>
 void BinASTParserPerTokenizer<Tok>::poison() {
   tokenizer_.reset();
 }
 
 template <typename Tok>
-void BinASTParserPerTokenizer<Tok>::reportErrorNoOffsetVA(unsigned errorNumber,
-                                                          va_list args) {
-  ErrorMetadata metadata;
-  metadata.filename = getFilename();
-  metadata.lineNumber = 0;
-  metadata.columnNumber = offset();
-  metadata.isMuted = options().mutedErrors();
-  ReportCompileError(cx_, std::move(metadata), nullptr, JSREPORT_ERROR,
-                     errorNumber, args);
-}
-
-template <typename Tok>
-void BinASTParserPerTokenizer<Tok>::errorAtVA(uint32_t offset,
-                                              unsigned errorNumber,
-                                              va_list* args) {
-  ErrorMetadata metadata;
-  metadata.filename = getFilename();
-  metadata.lineNumber = 0;
-  metadata.columnNumber = offset;
-  metadata.isMuted = options().mutedErrors();
-  ReportCompileError(cx_, std::move(metadata), nullptr, JSREPORT_ERROR,
-                     errorNumber, *args);
-}
-
-template <typename Tok>
-bool BinASTParserPerTokenizer<Tok>::reportExtraWarningErrorNumberVA(
-    UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned errorNumber,
-    va_list* args) {
-  if (!options().extraWarningsOption) {
-    return true;
+bool BinASTParserPerTokenizer<Tok>::computeErrorMetadata(
+    ErrorMetadata* err, const ErrorOffset& errorOffset) {
+  err->filename = getFilename();
+  err->lineNumber = 0;
+  if (errorOffset.is<uint32_t>()) {
+    err->columnNumber = errorOffset.as<uint32_t>();
+  } else if (errorOffset.is<Current>()) {
+    err->columnNumber = offset();
+  } else {
+    errorOffset.is<NoOffset>();
+    err->columnNumber = 0;
   }
 
-  ErrorMetadata metadata;
-  metadata.filename = getFilename();
-  metadata.lineNumber = 0;
-  metadata.columnNumber = offset;
-  metadata.isMuted = options().mutedErrors();
-
-  if (options().werrorOption) {
-    ReportCompileError(cx_, std::move(metadata), std::move(notes),
-                       JSREPORT_STRICT, errorNumber, *args);
-    return false;
-  }
-
-  return ReportCompileWarning(cx_, std::move(metadata), std::move(notes),
-                              JSREPORT_STRICT | JSREPORT_WARNING, errorNumber,
-                              *args);
+  err->isMuted = options().mutedErrors();
+  return true;
 }
 
 void TraceBinParser(JSTracer* trc, JS::AutoGCRooter* parser) {
   static_cast<BinASTParserBase*>(parser)->trace(trc);
 }
 
 template <typename Tok>
 void BinASTParserPerTokenizer<Tok>::doTrace(JSTracer* trc) {
--- a/js/src/frontend/BinASTParserPerTokenizer.h
+++ b/js/src/frontend/BinASTParserPerTokenizer.h
@@ -26,16 +26,18 @@
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 
 #include "js/CompileOptions.h"
 #include "js/GCHashTable.h"
 #include "js/GCVector.h"
 #include "js/Result.h"
 
+#include "vm/ErrorReporting.h"
+
 namespace js {
 namespace frontend {
 
 template <typename Tok>
 class BinASTParser;
 
 /**
  * The parser for a Binary AST.
@@ -188,23 +190,34 @@ class BinASTParserPerTokenizer : public 
 
   MOZ_MUST_USE JS::Result<Ok> prependDirectivesImpl(ListNode* body,
                                                     ParseNode* directive);
 
   // Optionally force a strict context without restarting the parse when we see
   // a strict directive.
   void forceStrictIfNecessary(SharedContext* sc, ListNode* directives);
 
- protected:  // Implement ErrorReporter
+ protected:
+  // Implement ErrorReportMixin.
   const JS::ReadOnlyCompileOptions& options_;
 
   const JS::ReadOnlyCompileOptions& options() const override {
     return this->options_;
   }
 
+  JSContext* getContext() const override { return cx_; };
+
+  MOZ_MUST_USE bool strictMode() const override {
+    return parseContext_->sc()->strict();
+  }
+
+  MOZ_MUST_USE bool computeErrorMetadata(ErrorMetadata* err,
+                                         const ErrorOffset& offset) override;
+
+ private:
   void doTrace(JSTracer* trc) final;
 
  public:
   virtual ObjectBox* newObjectBox(JSObject* obj) override {
     MOZ_ASSERT(obj);
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
@@ -225,16 +238,19 @@ class BinASTParserPerTokenizer : public 
     return objbox;
   }
 
   virtual ErrorReporter& errorReporter() override { return *this; }
   virtual const ErrorReporter& errorReporter() const override { return *this; }
 
   virtual FullParseHandler& astGenerator() override { return factory_; }
 
+ public:
+  // Implement ErrorReporter.
+
   virtual void lineAndColumnAt(size_t offset, uint32_t* line,
                                uint32_t* column) const override {
     *line = lineAt(offset);
     *column = columnAt(offset);
   }
   virtual uint32_t lineAt(size_t offset) const override { return 0; }
   virtual uint32_t columnAt(size_t offset) const override { return offset; }
 
@@ -257,29 +273,21 @@ class BinASTParserPerTokenizer : public 
       return tokenizer_->offset();
     }
 
     return 0;
   }
   virtual bool hasTokenizationStarted() const override {
     return tokenizer_.isSome();
   }
-  virtual void reportErrorNoOffsetVA(unsigned errorNumber,
-                                     va_list args) override;
-  virtual void errorAtVA(uint32_t offset, unsigned errorNumber,
-                         va_list* args) override;
-  virtual bool reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes,
-                                               uint32_t offset,
-                                               unsigned errorNumber,
-                                               va_list* args) override;
   virtual const char* getFilename() const override {
     return this->options_.filename();
   }
 
- protected:  // Implement ErrorReporter
+ protected:
   mozilla::Maybe<Tokenizer> tokenizer_;
   VariableDeclarationKind variableDeclarationKind_;
 
   friend class BinParseContext;
   friend class AutoVariableDeclarationKind;
 
   // Helper class: Restore field `variableDeclarationKind` upon leaving a scope.
   class MOZ_RAII AutoVariableDeclarationKind {
--- a/js/src/frontend/BinSource.yaml
+++ b/js/src/frontend/BinSource.yaml
@@ -342,17 +342,17 @@ hpp:
 
             } // namespace frontend
             } // namespace js
 
             #endif // frontend_BinToken_h
 
 Arguments:
     init:
-        BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::ArgumentsExpr, tokenizer_->pos(start)));
+        BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::Arguments, tokenizer_->pos(start)));
     append:
         factory_.addList(/* list = */ result, /* kid = */ item);
 
 ArrayExpression:
     build: |
         if (elements->empty()) {
             elements->setHasNonConstInitializer();
         }
@@ -763,17 +763,17 @@ ContinueStatement:
 DataProperty:
     build: |
         if (!factory_.isUsableAsObjectPropertyName(name)) {
             return raiseError("DataProperty key kind");
         }
 
         ParseNode* result;
         if (name->template is<NameNode>() && name->template as<NameNode>().atom() == cx_->names().proto) {
-            BINJS_TRY_VAR(result, factory_.newUnary(ParseNodeKind::MutateProtoExpr, start, expression));
+            BINJS_TRY_VAR(result, factory_.newUnary(ParseNodeKind::MutateProto, start, expression));
         } else {
             BINJS_TRY_VAR(result, factory_.newObjectMethodOrPropertyDefinition(name, expression, AccessorType::None));
         }
 
 Directive:
     build: |
         TokenPos pos = tokenizer_->pos(start);
         BINJS_TRY_DECL(result, factory_.newStringLiteral(rawValue, pos));
@@ -1167,17 +1167,17 @@ ListOfSwitchCase:
         BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
     append:
         factory_.addCaseStatementToList(result, item);
 
 ListOfVariableDeclarator:
     type-ok:
         ListNode*
     init: |
-        BINJS_TRY_DECL(result, factory_.newDeclarationList(ParseNodeKind::ConstStmt /*Placeholder*/,
+        BINJS_TRY_DECL(result, factory_.newDeclarationList(ParseNodeKind::ConstDecl /*Placeholder*/,
             tokenizer_->pos(start)));
 
 LiteralBooleanExpression:
     build:
         BINJS_TRY_DECL(result, factory_.newBooleanLiteral(value, tokenizer_->pos(start)));
 
 LiteralNumericExpression:
     build:
@@ -1281,17 +1281,17 @@ Script:
 
 ShorthandProperty:
     build: |
         MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
         MOZ_ASSERT(!factory_.isUsableAsObjectPropertyName(name));
         BINJS_TRY_DECL(propName, factory_.newObjectLiteralPropertyName(name->template as<NameNode>().name(), tokenizer_->pos(start)));
 
         BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(propName, name, AccessorType::None));
-        result->setKind(ParseNodeKind::ShorthandExpr);
+        result->setKind(ParseNodeKind::Shorthand);
 
 SwitchCase:
     type-ok:
         CaseClause*
     build: |
         BINJS_TRY_DECL(result, factory_.newCaseOrDefault(start, test, consequent));
 
 SwitchDefault:
@@ -1422,16 +1422,18 @@ UnaryExpression:
           case UnaryOperator::Void:
             pnk = ParseNodeKind::VoidExpr;
             break;
           case UnaryOperator::Delete: {
             switch (operand->getKind()) {
               case ParseNodeKind::Name:
                 operand->setOp(JSOP_DELNAME);
                 pnk = ParseNodeKind::DeleteNameExpr;
+                BINJS_TRY(this->strictModeError(JSMSG_DEPRECATED_DELETE_OPERAND));
+                parseContext_->sc()->setBindingsAccessedDynamically();
                 break;
               case ParseNodeKind::DotExpr:
                 pnk = ParseNodeKind::DeletePropExpr;
                 break;
               case ParseNodeKind::ElemExpr:
                 pnk = ParseNodeKind::DeleteElemExpr;
                 break;
               default:
--- a/js/src/frontend/BinTokenReaderBase.cpp
+++ b/js/src/frontend/BinTokenReaderBase.cpp
@@ -24,17 +24,17 @@ void BinTokenReaderBase::updateLatestKno
   const size_t update = current_ - start_;
   MOZ_ASSERT(update >= latestKnownGoodPos_);
   latestKnownGoodPos_ = update;
 }
 
 ErrorResult<JS::Error&> BinTokenReaderBase::raiseError(
     const char* description) {
   MOZ_ASSERT(!hasRaisedError());
-  errorReporter_->reportErrorNoOffset(JSMSG_BINAST, description);
+  errorReporter_->errorNoOffset(JSMSG_BINAST, description);
   return cx_->alreadyReportedError();
 }
 
 ErrorResult<JS::Error&> BinTokenReaderBase::raiseOOM() {
   ReportOutOfMemory(cx_);
   return cx_->alreadyReportedError();
 }
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -12,16 +12,17 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ReverseIterator.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/Variant.h"
 
 #include <string.h>
 
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/Nestable.h"
@@ -57,16 +58,17 @@
 #include "wasm/AsmJS.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::AssertedCast;
+using mozilla::AsVariant;
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::NumberEqualsInt32;
 using mozilla::NumberIsInt32;
 using mozilla::PodCopy;
 using mozilla::Some;
 using mozilla::Unused;
@@ -436,17 +438,17 @@ static inline unsigned LengthOfSetLine(u
   return 1 /* SRC_SETLINE */ + (line > SN_4BYTE_OFFSET_MASK ? 4 : 1);
 }
 
 /* Updates line number notes, not column notes. */
 bool BytecodeEmitter::updateLineNumberNotes(uint32_t offset) {
   ErrorReporter* er = &parser->errorReporter();
   bool onThisLine;
   if (!er->isOnThisLine(offset, currentLine(), &onThisLine)) {
-    er->reportErrorNoOffset(JSMSG_OUT_OF_MEMORY);
+    er->errorNoOffset(JSMSG_OUT_OF_MEMORY);
     return false;
   }
 
   if (!onThisLine) {
     unsigned line = er->lineAt(offset);
     unsigned delta = line - currentLine();
 
     /*
@@ -1524,59 +1526,61 @@ void BytecodeEmitter::reportNeedMoreArgs
 
 void BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...) {
   MOZ_ASSERT_IF(!pn, this->scriptStartOffsetSet);
   uint32_t offset = pn ? pn->pn_pos.begin : this->scriptStartOffset;
 
   va_list args;
   va_start(args, errorNumber);
 
-  parser->errorReporter().errorAtVA(offset, errorNumber, &args);
+  parser->errorReporter().errorWithNotesAtVA(nullptr, AsVariant(offset),
+                                             errorNumber, &args);
 
   va_end(args);
 }
 
 void BytecodeEmitter::reportError(const Maybe<uint32_t>& maybeOffset,
                                   unsigned errorNumber, ...) {
   MOZ_ASSERT_IF(!maybeOffset, this->scriptStartOffsetSet);
   uint32_t offset = maybeOffset ? *maybeOffset : this->scriptStartOffset;
 
   va_list args;
   va_start(args, errorNumber);
 
-  parser->errorReporter().errorAtVA(offset, errorNumber, &args);
+  parser->errorReporter().errorWithNotesAtVA(nullptr, AsVariant(offset),
+                                             errorNumber, &args);
 
   va_end(args);
 }
 
 bool BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber,
                                          ...) {
   MOZ_ASSERT_IF(!pn, this->scriptStartOffsetSet);
   uint32_t offset = pn ? pn->pn_pos.begin : this->scriptStartOffset;
 
   va_list args;
   va_start(args, errorNumber);
 
-  bool result = parser->errorReporter().reportExtraWarningErrorNumberVA(
-      nullptr, offset, errorNumber, &args);
+  bool result = parser->errorReporter().extraWarningWithNotesAtVA(
+      nullptr, AsVariant(offset), errorNumber, &args);
 
   va_end(args);
   return result;
 }
 
 bool BytecodeEmitter::reportExtraWarning(const Maybe<uint32_t>& maybeOffset,
                                          unsigned errorNumber, ...) {
   MOZ_ASSERT_IF(!maybeOffset, this->scriptStartOffsetSet);
   uint32_t offset = maybeOffset ? *maybeOffset : this->scriptStartOffset;
 
   va_list args;
   va_start(args, errorNumber);
 
-  bool result = parser->errorReporter().reportExtraWarningErrorNumberVA(
-      nullptr, offset, errorNumber, &args);
+  bool result = parser->errorReporter().extraWarningWithNotesAtVA(
+      nullptr, AsVariant(offset), errorNumber, &args);
 
   va_end(args);
   return result;
 }
 
 bool BytecodeEmitter::emitNewInit() {
   const size_t len = 1 + UINT32_INDEX_LEN;
   ptrdiff_t offset;
--- a/js/src/frontend/ErrorReporter.h
+++ b/js/src/frontend/ErrorReporter.h
@@ -2,57 +2,502 @@
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * 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 frontend_ErrorReporter_h
 #define frontend_ErrorReporter_h
 
+#include "mozilla/Variant.h"
+
 #include <stdarg.h>  // for va_list
 #include <stddef.h>  // for size_t
 #include <stdint.h>  // for uint32_t
 
 #include "js/CompileOptions.h"
 #include "js/UniquePtr.h"
+#include "vm/ErrorReporting.h"  // ErrorMetadata, ReportCompile{Error,Warning}
 
 class JSErrorNotes;
 
 namespace js {
 namespace frontend {
 
-class ErrorReporter {
+// An interface class to provide strictMode getter method, which is used by
+// ErrorReportMixin::strictModeError* methods.
+//
+// This class is separated to be used as a back-channel from TokenStream to the
+// strict mode flag which is available inside Parser, to avoid exposing the
+// rest of SharedContext to TokenStream.
+class StrictModeGetter {
  public:
+  virtual bool strictMode() const = 0;
+};
+
+// This class provides error reporting methods, including warning, extra
+// warning, and strict mode error.
+//
+// A class that inherits this class must provide the following methods:
+//   * options
+//   * getContext
+//   * computeErrorMetadata
+class ErrorReportMixin : public StrictModeGetter {
+ public:
+  // Returns a compile options (extra warning, warning as error) for current
+  // compilation.
   virtual const JS::ReadOnlyCompileOptions& options() const = 0;
 
-  virtual void lineAndColumnAt(size_t offset, uint32_t* line,
-                               uint32_t* column) const = 0;
-  virtual void currentLineAndColumn(uint32_t* line, uint32_t* column) const = 0;
-  virtual bool isOnThisLine(size_t offset, uint32_t lineNum,
-                            bool* onThisLine) const = 0;
-  virtual uint32_t lineAt(size_t offset) const = 0;
-  virtual uint32_t columnAt(size_t offset) const = 0;
+  // Returns the current context.
+  virtual JSContext* getContext() const = 0;
+
+  // A variant class for the offset of the error or warning.
+  struct Current {};
+  struct NoOffset {};
+  using ErrorOffset = mozilla::Variant<uint32_t, Current, NoOffset>;
+
+  // Fills ErrorMetadata fields for an error or warning at given offset.
+  //   * offset is uint32_t if methods ending with "At" is called
+  //   * offset is NoOffset if methods ending with "NoOffset" is called
+  //   * offset is Current otherwise
+  virtual MOZ_MUST_USE bool computeErrorMetadata(ErrorMetadata* err,
+                                                 const ErrorOffset& offset) = 0;
+
+  // ==== error ====
+  //
+  // Reports an error.
+  //
+  // Methods ending with "At" are for an error at given offset.
+  // The offset is passed to computeErrorMetadata method and is transparent
+  // for this class.
+  //
+  // Methods ending with "NoOffset" are for an error that doesn't correspond
+  // to any offset. NoOffset is passed to computeErrorMetadata for them.
+  //
+  // Other methods except errorWithNotesAtVA are for an error at the current
+  // offset. Current is passed to computeErrorMetadata for them.
+  //
+  // Methods contains "WithNotes" can be used if there are error notes.
+  //
+  // errorWithNotesAtVA is the actual implementation for all of above.
+  // This can be called if the caller already has a va_list.
+
+  void error(unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    errorWithNotesAtVA(nullptr, mozilla::AsVariant(Current()), errorNumber,
+                       &args);
+
+    va_end(args);
+  }
+  void errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber,
+                      ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    errorWithNotesAtVA(std::move(notes), mozilla::AsVariant(Current()),
+                       errorNumber, &args);
+
+    va_end(args);
+  }
+  void errorAt(uint32_t offset, unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    errorWithNotesAtVA(nullptr, mozilla::AsVariant(offset), errorNumber, &args);
+
+    va_end(args);
+  }
+  void errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset,
+                        unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    errorWithNotesAtVA(std::move(notes), mozilla::AsVariant(offset),
+                       errorNumber, &args);
+
+    va_end(args);
+  }
+  void errorNoOffset(unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    errorWithNotesAtVA(nullptr, mozilla::AsVariant(NoOffset()), errorNumber,
+                       &args);
+
+    va_end(args);
+  }
+  void errorWithNotesNoOffset(UniquePtr<JSErrorNotes> notes,
+                              unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    errorWithNotesAtVA(std::move(notes), mozilla::AsVariant(NoOffset()),
+                       errorNumber, &args);
+
+    va_end(args);
+  }
+  void errorWithNotesAtVA(UniquePtr<JSErrorNotes> notes,
+                          const ErrorOffset& offset, unsigned errorNumber,
+                          va_list* args) {
+    ErrorMetadata metadata;
+    if (!computeErrorMetadata(&metadata, offset)) {
+      return;
+    }
+
+    ReportCompileError(getContext(), std::move(metadata), std::move(notes),
+                       JSREPORT_ERROR, errorNumber, args);
+  }
 
-  virtual bool hasTokenizationStarted() const = 0;
-  virtual void reportErrorNoOffsetVA(unsigned errorNumber, va_list args) = 0;
-  virtual const char* getFilename() const = 0;
+  // ==== warning ====
+  //
+  // Reports a warning.
+  //
+  // Returns true if the warning is reported.
+  // Returns false if the warning is treated as an error, or an error occurs
+  // while reporting.
+  //
+  // See the comment on the error section for details on what the arguments
+  // and function names indicate for all these functions.
+
+  MOZ_MUST_USE bool warning(unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = warningWithNotesAtVA(nullptr, mozilla::AsVariant(Current()),
+                                       errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool warningWithNotes(UniquePtr<JSErrorNotes> notes,
+                                     unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = warningWithNotesAtVA(
+        std::move(notes), mozilla::AsVariant(Current()), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = warningWithNotesAtVA(nullptr, mozilla::AsVariant(offset),
+                                       errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool warningWithNotesAt(UniquePtr<JSErrorNotes> notes,
+                                       uint32_t offset, unsigned errorNumber,
+                                       ...) {
+    va_list args;
+    va_start(args, errorNumber);
 
-  void reportErrorNoOffset(unsigned errorNumber, ...) {
+    bool result = warningWithNotesAtVA(
+        std::move(notes), mozilla::AsVariant(offset), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool warningNoOffset(unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = warningWithNotesAtVA(nullptr, mozilla::AsVariant(NoOffset()),
+                                       errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool warningWithNotesNoOffset(UniquePtr<JSErrorNotes> notes,
+                                             unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = warningWithNotesAtVA(
+        std::move(notes), mozilla::AsVariant(NoOffset()), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool warningWithNotesAtVA(UniquePtr<JSErrorNotes> notes,
+                                         const ErrorOffset& offset,
+                                         unsigned errorNumber, va_list* args) {
+    ErrorMetadata metadata;
+    if (!computeErrorMetadata(&metadata, offset)) {
+      return false;
+    }
+
+    return compileWarning(std::move(metadata), std::move(notes),
+                          JSREPORT_WARNING, errorNumber, args);
+  }
+
+  // ==== extraWarning ====
+  //
+  // Reports a warning if extra warnings are enabled.
+  //
+  // Returns true if the warning is reported.
+  // Returns false if the warning is treated as an error, or an error occurs
+  // while reporting.
+  //
+  // See the comment on the error section for details on what the arguments
+  // and function names indicate for all these functions.
+
+  MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...) {
     va_list args;
     va_start(args, errorNumber);
 
-    reportErrorNoOffsetVA(errorNumber, args);
+    bool result = extraWarningWithNotesAtVA(
+        nullptr, mozilla::AsVariant(Current()), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool extraWarningWithNotes(UniquePtr<JSErrorNotes> notes,
+                                          unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = extraWarningWithNotesAtVA(
+        std::move(notes), mozilla::AsVariant(Current()), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = extraWarningWithNotesAtVA(nullptr, mozilla::AsVariant(offset),
+                                            errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool extraWarningWithNotesAt(UniquePtr<JSErrorNotes> notes,
+                                            uint32_t offset,
+                                            unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = extraWarningWithNotesAtVA(
+        std::move(notes), mozilla::AsVariant(offset), errorNumber, &args);
 
     va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool extraWarningNoOffset(unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = extraWarningWithNotesAtVA(
+        nullptr, mozilla::AsVariant(NoOffset()), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool extraWarningWithNotesNoOffset(UniquePtr<JSErrorNotes> notes,
+                                                  unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = extraWarningWithNotesAtVA(
+        std::move(notes), mozilla::AsVariant(NoOffset()), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool extraWarningWithNotesAtVA(UniquePtr<JSErrorNotes> notes,
+                                              const ErrorOffset& offset,
+                                              unsigned errorNumber,
+                                              va_list* args) {
+    if (!options().extraWarningsOption) {
+      return true;
+    }
+
+    ErrorMetadata metadata;
+    if (!computeErrorMetadata(&metadata, offset)) {
+      return false;
+    }
+
+    return compileWarning(std::move(metadata), std::move(notes),
+                          JSREPORT_STRICT | JSREPORT_WARNING, errorNumber,
+                          args);
   }
 
-  virtual void errorAtVA(uint32_t offset, unsigned errorNumber,
-                         va_list* args) = 0;
-  virtual bool reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes,
+  // ==== strictModeError ====
+  //
+  // Reports an error if in strict mode code, or warn if not.
+  //
+  // Returns true if not in strict mode and a warning is reported.
+  // Returns false if the error reported, or an error occurs while reporting.
+  //
+  // See the comment on the error section for details on what the arguments
+  // and function names indicate for all these functions.
+
+  MOZ_MUST_USE bool strictModeError(unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = strictModeErrorWithNotesAtVA(
+        nullptr, mozilla::AsVariant(Current()), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool strictModeErrorWithNotes(UniquePtr<JSErrorNotes> notes,
+                                             unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = strictModeErrorWithNotesAtVA(
+        std::move(notes), mozilla::AsVariant(Current()), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool strictModeErrorAt(uint32_t offset, unsigned errorNumber,
+                                      ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = strictModeErrorWithNotesAtVA(
+        nullptr, mozilla::AsVariant(offset), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool strictModeErrorWithNotesAt(UniquePtr<JSErrorNotes> notes,
                                                uint32_t offset,
-                                               unsigned errorNumber,
-                                               va_list* args) = 0;
+                                               unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = strictModeErrorWithNotesAtVA(
+        std::move(notes), mozilla::AsVariant(offset), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool strictModeErrorNoOffset(unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = strictModeErrorWithNotesAtVA(
+        nullptr, mozilla::AsVariant(NoOffset()), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool strictModeErrorWithNotesNoOffset(
+      UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...) {
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result = strictModeErrorWithNotesAtVA(
+        std::move(notes), mozilla::AsVariant(NoOffset()), errorNumber, &args);
+
+    va_end(args);
+
+    return result;
+  }
+  MOZ_MUST_USE bool strictModeErrorWithNotesAtVA(UniquePtr<JSErrorNotes> notes,
+                                                 const ErrorOffset& offset,
+                                                 unsigned errorNumber,
+                                                 va_list* args) {
+    bool strict = strictMode();
+    if (!strict && !options().extraWarningsOption) {
+      return true;
+    }
+
+    ErrorMetadata metadata;
+    if (!computeErrorMetadata(&metadata, offset)) {
+      return false;
+    }
+
+    if (strict) {
+      ReportCompileError(getContext(), std::move(metadata), std::move(notes),
+                         JSREPORT_ERROR, errorNumber, args);
+      return false;
+    }
+
+    return compileWarning(std::move(metadata), std::move(notes),
+                          JSREPORT_WARNING | JSREPORT_STRICT, errorNumber,
+                          args);
+  }
+
+  // Reports a warning, or an error if the warning is treated as an error.
+  MOZ_MUST_USE bool compileWarning(ErrorMetadata&& metadata,
+                                   UniquePtr<JSErrorNotes> notes,
+                                   unsigned flags, unsigned errorNumber,
+                                   va_list* args) {
+    if (options().werrorOption) {
+      flags &= ~JSREPORT_WARNING;
+      ReportCompileError(getContext(), std::move(metadata), std::move(notes),
+                         flags, errorNumber, args);
+      return false;
+    }
+
+    return ReportCompileWarning(getContext(), std::move(metadata),
+                                std::move(notes), flags, errorNumber, args);
+  }
+};
+
+// An interface class to provide miscellaneous methods used by error reporting
+// etc.  They're mostly used by BytecodeCompiler, BytecodeEmitter, and helper
+// classes for emitter.
+class ErrorReporter : public ErrorReportMixin {
+ public:
+  // Returns the line and column numbers for given offset.
+  virtual void lineAndColumnAt(size_t offset, uint32_t* line,
+                               uint32_t* column) const = 0;
+
+  // Returns the line and column numbers for current offset.
+  virtual void currentLineAndColumn(uint32_t* line, uint32_t* column) const = 0;
+
+  // Sets *onThisLine to true if the given offset is inside the given line
+  // number `lineNum`, or false otherwise, and returns true.
+  //
+  // Return false if an error happens.  This method itself doesn't report an
+  // error, and any failure is supposed to be reported as OOM in the caller.
+  virtual bool isOnThisLine(size_t offset, uint32_t lineNum,
+                            bool* onThisLine) const = 0;
+
+  // Returns the line number for given offset.
+  virtual uint32_t lineAt(size_t offset) const = 0;
+
+  // Returns the column number for given offset.
+  virtual uint32_t columnAt(size_t offset) const = 0;
+
+  // Returns true if tokenization is already started and hasn't yet finished.
+  // currentLineAndColumn returns meaningful value only if this is true.
+  virtual bool hasTokenizationStarted() const = 0;
+
+  // Returns the filename which is currently being compiled.
+  virtual const char* getFilename() const = 0;
 };
 
 }  // namespace frontend
 }  // namespace js
 
 #endif  // frontend_ErrorReporter_h
--- a/js/src/frontend/ParseContext.cpp
+++ b/js/src/frontend/ParseContext.cpp
@@ -247,17 +247,17 @@ ParseContext::ParseContext(JSContext* cx
       namedLambdaScope_.emplace(cx, parent, usedNames);
     }
     functionScope_.emplace(cx, parent, usedNames);
   }
 }
 
 bool ParseContext::init() {
   if (scriptId_ == UINT32_MAX) {
-    errorReporter_.reportErrorNoOffset(JSMSG_NEED_DIET, js_script_str);
+    errorReporter_.errorNoOffset(JSMSG_NEED_DIET, js_script_str);
     return false;
   }
 
   JSContext* cx = sc()->context;
 
   if (isFunctionBox()) {
     // Named lambdas always need a binding for their own name. If this
     // binding is closed over when we finish parsing the function in
--- a/js/src/frontend/ParseContext.h
+++ b/js/src/frontend/ParseContext.h
@@ -262,17 +262,17 @@ class ParseContext : public Nestable<Par
                           UsedNameTracker& usedNames);
 
     void dump(ParseContext* pc);
 
     uint32_t id() const { return id_; }
 
     MOZ_MUST_USE bool init(ParseContext* pc) {
       if (id_ == UINT32_MAX) {
-        pc->errorReporter_.reportErrorNoOffset(JSMSG_NEED_DIET, js_script_str);
+        pc->errorReporter_.errorNoOffset(JSMSG_NEED_DIET, js_script_str);
         return false;
       }
 
       return declared_.acquire(pc->sc()->context);
     }
 
     bool isEmpty() const { return declared_->all().empty(); }
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -21,16 +21,17 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Range.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Utf8.h"
+#include "mozilla/Variant.h"
 
 #include <memory>
 #include <new>
 
 #include "jsnum.h"
 #include "jstypes.h"
 
 #include "builtin/ModuleObject.h"
@@ -52,16 +53,17 @@
 
 #include "frontend/ParseContext-inl.h"
 #include "frontend/SharedContext-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 
 using namespace js;
 
 using mozilla::AssertedCast;
+using mozilla::AsVariant;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 using mozilla::PointerRangeSize;
 using mozilla::Some;
 using mozilla::Unused;
 using mozilla::Utf8Unit;
@@ -128,187 +130,16 @@ bool GeneralParser<ParseHandler, Unit>::
   }
   if (!condition(actual)) {
     errorReport(actual);
     return false;
   }
   return true;
 }
 
-template <class ParseHandler, typename Unit>
-void GeneralParser<ParseHandler, Unit>::error(unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  ErrorMetadata metadata;
-  if (tokenStream.computeErrorMetadata(&metadata, pos().begin)) {
-    ReportCompileError(context, std::move(metadata), nullptr, JSREPORT_ERROR,
-                       errorNumber, args);
-  }
-
-  va_end(args);
-}
-
-template <class ParseHandler, typename Unit>
-void GeneralParser<ParseHandler, Unit>::errorWithNotes(
-    UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  ErrorMetadata metadata;
-  if (tokenStream.computeErrorMetadata(&metadata, pos().begin)) {
-    ReportCompileError(context, std::move(metadata), std::move(notes),
-                       JSREPORT_ERROR, errorNumber, args);
-  }
-
-  va_end(args);
-}
-
-template <class ParseHandler, typename Unit>
-void GeneralParser<ParseHandler, Unit>::errorAt(uint32_t offset,
-                                                unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  ErrorMetadata metadata;
-  if (tokenStream.computeErrorMetadata(&metadata, offset)) {
-    ReportCompileError(context, std::move(metadata), nullptr, JSREPORT_ERROR,
-                       errorNumber, args);
-  }
-
-  va_end(args);
-}
-
-template <class ParseHandler, typename Unit>
-void GeneralParser<ParseHandler, Unit>::errorWithNotesAt(
-    UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  ErrorMetadata metadata;
-  if (tokenStream.computeErrorMetadata(&metadata, offset)) {
-    ReportCompileError(context, std::move(metadata), std::move(notes),
-                       JSREPORT_ERROR, errorNumber, args);
-  }
-
-  va_end(args);
-}
-
-template <class ParseHandler, typename Unit>
-bool GeneralParser<ParseHandler, Unit>::warning(unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  ErrorMetadata metadata;
-  bool result = tokenStream.computeErrorMetadata(&metadata, pos().begin) &&
-                anyChars.compileWarning(std::move(metadata), nullptr,
-                                        JSREPORT_WARNING, errorNumber, args);
-
-  va_end(args);
-  return result;
-}
-
-template <class ParseHandler, typename Unit>
-bool GeneralParser<ParseHandler, Unit>::warningAt(uint32_t offset,
-                                                  unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  ErrorMetadata metadata;
-  bool result = tokenStream.computeErrorMetadata(&metadata, offset);
-  if (result) {
-    result = anyChars.compileWarning(std::move(metadata), nullptr,
-                                     JSREPORT_WARNING, errorNumber, args);
-  }
-
-  va_end(args);
-  return result;
-}
-
-template <class ParseHandler, typename Unit>
-bool GeneralParser<ParseHandler, Unit>::extraWarning(unsigned errorNumber,
-                                                     ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  bool result = tokenStream.reportExtraWarningErrorNumberVA(
-      nullptr, pos().begin, errorNumber, &args);
-
-  va_end(args);
-  return result;
-}
-
-template <class ParseHandler, typename Unit>
-bool GeneralParser<ParseHandler, Unit>::extraWarningAt(uint32_t offset,
-                                                       unsigned errorNumber,
-                                                       ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  bool result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset,
-                                                            errorNumber, &args);
-
-  va_end(args);
-  return result;
-}
-
-template <class ParseHandler, typename Unit>
-bool GeneralParser<ParseHandler, Unit>::strictModeError(unsigned errorNumber,
-                                                        ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  bool res = tokenStream.reportStrictModeErrorNumberVA(
-      nullptr, pos().begin, pc->sc()->strict(), errorNumber, &args);
-
-  va_end(args);
-  return res;
-}
-
-template <class ParseHandler, typename Unit>
-bool GeneralParser<ParseHandler, Unit>::strictModeErrorAt(uint32_t offset,
-                                                          unsigned errorNumber,
-                                                          ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  bool res = tokenStream.reportStrictModeErrorNumberVA(
-      nullptr, offset, pc->sc()->strict(), errorNumber, &args);
-
-  va_end(args);
-  return res;
-}
-
-bool ParserBase::warningNoOffset(unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  ErrorMetadata metadata;
-  anyChars.computeErrorMetadataNoOffset(&metadata);
-
-  bool result = anyChars.compileWarning(std::move(metadata), nullptr,
-                                        JSREPORT_WARNING, errorNumber, args);
-
-  va_end(args);
-  return result;
-}
-
-void ParserBase::errorNoOffset(unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  ErrorMetadata metadata;
-  anyChars.computeErrorMetadataNoOffset(&metadata);
-
-  ReportCompileError(context, std::move(metadata), nullptr, JSREPORT_ERROR,
-                     errorNumber, args);
-
-  va_end(args);
-}
-
 ParserBase::ParserBase(JSContext* cx, LifoAlloc& alloc,
                        const ReadOnlyCompileOptions& options,
                        bool foldConstants, UsedNameTracker& usedNames,
                        ScriptSourceObject* sourceObject, ParseGoal parseGoal)
     : AutoGCRooter(cx, AutoGCRooter::Tag::Parser),
       context(cx),
       alloc(alloc),
       anyChars(cx, options, thisForCtor()),
@@ -1665,17 +1496,17 @@ CodeNode* Parser<FullParseHandler, Unit>
 
     DeclaredNamePtr p = modulepc.varScope().lookupDeclaredName(name);
     if (!p) {
       UniqueChars str = AtomToPrintableString(context, name);
       if (!str) {
         return null();
       }
 
-      errorAt(TokenStream::NoOffset, JSMSG_MISSING_EXPORT, str.get());
+      errorNoOffset(JSMSG_MISSING_EXPORT, str.get());
       return null();
     }
 
     p->value()->setClosedOver();
   }
 
   ParseNode* node = stmtList;
   if (!FoldConstants(context, &node, this)) {
@@ -8655,16 +8486,25 @@ bool ParserBase::checkAndMarkSuperScope(
     return false;
   }
 
   pc->setSuperScopeNeedsHomeObject();
   return true;
 }
 
 template <class ParseHandler, typename Unit>
+bool GeneralParser<ParseHandler, Unit>::computeErrorMetadata(
+    ErrorMetadata* err, const ErrorReportMixin::ErrorOffset& offset) {
+  if (offset.is<ErrorReportMixin::Current>()) {
+    return tokenStream.computeErrorMetadata(err, AsVariant(pos().begin));
+  }
+  return tokenStream.computeErrorMetadata(err, offset);
+}
+
+template <class ParseHandler, typename Unit>
 typename ParseHandler::Node GeneralParser<ParseHandler, Unit>::memberExpr(
     YieldHandling yieldHandling, TripledotHandling tripledotHandling,
     TokenKind tt, bool allowCallSyntax /* = true */,
     PossibleError* possibleError /* = nullptr */,
     InvokedPrediction invoked /* = PredictUninvoked */) {
   MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
 
   Node lhs;
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -21,17 +21,17 @@
  * Like token streams (see the comment near the top of TokenStream.h), parser
  * classes are heavily templatized -- along the token stream's character-type
  * axis, and also along a full-parse/syntax-parse axis.  Certain limitations of
  * C++ (primarily the inability to partially specialize function templates),
  * plus the desire to minimize compiled code size in duplicate function
  * template instantiations wherever possible, mean that Parser exhibits much of
  * the same unholy template/inheritance complexity as token streams.
  *
- * == ParserBase → JS::AutoGCRooter, StrictModeGetter ==
+ * == ParserBase → JS::AutoGCRooter, ErrorReportMixin ==
  *
  * ParserBase is the base parser class, shared by all parsers of all character
  * types and parse-handling behavior.  It stores everything character- and
  * handler-agnostic.
  *
  * ParserBase's most important field is the parser's token stream's
  * |TokenStreamAnyChars| component, for all tokenizing aspects that are
  * character-type-agnostic.  The character-type-sensitive components residing
@@ -169,24 +169,27 @@
 #include "mozilla/Array.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jspubtd.h"
 
 #include "ds/Nestable.h"
 #include "frontend/BytecodeCompiler.h"
+#include "frontend/ErrorReporter.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/NameAnalysisTypes.h"
 #include "frontend/NameCollections.h"
 #include "frontend/ParseContext.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SyntaxParseHandler.h"
 #include "frontend/TokenStream.h"
 
+#include "vm/ErrorReporting.h"
+
 namespace js {
 
 class ModuleObject;
 
 namespace frontend {
 
 class ParserBase;
 
@@ -227,18 +230,20 @@ enum AwaitHandling : uint8_t {
 };
 
 template <class ParseHandler, typename Unit>
 class AutoAwaitIsKeyword;
 
 template <class ParseHandler, typename Unit>
 class AutoInParametersOfAsyncFunction;
 
-class MOZ_STACK_CLASS ParserBase : public StrictModeGetter,
-                                   private JS::AutoGCRooter {
+class MOZ_STACK_CLASS ParserBase : private JS::AutoGCRooter,
+                                   public ErrorReportMixin {
+  using Base = ErrorReportMixin;
+
  private:
   ParserBase* thisForCtor() { return this; }
 
   // This is needed to cast a parser to JS::AutoGCRooter.
   friend void js::frontend::TraceParser(JSTracer* trc,
                                         JS::AutoGCRooter* parser);
 
  public:
@@ -308,30 +313,59 @@ class MOZ_STACK_CLASS ParserBase : publi
   void trace(JSTracer* trc);
 
   const char* getFilename() const { return anyChars.getFilename(); }
   TokenPos pos() const { return anyChars.currentToken().pos; }
 
   // Determine whether |yield| is a valid name in the current context.
   bool yieldExpressionsSupported() const { return pc->isGenerator(); }
 
-  virtual bool strictMode() override { return pc->sc()->strict(); }
   bool setLocalStrictMode(bool strict) {
     MOZ_ASSERT(anyChars.debugHasNoLookahead());
     return pc->sc()->setLocalStrictMode(strict);
   }
 
-  const JS::ReadOnlyCompileOptions& options() const {
+ public:
+  // Implement ErrorReportMixin.
+
+  JSContext* getContext() const override { return context; }
+
+  bool strictMode() const override { return pc->sc()->strict(); }
+
+  const JS::ReadOnlyCompileOptions& options() const override {
     return anyChars.options();
   }
 
-  bool isUnexpectedEOF() const { return isUnexpectedEOF_; }
+  using Base::error;
+  using Base::errorAt;
+  using Base::errorNoOffset;
+  using Base::errorWithNotes;
+  using Base::errorWithNotesAt;
+  using Base::errorWithNotesNoOffset;
+  using Base::extraWarning;
+  using Base::extraWarningAt;
+  using Base::extraWarningNoOffset;
+  using Base::extraWarningWithNotes;
+  using Base::extraWarningWithNotesAt;
+  using Base::extraWarningWithNotesNoOffset;
+  using Base::strictModeError;
+  using Base::strictModeErrorAt;
+  using Base::strictModeErrorNoOffset;
+  using Base::strictModeErrorWithNotes;
+  using Base::strictModeErrorWithNotesAt;
+  using Base::strictModeErrorWithNotesNoOffset;
+  using Base::warning;
+  using Base::warningAt;
+  using Base::warningNoOffset;
+  using Base::warningWithNotes;
+  using Base::warningWithNotesAt;
+  using Base::warningWithNotesNoOffset;
 
-  MOZ_MUST_USE bool warningNoOffset(unsigned errorNumber, ...);
-  void errorNoOffset(unsigned errorNumber, ...);
+ public:
+  bool isUnexpectedEOF() const { return isUnexpectedEOF_; }
 
   bool isValidStrictBinding(PropertyName* name);
 
   bool hasValidSimpleStrictParameterNames();
 
   /*
    * Create a new function object given a name (which is optional if this is
    * a function expression).
@@ -406,16 +440,18 @@ class MOZ_STACK_CLASS ParserBase : publi
 
 enum FunctionCallBehavior {
   PermitAssignmentToFunctionCalls,
   ForbidAssignmentToFunctionCalls
 };
 
 template <class ParseHandler>
 class MOZ_STACK_CLASS PerHandlerParser : public ParserBase {
+  using Base = ParserBase;
+
  private:
   using Node = typename ParseHandler::Node;
 
 #define DECLARE_TYPE(typeName, longTypeName, asMethodName) \
   using longTypeName = typename ParseHandler::longTypeName;
   FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
 #undef DECLARE_TYPE
 
@@ -553,16 +589,44 @@ class MOZ_STACK_CLASS PerHandlerParser :
   PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) {
     return handler.newPropertyAccess(expr, key);
   }
 
   FunctionBox* newFunctionBox(CodeNodeType funNode, JSFunction* fun,
                               uint32_t toStringStart, Directives directives,
                               GeneratorKind generatorKind,
                               FunctionAsyncKind asyncKind);
+
+ public:
+  // ErrorReportMixin.
+
+  using Base::error;
+  using Base::errorAt;
+  using Base::errorNoOffset;
+  using Base::errorWithNotes;
+  using Base::errorWithNotesAt;
+  using Base::errorWithNotesNoOffset;
+  using Base::extraWarning;
+  using Base::extraWarningAt;
+  using Base::extraWarningNoOffset;
+  using Base::extraWarningWithNotes;
+  using Base::extraWarningWithNotesAt;
+  using Base::extraWarningWithNotesNoOffset;
+  using Base::strictModeError;
+  using Base::strictModeErrorAt;
+  using Base::strictModeErrorNoOffset;
+  using Base::strictModeErrorWithNotes;
+  using Base::strictModeErrorWithNotesAt;
+  using Base::strictModeErrorWithNotesNoOffset;
+  using Base::warning;
+  using Base::warningAt;
+  using Base::warningNoOffset;
+  using Base::warningWithNotes;
+  using Base::warningWithNotesAt;
+  using Base::warningWithNotesNoOffset;
 };
 
 #define ABORTED_SYNTAX_PARSE_SENTINEL reinterpret_cast<void*>(0x1)
 
 template <>
 inline void PerHandlerParser<SyntaxParseHandler>::disableSyntaxParser() {}
 
 template <>
@@ -683,16 +747,47 @@ class MOZ_STACK_CLASS GeneralParser : pu
   using Base::yieldExpressionsSupported;
 
   using Base::abortIfSyntaxParser;
   using Base::clearAbortedSyntaxParse;
   using Base::disableSyntaxParser;
   using Base::hadAbortedSyntaxParse;
 
  public:
+  // Implement ErrorReportMixin.
+
+  MOZ_MUST_USE bool computeErrorMetadata(
+      ErrorMetadata* err, const ErrorReportMixin::ErrorOffset& offset) override;
+
+  using Base::error;
+  using Base::errorAt;
+  using Base::errorNoOffset;
+  using Base::errorWithNotes;
+  using Base::errorWithNotesAt;
+  using Base::errorWithNotesNoOffset;
+  using Base::extraWarning;
+  using Base::extraWarningAt;
+  using Base::extraWarningNoOffset;
+  using Base::extraWarningWithNotes;
+  using Base::extraWarningWithNotesAt;
+  using Base::extraWarningWithNotesNoOffset;
+  using Base::strictModeError;
+  using Base::strictModeErrorAt;
+  using Base::strictModeErrorNoOffset;
+  using Base::strictModeErrorWithNotes;
+  using Base::strictModeErrorWithNotesAt;
+  using Base::strictModeErrorWithNotesNoOffset;
+  using Base::warning;
+  using Base::warningAt;
+  using Base::warningNoOffset;
+  using Base::warningWithNotes;
+  using Base::warningWithNotesAt;
+  using Base::warningWithNotesNoOffset;
+
+ public:
   using Base::anyChars;
   using Base::context;
   using Base::handler;
   using Base::isValidSimpleAssignmentTarget;
   using Base::pc;
   using Base::usedNames;
 
  private:
@@ -930,58 +1025,16 @@ class MOZ_STACK_CLASS GeneralParser : pu
   }
 
   template <typename ErrorReportT>
   MOZ_MUST_USE bool mustMatchToken(TokenKind expected,
                                    ErrorReportT errorReport) {
     return mustMatchToken(expected, TokenStream::None, errorReport);
   }
 
-  /* Report the given error at the current offset. */
-  void error(unsigned errorNumber, ...);
-  void errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...);
-
-  /* Report the given error at the given offset. */
-  void errorAt(uint32_t offset, unsigned errorNumber, ...);
-  void errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset,
-                        unsigned errorNumber, ...);
-
-  /*
-   * Handle a strict mode error at the current offset.  Report an error if in
-   * strict mode code, or warn if not, using the given error number and
-   * arguments.
-   */
-  MOZ_MUST_USE bool strictModeError(unsigned errorNumber, ...);
-
-  /*
-   * Handle a strict mode error at the given offset.  Report an error if in
-   * strict mode code, or warn if not, using the given error number and
-   * arguments.
-   */
-  MOZ_MUST_USE bool strictModeErrorAt(uint32_t offset, unsigned errorNumber,
-                                      ...);
-
-  /* Report the given warning at the current offset. */
-  MOZ_MUST_USE bool warning(unsigned errorNumber, ...);
-
-  /* Report the given warning at the given offset. */
-  MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...);
-
-  /*
-   * If extra warnings are enabled, report the given warning at the current
-   * offset.
-   */
-  MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...);
-
-  /*
-   * If extra warnings are enabled, report the given warning at the given
-   * offset.
-   */
-  MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...);
-
  private:
   GeneralParser* thisForCtor() { return this; }
 
   NameNodeType noSubstitutionUntaggedTemplate();
   ListNodeType templateLiteral(YieldHandling yieldHandling);
   bool taggedTemplate(YieldHandling yieldHandling, ListNodeType tagArgsList,
                       TokenKind tt);
   bool appendToCallSiteObj(CallSiteNodeType callSiteObj);
@@ -1398,23 +1451,49 @@ class MOZ_STACK_CLASS Parser<SyntaxParse
  public:
   using Base::anyChars;
   using Base::clearAbortedSyntaxParse;
   using Base::context;
   using Base::hadAbortedSyntaxParse;
   using Base::innerFunctionForFunctionBox;
   using Base::tokenStream;
 
+ public:
+  // ErrorReportMixin.
+
+  using Base::error;
+  using Base::errorAt;
+  using Base::errorNoOffset;
+  using Base::errorWithNotes;
+  using Base::errorWithNotesAt;
+  using Base::errorWithNotesNoOffset;
+  using Base::extraWarning;
+  using Base::extraWarningAt;
+  using Base::extraWarningNoOffset;
+  using Base::extraWarningWithNotes;
+  using Base::extraWarningWithNotesAt;
+  using Base::extraWarningWithNotesNoOffset;
+  using Base::strictModeError;
+  using Base::strictModeErrorAt;
+  using Base::strictModeErrorNoOffset;
+  using Base::strictModeErrorWithNotes;
+  using Base::strictModeErrorWithNotesAt;
+  using Base::strictModeErrorWithNotesNoOffset;
+  using Base::warning;
+  using Base::warningAt;
+  using Base::warningNoOffset;
+  using Base::warningWithNotes;
+  using Base::warningWithNotesAt;
+  using Base::warningWithNotesNoOffset;
+
  private:
   using Base::alloc;
 #if DEBUG
   using Base::checkOptionsCalled;
 #endif
-  using Base::error;
-  using Base::errorAt;
   using Base::finishFunctionScopes;
   using Base::functionFormalParametersAndBody;
   using Base::handler;
   using Base::innerFunction;
   using Base::keepAtoms;
   using Base::matchOrInsertSemicolon;
   using Base::mustMatchToken;
   using Base::newFunctionBox;
@@ -1523,25 +1602,51 @@ class MOZ_STACK_CLASS Parser<FullParseHa
   using Base::handler;
   using Base::newFunctionBox;
   using Base::options;
   using Base::pc;
   using Base::pos;
   using Base::ss;
   using Base::tokenStream;
 
+ public:
+  // ErrorReportMixin.
+
+  using Base::error;
+  using Base::errorAt;
+  using Base::errorNoOffset;
+  using Base::errorWithNotes;
+  using Base::errorWithNotesAt;
+  using Base::errorWithNotesNoOffset;
+  using Base::extraWarning;
+  using Base::extraWarningAt;
+  using Base::extraWarningNoOffset;
+  using Base::extraWarningWithNotes;
+  using Base::extraWarningWithNotesAt;
+  using Base::extraWarningWithNotesNoOffset;
+  using Base::strictModeError;
+  using Base::strictModeErrorAt;
+  using Base::strictModeErrorNoOffset;
+  using Base::strictModeErrorWithNotes;
+  using Base::strictModeErrorWithNotesAt;
+  using Base::strictModeErrorWithNotesNoOffset;
+  using Base::warning;
+  using Base::warningAt;
+  using Base::warningNoOffset;
+  using Base::warningWithNotes;
+  using Base::warningWithNotesAt;
+  using Base::warningWithNotesNoOffset;
+
  private:
   using Base::alloc;
   using Base::checkLabelOrIdentifierReference;
 #if DEBUG
   using Base::checkOptionsCalled;
 #endif
   using Base::context;
-  using Base::error;
-  using Base::errorAt;
   using Base::finishFunctionScopes;
   using Base::finishLexicalScope;
   using Base::innerFunction;
   using Base::innerFunctionForFunctionBox;
   using Base::keepAtoms;
   using Base::matchOrInsertSemicolon;
   using Base::mustMatchToken;
   using Base::newEvalScopeData;
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -573,16 +573,34 @@ bool TokenStreamAnyChars::checkOptions()
   if (options().column >= mozilla::MaxValue<int32_t>::value / 2 + 1) {
     reportErrorNoOffset(JSMSG_BAD_COLUMN_NUMBER);
     return false;
   }
 
   return true;
 }
 
+void TokenStreamAnyChars::reportErrorNoOffset(unsigned errorNumber, ...) {
+  va_list args;
+  va_start(args, errorNumber);
+
+  reportErrorNoOffsetVA(errorNumber, &args);
+
+  va_end(args);
+}
+
+void TokenStreamAnyChars::reportErrorNoOffsetVA(unsigned errorNumber,
+                                                va_list* args) {
+  ErrorMetadata metadata;
+  computeErrorMetadataNoOffset(&metadata);
+
+  ReportCompileError(cx, std::move(metadata), nullptr, JSREPORT_ERROR,
+                     errorNumber, args);
+}
+
 // Use the fastest available getc.
 #if defined(HAVE_GETC_UNLOCKED)
 #define fast_getc getc_unlocked
 #elif defined(HAVE__GETC_NOLOCK)
 #define fast_getc _getc_nolock
 #else
 #define fast_getc getc
 #endif
@@ -743,17 +761,17 @@ MOZ_COLD void TokenStreamChars<Utf8Unit,
 
     if (!notes->addNoteASCII(anyChars.cx, anyChars.getFilename(), line, column,
                              GetErrorMessage, nullptr, JSMSG_BAD_CODE_UNITS,
                              badUnitsStr)) {
       break;
     }
 
     ReportCompileError(anyChars.cx, std::move(err), std::move(notes),
-                       JSREPORT_ERROR, errorNumber, args);
+                       JSREPORT_ERROR, errorNumber, &args);
   } while (false);
 
   va_end(args);
 }
 
 template <class AnyCharsAccess>
 MOZ_COLD void TokenStreamChars<Utf8Unit, AnyCharsAccess>::badLeadUnit(
     Utf8Unit lead) {
@@ -1286,56 +1304,16 @@ bool TokenStreamSpecific<Unit, AnyCharsA
   if (!anyCharsAccess().srcCoords.fill(other.srcCoords)) {
     return false;
   }
 
   seek(pos);
   return true;
 }
 
-template <typename Unit, class AnyCharsAccess>
-bool TokenStreamSpecific<Unit, AnyCharsAccess>::reportStrictModeErrorNumberVA(
-    UniquePtr<JSErrorNotes> notes, uint32_t offset, bool strictMode,
-    unsigned errorNumber, va_list* args) {
-  TokenStreamAnyChars& anyChars = anyCharsAccess();
-  if (!strictMode && !anyChars.options().extraWarningsOption) {
-    return true;
-  }
-
-  ErrorMetadata metadata;
-  if (!computeErrorMetadata(&metadata, offset)) {
-    return false;
-  }
-
-  if (strictMode) {
-    ReportCompileError(anyChars.cx, std::move(metadata), std::move(notes),
-                       JSREPORT_ERROR, errorNumber, *args);
-    return false;
-  }
-
-  return anyChars.compileWarning(std::move(metadata), std::move(notes),
-                                 JSREPORT_WARNING | JSREPORT_STRICT,
-                                 errorNumber, *args);
-}
-
-bool TokenStreamAnyChars::compileWarning(ErrorMetadata&& metadata,
-                                         UniquePtr<JSErrorNotes> notes,
-                                         unsigned flags, unsigned errorNumber,
-                                         va_list args) {
-  if (options().werrorOption) {
-    flags &= ~JSREPORT_WARNING;
-    ReportCompileError(cx, std::move(metadata), std::move(notes), flags,
-                       errorNumber, args);
-    return false;
-  }
-
-  return ReportCompileWarning(cx, std::move(metadata), std::move(notes), flags,
-                              errorNumber, args);
-}
-
 void TokenStreamAnyChars::computeErrorMetadataNoOffset(ErrorMetadata* err) {
   err->isMuted = mutedErrors;
   err->filename = filename_;
   err->lineNumber = 0;
   err->columnNumber = 0;
 
   MOZ_ASSERT(err->lineOfContext == nullptr);
 }
@@ -1503,159 +1481,41 @@ bool TokenStreamCharsBase<Unit>::addLine
     }
   }
 
   return true;
 }
 
 template <typename Unit, class AnyCharsAccess>
 bool TokenStreamSpecific<Unit, AnyCharsAccess>::computeErrorMetadata(
-    ErrorMetadata* err, uint32_t offset) {
-  if (offset == NoOffset) {
+    ErrorMetadata* err, const ErrorOffset& errorOffset) {
+  if (errorOffset.is<NoOffset>()) {
     anyCharsAccess().computeErrorMetadataNoOffset(err);
     return true;
   }
 
+  uint32_t offset;
+  if (errorOffset.is<uint32_t>()) {
+    offset = errorOffset.as<uint32_t>();
+  } else {
+    offset = this->sourceUnits.offset();
+  }
+
   // This function's return value isn't a success/failure indication: it
   // returns true if this TokenStream can be used to provide a line of
   // context.
   if (fillExceptingContext(err, offset)) {
     // Add a line of context from this TokenStream to help with debugging.
     return internalComputeLineOfContext(err, offset);
   }
 
   // We can't fill in any more here.
   return true;
 }
 
-template <typename Unit, class AnyCharsAccess>
-bool TokenStreamSpecific<Unit, AnyCharsAccess>::reportStrictModeError(
-    unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  TokenStreamAnyChars& anyChars = anyCharsAccess();
-  bool result =
-      reportStrictModeErrorNumberVA(nullptr, anyChars.currentToken().pos.begin,
-                                    anyChars.strictMode(), errorNumber, &args);
-
-  va_end(args);
-  return result;
-}
-
-template <typename Unit, class AnyCharsAccess>
-void TokenStreamSpecific<Unit, AnyCharsAccess>::reportError(
-    unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  TokenStreamAnyChars& anyChars = anyCharsAccess();
-  ErrorMetadata metadata;
-  if (computeErrorMetadata(&metadata, anyChars.currentToken().pos.begin)) {
-    ReportCompileError(anyChars.cx, std::move(metadata), nullptr,
-                       JSREPORT_ERROR, errorNumber, args);
-  }
-
-  va_end(args);
-}
-
-void TokenStreamAnyChars::reportErrorNoOffset(unsigned errorNumber, ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  reportErrorNoOffsetVA(errorNumber, args);
-
-  va_end(args);
-}
-
-void TokenStreamAnyChars::reportErrorNoOffsetVA(unsigned errorNumber,
-                                                va_list args) {
-  ErrorMetadata metadata;
-  computeErrorMetadataNoOffset(&metadata);
-
-  ReportCompileError(cx, std::move(metadata), nullptr, JSREPORT_ERROR,
-                     errorNumber, args);
-}
-
-template <typename Unit, class AnyCharsAccess>
-bool TokenStreamSpecific<Unit, AnyCharsAccess>::warning(unsigned errorNumber,
-                                                        ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  ErrorMetadata metadata;
-  bool result =
-      computeErrorMetadata(&metadata,
-                           anyCharsAccess().currentToken().pos.begin) &&
-      anyCharsAccess().compileWarning(std::move(metadata), nullptr,
-                                      JSREPORT_WARNING, errorNumber, args);
-
-  va_end(args);
-  return result;
-}
-
-template <typename Unit, class AnyCharsAccess>
-bool TokenStreamSpecific<Unit, AnyCharsAccess>::reportExtraWarningErrorNumberVA(
-    UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned errorNumber,
-    va_list* args) {
-  TokenStreamAnyChars& anyChars = anyCharsAccess();
-  if (!anyChars.options().extraWarningsOption) {
-    return true;
-  }
-
-  ErrorMetadata metadata;
-  if (!computeErrorMetadata(&metadata, offset)) {
-    return false;
-  }
-
-  return anyChars.compileWarning(std::move(metadata), std::move(notes),
-                                 JSREPORT_STRICT | JSREPORT_WARNING,
-                                 errorNumber, *args);
-}
-
-template <typename Unit, class AnyCharsAccess>
-void TokenStreamSpecific<Unit, AnyCharsAccess>::error(unsigned errorNumber,
-                                                      ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  ErrorMetadata metadata;
-  if (computeErrorMetadata(&metadata, this->sourceUnits.offset())) {
-    TokenStreamAnyChars& anyChars = anyCharsAccess();
-    ReportCompileError(anyChars.cx, std::move(metadata), nullptr,
-                       JSREPORT_ERROR, errorNumber, args);
-  }
-
-  va_end(args);
-}
-
-template <typename Unit, class AnyCharsAccess>
-void TokenStreamSpecific<Unit, AnyCharsAccess>::errorAtVA(uint32_t offset,
-                                                          unsigned errorNumber,
-                                                          va_list* args) {
-  ErrorMetadata metadata;
-  if (computeErrorMetadata(&metadata, offset)) {
-    TokenStreamAnyChars& anyChars = anyCharsAccess();
-    ReportCompileError(anyChars.cx, std::move(metadata), nullptr,
-                       JSREPORT_ERROR, errorNumber, *args);
-  }
-}
-
-template <typename Unit, class AnyCharsAccess>
-void TokenStreamSpecific<Unit, AnyCharsAccess>::errorAt(uint32_t offset,
-                                                        unsigned errorNumber,
-                                                        ...) {
-  va_list args;
-  va_start(args, errorNumber);
-
-  errorAtVA(offset, errorNumber, &args);
-
-  va_end(args);
-}
-
 // We have encountered a '\': check for a Unicode escape sequence after it.
 // Return the length of the escape sequence and the encoded code point (by
 // value) if we found a Unicode escape sequence, and skip all code units
 // involed.  Otherwise, return 0 and don't advance along the buffer.
 template <typename Unit, class AnyCharsAccess>
 uint32_t GeneralTokenStreamChars<Unit, AnyCharsAccess>::matchUnicodeEscape(
     uint32_t* codePoint) {
   MOZ_ASSERT(this->sourceUnits.previousCodeUnit() == Unit('\\'));
@@ -2390,17 +2250,17 @@ MOZ_MUST_USE bool TokenStreamSpecific<Un
     if (!this->getNonAsciiCodePointDontNormalize(this->toUnit(lead),
                                                  &codePoint)) {
       return false;
     }
 
     if (MOZ_UNLIKELY(codePoint == unicode::LINE_SEPARATOR ||
                      codePoint == unicode::PARA_SEPARATOR)) {
       this->sourceUnits.ungetLineOrParagraphSeparator();
-      this->reportError(JSMSG_UNTERMINATED_REGEXP);
+      this->error(JSMSG_UNTERMINATED_REGEXP);
       return false;
     }
 
     return this->appendCodePointToCharBuffer(codePoint);
   };
 
   auto ReportUnterminatedRegExp = [this](int32_t unit) {
     this->ungetCodeUnit(unit);
@@ -2751,17 +2611,17 @@ MOZ_MUST_USE bool TokenStreamSpecific<Un
         isLegacyOctalOrNoctal = true;
 #endif
         // one past the '0'
         numStart = this->sourceUnits.addressOfNextCodeUnit() - 1;
 
         do {
           // Octal integer literals are not permitted in strict mode
           // code.
-          if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL)) {
+          if (!strictModeError(JSMSG_DEPRECATED_OCTAL)) {
             return badToken();
           }
 
           // Outside strict mode, we permit 08 and 09 as decimal
           // numbers, which makes our behaviour a superset of the
           // ECMA numeric grammar. We might not always be so
           // permissive, so we warn about it.
           if (unit >= '8') {
@@ -3023,17 +2883,17 @@ MOZ_MUST_USE bool TokenStreamSpecific<Un
         // Look for a multi-line comment.
         if (matchCodeUnit('*')) {
           TokenStreamAnyChars& anyChars = anyCharsAccess();
           unsigned linenoBefore = anyChars.lineno;
 
           do {
             int32_t unit = getCodeUnit();
             if (unit == EOF) {
-              reportError(JSMSG_UNTERMINATED_COMMENT);
+              error(JSMSG_UNTERMINATED_COMMENT);
               return badToken();
             }
 
             if (unit == '*' && matchCodeUnit('/')) {
               break;
             }
 
             if (unit == '@' || unit == '#') {
@@ -3395,17 +3255,17 @@ bool TokenStreamSpecific<Unit, AnyCharsA
           // Strict mode code allows only \0, then a non-digit.
           if (val != 0 || IsAsciiDigit(unit)) {
             TokenStreamAnyChars& anyChars = anyCharsAccess();
             if (parsingTemplate) {
               anyChars.setInvalidTemplateEscape(this->sourceUnits.offset() - 2,
                                                 InvalidEscapeType::Octal);
               continue;
             }
-            if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL)) {
+            if (!strictModeError(JSMSG_DEPRECATED_OCTAL)) {
               return false;
             }
             anyChars.flags.sawOctalEscape = true;
           }
 
           if (JS7_ISOCT(unit)) {
             val = 8 * val + JS7_UNOCT(unit);
             consumeKnownCodeUnit(unit);
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -146,17 +146,18 @@
  * Functions can be defined differently in the different specializations,
  * because AnyCharsAccess as the only template parameter on member functions
  * *can* vary.
  *
  * All TokenStreamChars<Unit, AnyCharsAccess> specializations, one per Unit,
  * are just functionality, no actual member data.
  *
  * == TokenStreamSpecific<Unit, AnyCharsAccess> →
- *    TokenStreamChars<Unit, AnyCharsAccess>, TokenStreamShared ==
+ *    TokenStreamChars<Unit, AnyCharsAccess>, TokenStreamShared,
+ *    ErrorReporter ==
  *
  * TokenStreamSpecific is operations that are parametrized on character type
  * but implement the *general* idea of tokenizing, without being intrinsically
  * tied to character type.  Notably, this includes all operations that can
  * report warnings or errors at particular offsets, because we include a line
  * of context with such errors -- and that necessarily accesses the raw
  * characters of their specific type.
  *
@@ -449,28 +450,16 @@ struct Token {
 };
 
 extern TokenKind ReservedWordTokenKind(PropertyName* str);
 
 extern const char* ReservedWordToCharZ(PropertyName* str);
 
 extern const char* ReservedWordToCharZ(TokenKind tt);
 
-// Ideally, tokenizing would be entirely independent of context.  But the
-// strict mode flag, which is in SharedContext, affects tokenizing, and
-// TokenStream needs to see it.
-//
-// This class is a tiny back-channel from TokenStream to the strict mode flag
-// that avoids exposing the rest of SharedContext to TokenStream.
-//
-class StrictModeGetter {
- public:
-  virtual bool strictMode() = 0;
-};
-
 struct TokenStreamFlags {
   bool isEOF : 1;           // Hit end of file.
   bool isDirtyLine : 1;     // Non-whitespace since start of line.
   bool sawOctalEscape : 1;  // Saw an octal character escape.
   bool hadError : 1;        // Hit a syntax error, at start or during a
                             // token.
 
   TokenStreamFlags() : isEOF(), isDirtyLine(), sawOctalEscape(), hadError() {}
@@ -492,18 +481,16 @@ class TokenStreamShared {
   static constexpr unsigned ntokensMask = ntokens - 1;
 
   template <typename Unit>
   friend class TokenStreamPosition;
 
  public:
   static constexpr unsigned maxLookahead = 2;
 
-  static constexpr uint32_t NoOffset = UINT32_MAX;
-
   using Modifier = Token::Modifier;
   static constexpr Modifier None = Token::None;
   static constexpr Modifier Operand = Token::Operand;
   static constexpr Modifier TemplateTail = Token::TemplateTail;
 
   using ModifierException = Token::ModifierException;
   static constexpr ModifierException NoException = Token::NoException;
   static constexpr ModifierException NoneIsOperand = Token::NoneIsOperand;
@@ -938,30 +925,26 @@ class TokenStreamAnyChars : public Token
   // Push the last scanned token back into the stream.
   void ungetToken() {
     MOZ_ASSERT(lookahead < maxLookahead);
     lookahead++;
     retractCursor();
   }
 
  public:
-  MOZ_MUST_USE bool compileWarning(ErrorMetadata&& metadata,
-                                   UniquePtr<JSErrorNotes> notes,
-                                   unsigned flags, unsigned errorNumber,
-                                   va_list args);
-
   // Compute error metadata for an error at no offset.
   void computeErrorMetadataNoOffset(ErrorMetadata* err);
 
   // ErrorReporter API Helpers
 
-  // This is just straight up duplicated from TokenStreamSpecific's inheritance
-  // of ErrorReporter's reportErrorNoOffset. varargs delenda est.
+  // Provide minimal set of error reporting API given we cannot use
+  // ErrorReportMixin here. "report" prefix is added to avoid conflict with
+  // ErrorReportMixin methods in TokenStream class.
   void reportErrorNoOffset(unsigned errorNumber, ...);
-  void reportErrorNoOffsetVA(unsigned errorNumber, va_list args);
+  void reportErrorNoOffsetVA(unsigned errorNumber, va_list* args);
 
   const JS::ReadOnlyCompileOptions& options() const { return options_; }
 
   const char* getFilename() const { return filename_; }
 
  protected:
   // Options used for parsing/tokenizing.
   const JS::ReadOnlyCompileOptions& options_;
@@ -2462,21 +2445,18 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
       return true;
     }
 
     reportInvalidEscapeError(anyCharsAccess().invalidTemplateEscapeOffset,
                              anyCharsAccess().invalidTemplateEscapeType);
     return false;
   }
 
-  // ErrorReporter API.
-
-  const JS::ReadOnlyCompileOptions& options() const final {
-    return anyCharsAccess().options();
-  }
+ public:
+  // Implement ErrorReporter.
 
   void lineAndColumnAt(size_t offset, uint32_t* line,
                        uint32_t* column) const final {
     computeLineAndColumn(offset, line, column);
   }
 
   void currentLineAndColumn(uint32_t* line, uint32_t* column) const final {
     computeLineAndColumn(anyCharsAccess().currentToken().pos.begin, line,
@@ -2495,60 +2475,40 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
   }
 
   uint32_t columnAt(size_t offset) const final {
     return computeColumn(anyCharsAccess().lineToken(offset), offset);
   }
 
   bool hasTokenizationStarted() const final;
 
-  void reportErrorNoOffsetVA(unsigned errorNumber, va_list args) final {
-    anyCharsAccess().reportErrorNoOffsetVA(errorNumber, args);
-  }
-
   const char* getFilename() const final {
     return anyCharsAccess().getFilename();
   }
 
-  // TokenStream-specific error reporters.
-  void reportError(unsigned errorNumber, ...);
-
-  // Report the given error at the current offset.
-  void error(unsigned errorNumber, ...);
-
-  // Report the given error at the given offset.
-  void errorAt(uint32_t offset, unsigned errorNumber, ...);
-  void errorAtVA(uint32_t offset, unsigned errorNumber, va_list* args);
-
-  // Warn at the current offset.
-  MOZ_MUST_USE bool warning(unsigned errorNumber, ...);
+ private:
+  // Implement ErrorReportMixin.
+
+  JSContext* getContext() const override { return anyCharsAccess().cx; }
+
+  MOZ_MUST_USE bool strictMode() const override {
+    return anyCharsAccess().strictMode();
+  }
 
  public:
-  // Compute error metadata for an error at the given offset.
-  MOZ_MUST_USE bool computeErrorMetadata(ErrorMetadata* err, uint32_t offset);
-
-  // General-purpose error reporters.  You should avoid calling these
-  // directly, and instead use the more succinct alternatives (error(),
-  // warning(), &c.) in TokenStream, Parser, and BytecodeEmitter.
-  //
-  // These functions take a |va_list*| parameter, not a |va_list| parameter,
-  // to hack around bug 1363116.  (Longer-term, the right fix is of course to
-  // not use ellipsis functions or |va_list| at all in error reporting.)
-  bool reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes,
-                                     uint32_t offset, bool strictMode,
-                                     unsigned errorNumber, va_list* args);
-  bool reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes,
-                                       uint32_t offset, unsigned errorNumber,
-                                       va_list* args);
+  // Implement ErrorReportMixin.
+
+  const JS::ReadOnlyCompileOptions& options() const final {
+    return anyCharsAccess().options();
+  }
+
+  MOZ_MUST_USE bool computeErrorMetadata(
+      ErrorMetadata* err, const ErrorOffset& errorOffset) override;
 
  private:
-  // This is private because it should only be called by the tokenizer while
-  // tokenizing not by, for example, BytecodeEmitter.
-  bool reportStrictModeError(unsigned errorNumber, ...);
-
   void reportInvalidEscapeError(uint32_t offset, InvalidEscapeType type) {
     switch (type) {
       case InvalidEscapeType::None:
         MOZ_ASSERT_UNREACHABLE("unexpected InvalidEscapeType");
         return;
       case InvalidEscapeType::Hexadecimal:
         errorAt(offset, JSMSG_MALFORMED_ESCAPE, "hexadecimal");
         return;
@@ -2694,17 +2654,17 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
     // |lineno| is the line that the furthest-scanned token ends on.  If
     // it's the same as the line that the current token ends on, that's a
     // stronger condition than what we are looking for, and we don't need
     // to return Eol.
     if (anyChars.lookahead != 0) {
       bool onThisLine;
       if (!anyChars.srcCoords.isOnThisLine(curr.pos.end, anyChars.lineno,
                                            &onThisLine)) {
-        reportError(JSMSG_OUT_OF_MEMORY);
+        error(JSMSG_OUT_OF_MEMORY);
         return false;
       }
 
       if (onThisLine) {
         MOZ_ASSERT(!anyChars.flags.hadError);
         verifyConsistentModifier(modifier, anyChars.nextToken());
         *ttp = anyChars.nextToken().type;
         return true;
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -329,17 +329,17 @@ RegExpParser<CharT>::SyntaxError(unsigne
         return;
 
     err.lineLength = windowLength;
     err.tokenOffset = offset - (windowStart - start_);
 
     va_list args;
     va_start(args, errorNumber);
 
-    ReportCompileError(ts.context(), std::move(err), nullptr, JSREPORT_ERROR, errorNumber, args);
+    ReportCompileError(ts.context(), std::move(err), nullptr, JSREPORT_ERROR, errorNumber, &args);
 
     va_end(args);
 }
 
 template <typename CharT>
 RegExpTree*
 RegExpParser<CharT>::ReportError(unsigned errorNumber, const char* param /* = nullptr */)
 {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -42,16 +42,17 @@
 #include "jit/MoveEmitter.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/SharedICHelpers.h"
 #include "jit/StackSlotAllocator.h"
 #include "jit/VMFunctions.h"
 #include "util/Unicode.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
+#include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/MatchPairs.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 #include "vm/TypedArrayObject.h"
 #include "vtune/VTuneWrapper.h"
 #include "wasm/WasmStubs.h"
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1170,29 +1170,18 @@ static void AllocateObjectBufferWithInit
   // raise an invalid argument exception or create a correct object with zero
   // elements.
   if (count <= 0 || uint32_t(count) >= INT32_MAX / obj->bytesPerElement()) {
     obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(0));
     return;
   }
 
   obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(count));
-  size_t nbytes;
-
-  switch (obj->type()) {
-#define CREATE_TYPED_ARRAY(T, N)                                \
-  case Scalar::N:                                               \
-    MOZ_ALWAYS_TRUE(js::CalculateAllocSize<T>(count, &nbytes)); \
-    break;
-    JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
-#undef CREATE_TYPED_ARRAY
-    default:
-      MOZ_CRASH("Unsupported TypedArray type");
-  }
-
+
+  size_t nbytes = count * obj->bytesPerElement();
   MOZ_ASSERT((CheckedUint32(nbytes) + sizeof(Value)).isValid());
 
   nbytes = JS_ROUNDUP(nbytes, sizeof(Value));
   void* buf = cx->nursery().allocateBuffer(obj, nbytes);
   if (buf) {
     obj->initPrivate(buf);
     memset(buf, 0, nbytes);
   }
@@ -1201,29 +1190,34 @@ static void AllocateObjectBufferWithInit
 void MacroAssembler::initTypedArraySlots(Register obj, Register temp,
                                          Register lengthReg,
                                          LiveRegisterSet liveRegs, Label* fail,
                                          TypedArrayObject* templateObj,
                                          TypedArrayLength lengthKind) {
   MOZ_ASSERT(templateObj->hasPrivate());
   MOZ_ASSERT(!templateObj->hasBuffer());
 
-  size_t dataSlotOffset = TypedArrayObject::dataOffset();
-  size_t dataOffset = TypedArrayObject::dataOffset() + sizeof(HeapSlot);
+  constexpr size_t dataSlotOffset = TypedArrayObject::dataOffset();
+  constexpr size_t dataOffset = dataSlotOffset + sizeof(HeapSlot);
 
   static_assert(
       TypedArrayObject::FIXED_DATA_START == TypedArrayObject::DATA_SLOT + 1,
       "fixed inline element data assumed to begin after the data slot");
 
+  static_assert(
+      TypedArrayObject::INLINE_BUFFER_LIMIT ==
+          JSObject::MAX_BYTE_SIZE - dataOffset,
+      "typed array inline buffer is limited by the maximum object byte size");
+
   // Initialise data elements to zero.
   int32_t length = templateObj->length();
   size_t nbytes = length * templateObj->bytesPerElement();
 
   if (lengthKind == TypedArrayLength::Fixed &&
-      dataOffset + nbytes <= JSObject::MAX_BYTE_SIZE) {
+      nbytes <= TypedArrayObject::INLINE_BUFFER_LIMIT) {
     MOZ_ASSERT(dataOffset + nbytes <= templateObj->tenuredSizeOfThis());
 
     // Store data elements inside the remaining JSObject slots.
     computeEffectiveAddress(Address(obj, dataOffset), temp);
     storePtr(temp, Address(obj, dataSlotOffset));
 
     // Write enough zero pointers into fixed data to zero every
     // element.  (This zeroes past the end of a byte count that's
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -12,16 +12,17 @@
 #include "jit/arm/Simulator-arm.h"
 #include "jit/BaselineIC.h"
 #include "jit/JitFrames.h"
 #include "jit/JitRealm.h"
 #include "jit/mips32/Simulator-mips32.h"
 #include "jit/mips64/Simulator-mips64.h"
 #include "vm/ArrayObject.h"
 #include "vm/Debugger.h"
+#include "vm/EqualityOperations.h"  // js::StrictlyEqual
 #include "vm/Interpreter.h"
 #include "vm/SelfHosting.h"
 #include "vm/TraceLogging.h"
 
 #include "jit/BaselineFrame-inl.h"
 #include "jit/JitFrames-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/Interpreter-inl.h"
--- a/js/src/jsapi-tests/testExecuteInJSMEnvironment.cpp
+++ b/js/src/jsapi-tests/testExecuteInJSMEnvironment.cpp
@@ -1,13 +1,14 @@
 /* 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 "js/CompilationAndEvaluation.h"
+#include "js/PropertySpec.h"
 #include "jsapi-tests/tests.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/EnvironmentObject-inl.h"
 
 BEGIN_TEST(testExecuteInJSMEnvironment_Basic) {
   static const char src[] =
       "var output = input;\n"
       "\n"
--- a/js/src/jsapi-tests/testGetPropertyDescriptor.cpp
+++ b/js/src/jsapi-tests/testGetPropertyDescriptor.cpp
@@ -1,12 +1,13 @@
 /* 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 "js/PropertyDescriptor.h"  // JS::FromPropertyDescriptor
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(test_GetPropertyDescriptor) {
   JS::RootedValue v(cx);
   EVAL("({ somename : 123 })", &v);
   CHECK(v.isObject());
 
   JS::RootedObject obj(cx, &v.toObject());
--- a/js/src/jsapi-tests/testLooselyEqual.cpp
+++ b/js/src/jsapi-tests/testLooselyEqual.cpp
@@ -1,33 +1,34 @@
 /* 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 <limits>
 #include <math.h>
 
+#include "js/Equality.h"  // JS::LooselyEqual
 #include "jsapi-tests/tests.h"
 
 using namespace std;
 
 struct LooseEqualityFixture : public JSAPITest {
   virtual ~LooseEqualityFixture() {}
 
   bool leq(JS::HandleValue x, JS::HandleValue y) {
     bool equal;
-    CHECK(JS_LooselyEqual(cx, x, y, &equal) && equal);
-    CHECK(JS_LooselyEqual(cx, y, x, &equal) && equal);
+    CHECK(JS::LooselyEqual(cx, x, y, &equal) && equal);
+    CHECK(JS::LooselyEqual(cx, y, x, &equal) && equal);
     return true;
   }
 
   bool nleq(JS::HandleValue x, JS::HandleValue y) {
     bool equal;
-    CHECK(JS_LooselyEqual(cx, x, y, &equal) && !equal);
-    CHECK(JS_LooselyEqual(cx, y, x, &equal) && !equal);
+    CHECK(JS::LooselyEqual(cx, x, y, &equal) && !equal);
+    CHECK(JS::LooselyEqual(cx, y, x, &equal) && !equal);
     return true;
   }
 };
 
 struct LooseEqualityData {
   JS::RootedValue qNaN;
   JS::RootedValue sNaN;
   JS::RootedValue d42;
--- a/js/src/jsapi-tests/testProfileStrings.cpp
+++ b/js/src/jsapi-tests/testProfileStrings.cpp
@@ -4,16 +4,17 @@
  * Tests the stack-based instrumentation profiler on a JSRuntime
  */
 /* 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 "mozilla/Atomics.h"
 
+#include "js/PropertySpec.h"
 #include "jsapi-tests/tests.h"
 #include "vm/JSContext.h"
 
 static ProfilingStack profilingStack;
 static uint32_t peakStackPointer = 0;
 
 static void reset(JSContext* cx) {
   profilingStack.stackPointer = 0;
--- a/js/src/jsapi-tests/testSameValue.cpp
+++ b/js/src/jsapi-tests/testSameValue.cpp
@@ -1,25 +1,26 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  */
 /* 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 "js/Equality.h"  // JS::SameValue
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testSameValue) {
   /*
    * NB: passing a double that fits in an integer jsval is API misuse.  As a
-   * matter of defense in depth, however, JS_SameValue should return the
+   * matter of defense in depth, however, JS::SameValue should return the
    * correct result comparing a positive-zero double to a negative-zero
    * double, and this is believed to be the only way to make such a
    * comparison possible.
    */
   JS::RootedValue v1(cx, JS::DoubleValue(0.0));
   JS::RootedValue v2(cx, JS::DoubleValue(-0.0));
   bool same;
-  CHECK(JS_SameValue(cx, v1, v2, &same));
+  CHECK(JS::SameValue(cx, v1, v2, &same));
   CHECK(!same);
   return true;
 }
 END_TEST(testSameValue)
--- a/js/src/jsapi-tests/testXDR.cpp
+++ b/js/src/jsapi-tests/testXDR.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * 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 "jsfriendapi.h"
 #include "builtin/String.h"
 
+#include "js/BuildId.h"  // JS::BuildIdCharVector, JS::SetProcessBuildIdOp
 #include "js/CompilationAndEvaluation.h"
 #include "js/Transcoding.h"
 #include "jsapi-tests/tests.h"
 #include "vm/JSScript.h"
 
 #include "vm/JSScript-inl.h"
 
 static bool GetBuildId(JS::BuildIdCharVector* buildId) {
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -14,16 +14,17 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "gc/GC.h"
 #include "js/AllocPolicy.h"
 #include "js/CharacterEncoding.h"
+#include "js/Equality.h"  // JS::SameValue
 #include "js/Vector.h"
 #include "vm/JSContext.h"
 
 /* Note: Aborts on OOM. */
 class JSAPITestString {
   js::Vector<char, 0, js::SystemAllocPolicy> chars;
 
  public:
@@ -203,22 +204,22 @@ class JSAPITest {
     if (!checkNull(actual, #actual, __FILE__, __LINE__)) return false; \
   } while (false)
 
   bool checkSame(const JS::Value& actualArg, const JS::Value& expectedArg,
                  const char* actualExpr, const char* expectedExpr,
                  const char* filename, int lineno) {
     bool same;
     JS::RootedValue actual(cx, actualArg), expected(cx, expectedArg);
-    return (JS_SameValue(cx, actual, expected, &same) && same) ||
+    return (JS::SameValue(cx, actual, expected, &same) && same) ||
            fail(JSAPITestString(
-                    "CHECK_SAME failed: expected JS_SameValue(cx, ") +
+                    "CHECK_SAME failed: expected JS::SameValue(cx, ") +
                     actualExpr + ", " + expectedExpr +
-                    "), got !JS_SameValue(cx, " + jsvalToSource(actual) + ", " +
-                    jsvalToSource(expected) + ")",
+                    "), got !JS::SameValue(cx, " + jsvalToSource(actual) +
+                    ", " + jsvalToSource(expected) + ")",
                 filename, lineno);
   }
 
 #define CHECK_SAME(actual, expected)                                          \
   do {                                                                        \
     if (!checkSame(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
       return false;                                                           \
   } while (false)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -56,16 +56,17 @@
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Initialization.h"
 #include "js/JSON.h"
 #include "js/LocaleSensitive.h"
 #include "js/MemoryFunctions.h"
+#include "js/PropertySpec.h"
 #include "js/Proxy.h"
 #include "js/SliceBudget.h"
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/StructuredClone.h"
 #include "js/Symbol.h"
 #include "js/Utility.h"
 #include "js/Wrapper.h"
@@ -354,43 +355,16 @@ JS_PUBLIC_API bool JS_DoubleIsInt32(doub
 
 JS_PUBLIC_API JSType JS_TypeOfValue(JSContext* cx, HandleValue value) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(value);
   return TypeOfValue(value);
 }
 
-JS_PUBLIC_API bool JS_StrictlyEqual(JSContext* cx, HandleValue value1,
-                                    HandleValue value2, bool* equal) {
-  AssertHeapIsIdle();
-  CHECK_THREAD(cx);
-  cx->check(value1, value2);
-  MOZ_ASSERT(equal);
-  return StrictlyEqual(cx, value1, value2, equal);
-}
-
-JS_PUBLIC_API bool JS_LooselyEqual(JSContext* cx, HandleValue value1,
-                                   HandleValue value2, bool* equal) {
-  AssertHeapIsIdle();
-  CHECK_THREAD(cx);
-  cx->check(value1, value2);
-  MOZ_ASSERT(equal);
-  return LooselyEqual(cx, value1, value2, equal);
-}
-
-JS_PUBLIC_API bool JS_SameValue(JSContext* cx, HandleValue value1,
-                                HandleValue value2, bool* same) {
-  AssertHeapIsIdle();
-  CHECK_THREAD(cx);
-  cx->check(value1, value2);
-  MOZ_ASSERT(same);
-  return SameValue(cx, value1, value2, same);
-}
-
 JS_PUBLIC_API bool JS_IsBuiltinEvalFunction(JSFunction* fun) {
   return IsAnyBuiltinEval(fun);
 }
 
 JS_PUBLIC_API bool JS_IsBuiltinFunctionConstructor(JSFunction* fun) {
   return fun->isBuiltinFunctionConstructor();
 }
 
@@ -4163,21 +4137,16 @@ JS_PUBLIC_API void JS::InitDispatchToEve
     JSContext* cx, JS::DispatchToEventLoopCallback callback, void* closure) {
   cx->runtime()->offThreadPromiseState.ref().init(callback, closure);
 }
 
 JS_PUBLIC_API void JS::ShutdownAsyncTasks(JSContext* cx) {
   cx->runtime()->offThreadPromiseState.ref().shutdown(cx);
 }
 
-JS_PUBLIC_API bool JS::GetOptimizedEncodingBuildId(
-    JS::BuildIdCharVector* buildId) {
-  return wasm::GetOptimizedEncodingBuildId(buildId);
-}
-
 JS_PUBLIC_API void JS::InitConsumeStreamCallback(
     JSContext* cx, ConsumeStreamCallback consume,
     ReportStreamErrorCallback report) {
   cx->runtime()->consumeStreamCallback = consume;
   cx->runtime()->reportStreamErrorCallback = report;
 }
 
 JS_PUBLIC_API void JS_RequestInterruptCallback(JSContext* cx) {
@@ -4997,47 +4966,16 @@ JS_PUBLIC_API JS::WarningReporter JS::Se
   WarningReporter older = cx->runtime()->warningReporter;
   cx->runtime()->warningReporter = reporter;
   return older;
 }
 
 /************************************************************************/
 
 /*
- * Dates.
- */
-JS_PUBLIC_API JSObject* JS_NewDateObject(JSContext* cx, int year, int mon,
-                                         int mday, int hour, int min, int sec) {
-  AssertHeapIsIdle();
-  CHECK_THREAD(cx);
-  return NewDateObject(cx, year, mon, mday, hour, min, sec);
-}
-
-JS_PUBLIC_API JSObject* JS::NewDateObject(JSContext* cx, JS::ClippedTime time) {
-  AssertHeapIsIdle();
-  CHECK_THREAD(cx);
-  return NewDateObjectMsec(cx, time);
-}
-
-JS_PUBLIC_API bool JS_ObjectIsDate(JSContext* cx, HandleObject obj,
-                                   bool* isDate) {
-  cx->check(obj);
-
-  ESClass cls;
-  if (!GetBuiltinClass(cx, obj, &cls)) {
-    return false;
-  }
-
-  *isDate = cls == ESClass::Date;
-  return true;
-}
-
-/************************************************************************/
-
-/*
  * Regular Expressions.
  */
 JS_PUBLIC_API JSObject* JS_NewRegExpObject(JSContext* cx, const char* bytes,
                                            size_t length, unsigned flags) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
   UniqueTwoByteChars chars(InflateString(cx, bytes, length));
@@ -6044,20 +5982,16 @@ JS_PUBLIC_API bool JS::FinishIncremental
     return false;
   }
   if (!script->scriptSource()->xdrFinalizeEncoder(buffer)) {
     return false;
   }
   return true;
 }
 
-JS_PUBLIC_API void JS::SetProcessBuildIdOp(JS::BuildIdOp buildIdOp) {
-  GetBuildId = buildIdOp;
-}
-
 JS_PUBLIC_API void JS::SetAsmJSCacheOps(JSContext* cx,
                                         const JS::AsmJSCacheOps* ops) {
   cx->runtime()->asmJSCacheOps = *ops;
 }
 
 bool JS::IsWasmModuleObject(HandleObject obj) {
   JSObject* unwrapped = CheckedUnwrap(obj);
   if (!unwrapped) {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -31,29 +31,32 @@
 #include "js/Class.h"
 #include "js/CompileOptions.h"
 #include "js/ErrorReport.h"
 #include "js/GCVector.h"
 #include "js/HashTable.h"
 #include "js/Id.h"
 #include "js/OffThreadScriptCompilation.h"
 #include "js/Principals.h"
+#include "js/PropertyDescriptor.h"
 #include "js/Realm.h"
 #include "js/RefCounted.h"
 #include "js/RootingAPI.h"
 #include "js/TracingAPI.h"
 #include "js/Transcoding.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 #include "js/Vector.h"
 
 /************************************************************************/
 
 struct JSFreeOp;
+struct JSFunctionSpec;
+struct JSPropertySpec;
 
 namespace JS {
 
 template <typename UnitT>
 class SourceText;
 
 class TwoByteChars;
 
@@ -238,80 +241,16 @@ static MOZ_ALWAYS_INLINE JS::Value JS_Nu
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API bool JS_StringHasBeenPinned(JSContext* cx, JSString* str);
 
 /************************************************************************/
 
-/* Property attributes, set in JSPropertySpec and passed to API functions.
- *
- * NB: The data structure in which some of these values are stored only uses
- *     a uint8_t to store the relevant information. Proceed with caution if
- *     trying to reorder or change the the first byte worth of flags.
- */
-
-/* property is visible to for/in loop */
-static const uint8_t JSPROP_ENUMERATE = 0x01;
-
-/* not settable: assignment is no-op.  This flag is only valid when neither
-   JSPROP_GETTER nor JSPROP_SETTER is set. */
-static const uint8_t JSPROP_READONLY = 0x02;
-
-/* property cannot be deleted */
-static const uint8_t JSPROP_PERMANENT = 0x04;
-
-/* (0x08 is unused) */
-
-/* property holds getter function */
-static const uint8_t JSPROP_GETTER = 0x10;
-
-/* property holds setter function */
-static const uint8_t JSPROP_SETTER = 0x20;
-
-/* internal JS engine use only */
-static const uint8_t JSPROP_INTERNAL_USE_BIT = 0x80;
-
-/* native that can be called as a ctor */
-static const unsigned JSFUN_CONSTRUCTOR = 0x400;
-
-/* | of all the JSFUN_* flags */
-static const unsigned JSFUN_FLAGS_MASK = 0x400;
-
-/*
- * Resolve hooks and enumerate hooks must pass this flag when calling
- * JS_Define* APIs to reify lazily-defined properties.
- *
- * JSPROP_RESOLVING is used only with property-defining APIs. It tells the
- * engine to skip the resolve hook when performing the lookup at the beginning
- * of property definition. This keeps the resolve hook from accidentally
- * triggering itself: unchecked recursion.
- *
- * For enumerate hooks, triggering the resolve hook would be merely silly, not
- * fatal, except in some cases involving non-configurable properties.
- */
-static const unsigned JSPROP_RESOLVING = 0x2000;
-
-/* ignore the value in JSPROP_ENUMERATE.  This flag only valid when defining
-   over an existing property. */
-static const unsigned JSPROP_IGNORE_ENUMERATE = 0x4000;
-
-/* ignore the value in JSPROP_READONLY.  This flag only valid when defining over
-   an existing property. */
-static const unsigned JSPROP_IGNORE_READONLY = 0x8000;
-
-/* ignore the value in JSPROP_PERMANENT.  This flag only valid when defining
-   over an existing property. */
-static const unsigned JSPROP_IGNORE_PERMANENT = 0x10000;
-
-/* ignore the Value in the descriptor. Nothing was specified when passed to
-   Object.defineProperty from script. */
-static const unsigned JSPROP_IGNORE_VALUE = 0x20000;
-
 /** Microseconds since the epoch, midnight, January 1, 1970 UTC. */
 extern JS_PUBLIC_API int64_t JS_Now(void);
 
 /** Don't want to export data, so provide accessors for non-inline Values. */
 extern JS_PUBLIC_API JS::Value JS_GetNaNValue(JSContext* cx);
 
 extern JS_PUBLIC_API JS::Value JS_GetNegativeInfinityValue(JSContext* cx);
 
@@ -339,29 +278,16 @@ extern JS_PUBLIC_API JSType JS_TypeOfVal
                                            JS::Handle<JS::Value> v);
 
 namespace JS {
 
 extern JS_PUBLIC_API const char* InformalValueTypeName(const JS::Value& v);
 
 } /* namespace JS */
 
-extern JS_PUBLIC_API bool JS_StrictlyEqual(JSContext* cx,
-                                           JS::Handle<JS::Value> v1,
-                                           JS::Handle<JS::Value> v2,
-                                           bool* equal);
-
-extern JS_PUBLIC_API bool JS_LooselyEqual(JSContext* cx,
-                                          JS::Handle<JS::Value> v1,
-                                          JS::Handle<JS::Value> v2,
-                                          bool* equal);
-
-extern JS_PUBLIC_API bool JS_SameValue(JSContext* cx, JS::Handle<JS::Value> v1,
-                                       JS::Handle<JS::Value> v2, bool* same);
-
 /** True iff fun is the global eval function. */
 extern JS_PUBLIC_API bool JS_IsBuiltinEvalFunction(JSFunction* fun);
 
 /** True iff fun is the Function constructor. */
 extern JS_PUBLIC_API bool JS_IsBuiltinFunctionConstructor(JSFunction* fun);
 
 /************************************************************************/
 
@@ -1147,260 +1073,18 @@ extern JS_PUBLIC_API bool GetFirstArgume
 } /* namespace JS */
 
 template <typename T>
 struct JSConstScalarSpec {
   const char* name;
   T val;
 };
 
-typedef JSConstScalarSpec<double> JSConstDoubleSpec;
-typedef JSConstScalarSpec<int32_t> JSConstIntegerSpec;
-
-struct JSJitInfo;
-
-/**
- * Wrapper to relace JSNative for JSPropertySpecs and JSFunctionSpecs. This will
- * allow us to pass one JSJitInfo per function with the property/function spec,
- * without additional field overhead.
- */
-struct JSNativeWrapper {
-  JSNative op;
-  const JSJitInfo* info;
-};
-
-/*
- * Macro static initializers which make it easy to pass no JSJitInfo as part of
- * a JSPropertySpec or JSFunctionSpec.
- */
-#define JSNATIVE_WRAPPER(native) \
-  {                              \
-    { native, nullptr }          \
-  }
-
-/**
- * Description of a property. JS_DefineProperties and JS_InitClass take arrays
- * of these and define many properties at once. JS_PSG, JS_PSGS and JS_PS_END
- * are helper macros for defining such arrays.
- */
-struct JSPropertySpec {
-  struct SelfHostedWrapper {
-    void* unused;
-    const char* funname;
-  };
-
-  struct ValueWrapper {
-    uintptr_t type;
-    union {
-      const char* string;
-      int32_t int32;
-    };
-  };
-
-  const char* name;
-  uint8_t flags;
-  union {
-    struct {
-      union {
-        JSNativeWrapper native;
-        SelfHostedWrapper selfHosted;
-      } getter;
-      union {
-        JSNativeWrapper native;
-        SelfHostedWrapper selfHosted;
-      } setter;
-    } accessors;
-    ValueWrapper value;
-  };
-
-  bool isAccessor() const { return !(flags & JSPROP_INTERNAL_USE_BIT); }
-  JS_PUBLIC_API bool getValue(JSContext* cx,
-                              JS::MutableHandleValue value) const;
-
-  bool isSelfHosted() const {
-    MOZ_ASSERT(isAccessor());
-
-#ifdef DEBUG
-    // Verify that our accessors match our JSPROP_GETTER flag.
-    if (flags & JSPROP_GETTER) {
-      checkAccessorsAreSelfHosted();
-    } else {
-      checkAccessorsAreNative();
-    }
-#endif
-    return (flags & JSPROP_GETTER);
-  }
-
-  static_assert(sizeof(SelfHostedWrapper) == sizeof(JSNativeWrapper),
-                "JSPropertySpec::getter/setter must be compact");
-  static_assert(offsetof(SelfHostedWrapper, funname) ==
-                    offsetof(JSNativeWrapper, info),
-                "JS_SELF_HOSTED* macros below require that "
-                "SelfHostedWrapper::funname overlay "
-                "JSNativeWrapper::info");
-
- private:
-  void checkAccessorsAreNative() const {
-    // We may have a getter or a setter or both.  And whichever ones we have
-    // should not have a SelfHostedWrapper for the accessor.
-    MOZ_ASSERT_IF(accessors.getter.native.info, accessors.getter.native.op);
-    MOZ_ASSERT_IF(accessors.setter.native.info, accessors.setter.native.op);
-  }
-
-  void checkAccessorsAreSelfHosted() const {
-    MOZ_ASSERT(!accessors.getter.selfHosted.unused);
-    MOZ_ASSERT(!accessors.setter.selfHosted.unused);
-  }
-};
-
-namespace JS {
-namespace detail {
-
-/* NEVER DEFINED, DON'T USE.  For use by JS_CAST_STRING_TO only. */
-template <size_t N>
-inline int CheckIsCharacterLiteral(const char (&arr)[N]);
-
-/* NEVER DEFINED, DON'T USE.  For use by JS_CAST_INT32_TO only. */
-inline int CheckIsInt32(int32_t value);
-
-}  // namespace detail
-}  // namespace JS
-
-#define JS_CAST_STRING_TO(s, To)                                      \
-  (static_cast<void>(sizeof(JS::detail::CheckIsCharacterLiteral(s))), \
-   reinterpret_cast<To>(s))
-
-#define JS_CAST_INT32_TO(s, To)                            \
-  (static_cast<void>(sizeof(JS::detail::CheckIsInt32(s))), \
-   reinterpret_cast<To>(s))
-
-#define JS_CHECK_ACCESSOR_FLAGS(flags)                                     \
-  (static_cast<mozilla::EnableIf<                                          \
-       ((flags) & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT)) == 0>::Type>(0), \
-   (flags))
-
-#define JS_PS_ACCESSOR_SPEC(name, getter, setter, flags, extraFlags) \
-  {                                                                  \
-    name, uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | extraFlags), {    \
-      { getter, setter }                                             \
-    }                                                                \
-  }
-#define JS_PS_VALUE_SPEC(name, value, flags)          \
-  {                                                   \
-    name, uint8_t(flags | JSPROP_INTERNAL_USE_BIT), { \
-      { value, JSNATIVE_WRAPPER(nullptr) }            \
-    }                                                 \
-  }
-
-#define SELFHOSTED_WRAPPER(name)                           \
-  {                                                        \
-    { nullptr, JS_CAST_STRING_TO(name, const JSJitInfo*) } \
-  }
-#define STRINGVALUE_WRAPPER(value)                   \
-  {                                                  \
-    {                                                \
-      reinterpret_cast<JSNative>(JSVAL_TYPE_STRING), \
-          JS_CAST_STRING_TO(value, const JSJitInfo*) \
-    }                                                \
-  }
-#define INT32VALUE_WRAPPER(value)                   \
-  {                                                 \
-    {                                               \
-      reinterpret_cast<JSNative>(JSVAL_TYPE_INT32), \
-          JS_CAST_INT32_TO(value, const JSJitInfo*) \
-    }                                               \
-  }
-
-/*
- * JSPropertySpec uses JSNativeWrapper.  These macros encapsulate the definition
- * of JSNative-backed JSPropertySpecs, by defining the JSNativeWrappers for
- * them.
- */
-#define JS_PSG(name, getter, flags)                   \
-  JS_PS_ACCESSOR_SPEC(name, JSNATIVE_WRAPPER(getter), \
-                      JSNATIVE_WRAPPER(nullptr), flags, 0)
-#define JS_PSGS(name, getter, setter, flags)          \
-  JS_PS_ACCESSOR_SPEC(name, JSNATIVE_WRAPPER(getter), \
-                      JSNATIVE_WRAPPER(setter), flags, 0)
-#define JS_SYM_GET(symbol, getter, flags)                                    \
-  JS_PS_ACCESSOR_SPEC(                                                       \
-      reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
-      JSNATIVE_WRAPPER(getter), JSNATIVE_WRAPPER(nullptr), flags, 0)
-#define JS_SELF_HOSTED_GET(name, getterName, flags)         \
-  JS_PS_ACCESSOR_SPEC(name, SELFHOSTED_WRAPPER(getterName), \
-                      JSNATIVE_WRAPPER(nullptr), flags, JSPROP_GETTER)
-#define JS_SELF_HOSTED_GETSET(name, getterName, setterName, flags) \
-  JS_PS_ACCESSOR_SPEC(name, SELFHOSTED_WRAPPER(getterName),        \
-                      SELFHOSTED_WRAPPER(setterName), flags,       \
-                      JSPROP_GETTER | JSPROP_SETTER)
-#define JS_SELF_HOSTED_SYM_GET(symbol, getterName, flags)                    \
-  JS_PS_ACCESSOR_SPEC(                                                       \
-      reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
-      SELFHOSTED_WRAPPER(getterName), JSNATIVE_WRAPPER(nullptr), flags,      \
-      JSPROP_GETTER)
-#define JS_STRING_PS(name, string, flags) \
-  JS_PS_VALUE_SPEC(name, STRINGVALUE_WRAPPER(string), flags)
-#define JS_STRING_SYM_PS(symbol, string, flags)                              \
-  JS_PS_VALUE_SPEC(                                                          \
-      reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
-      STRINGVALUE_WRAPPER(string), flags)
-#define JS_INT32_PS(name, value, flags) \
-  JS_PS_VALUE_SPEC(name, INT32VALUE_WRAPPER(value), flags)
-#define JS_PS_END                                         \
-  JS_PS_ACCESSOR_SPEC(nullptr, JSNATIVE_WRAPPER(nullptr), \
-                      JSNATIVE_WRAPPER(nullptr), 0, 0)
-
-/**
- * To define a native function, set call to a JSNativeWrapper. To define a
- * self-hosted function, set selfHostedName to the name of a function
- * compiled during JSRuntime::initSelfHosting.
- */
-struct JSFunctionSpec {
-  const char* name;
-  JSNativeWrapper call;
-  uint16_t nargs;
-  uint16_t flags;
-  const char* selfHostedName;
-};
-
-/*
- * Terminating sentinel initializer to put at the end of a JSFunctionSpec array
- * that's passed to JS_DefineFunctions or JS_InitClass.
- */
-#define JS_FS_END JS_FN(nullptr, nullptr, 0, 0)
-
-/*
- * Initializer macros for a JSFunctionSpec array element. JS_FNINFO allows the
- * simple adding of JSJitInfos. JS_SELF_HOSTED_FN declares a self-hosted
- * function. JS_INLINABLE_FN allows specifying an InlinableNative enum value for
- * natives inlined or specialized by the JIT. Finally JS_FNSPEC has slots for
- * all the fields.
- *
- * The _SYM variants allow defining a function with a symbol key rather than a
- * string key. For example, use JS_SYM_FN(iterator, ...) to define an
- * @@iterator method.
- */
-#define JS_FN(name, call, nargs, flags) \
-  JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr)
-#define JS_INLINABLE_FN(name, call, nargs, flags, native) \
-  JS_FNSPEC(name, call, &js::jit::JitInfo_##native, nargs, flags, nullptr)
-#define JS_SYM_FN(symbol, call, nargs, flags) \
-  JS_SYM_FNSPEC(symbol, call, nullptr, nargs, flags, nullptr)
-#define JS_FNINFO(name, call, info, nargs, flags) \
-  JS_FNSPEC(name, call, info, nargs, flags, nullptr)
-#define JS_SELF_HOSTED_FN(name, selfHostedName, nargs, flags) \
-  JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName)
-#define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags) \
-  JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName)
-#define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName)      \
-  JS_FNSPEC(                                                                 \
-      reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
-      call, info, nargs, flags, selfHostedName)
-#define JS_FNSPEC(name, call, info, nargs, flags, selfHostedName) \
-  { name, {call, info}, nargs, flags, selfHostedName }
+using JSConstDoubleSpec = JSConstScalarSpec<double>;
+using JSConstIntegerSpec = JSConstScalarSpec<int32_t>;
 
 extern JS_PUBLIC_API JSObject* JS_InitClass(
     JSContext* cx, JS::HandleObject obj, JS::HandleObject parent_proto,
     const JSClass* clasp, JSNative constructor, unsigned nargs,
     const JSPropertySpec* ps, const JSFunctionSpec* fs,
     const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs);
 
 /**
@@ -1808,296 +1492,16 @@ extern JS_PUBLIC_API bool JS_DeepFreezeO
                                               JS::Handle<JSObject*> obj);
 
 /**
  * Freezes an object; see ES5's Object.freeze(obj) method.
  */
 extern JS_PUBLIC_API bool JS_FreezeObject(JSContext* cx,
                                           JS::Handle<JSObject*> obj);
 
-/*** Property descriptors ***************************************************/
-
-namespace JS {
-
-struct JS_PUBLIC_API PropertyDescriptor {
-  JSObject* obj;
-  unsigned attrs;
-  JSGetterOp getter;
-  JSSetterOp setter;
-  JS::Value value;
-
-  PropertyDescriptor()
-      : obj(nullptr),
-        attrs(0),
-        getter(nullptr),
-        setter(nullptr),
-        value(JS::UndefinedValue()) {}
-
-  static void trace(PropertyDescriptor* self, JSTracer* trc) {
-    self->trace(trc);
-  }
-  void trace(JSTracer* trc);
-};
-
-}  // namespace JS
-
-namespace js {
-
-template <typename Wrapper>
-class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
-  const JS::PropertyDescriptor& desc() const {
-    return static_cast<const Wrapper*>(this)->get();
-  }
-
-  bool has(unsigned bit) const {
-    MOZ_ASSERT(bit != 0);
-    MOZ_ASSERT((bit & (bit - 1)) == 0);  // only a single bit
-    return (desc().attrs & bit) != 0;
-  }
-
-  bool hasAny(unsigned bits) const { return (desc().attrs & bits) != 0; }
-
-  bool hasAll(unsigned bits) const { return (desc().attrs & bits) == bits; }
-
- public:
-  // Descriptors with JSGetterOp/JSSetterOp are considered data
-  // descriptors. It's complicated.
-  bool isAccessorDescriptor() const {
-    return hasAny(JSPROP_GETTER | JSPROP_SETTER);
-  }
-  bool isGenericDescriptor() const {
-    return (desc().attrs & (JSPROP_GETTER | JSPROP_SETTER |
-                            JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE)) ==
-           (JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
-  }
-  bool isDataDescriptor() const {
-    return !isAccessorDescriptor() && !isGenericDescriptor();
-  }
-
-  bool hasConfigurable() const { return !has(JSPROP_IGNORE_PERMANENT); }
-  bool configurable() const {
-    MOZ_ASSERT(hasConfigurable());
-    return !has(JSPROP_PERMANENT);
-  }
-
-  bool hasEnumerable() const { return !has(JSPROP_IGNORE_ENUMERATE); }
-  bool enumerable() const {
-    MOZ_ASSERT(hasEnumerable());
-    return has(JSPROP_ENUMERATE);
-  }
-
-  bool hasValue() const {
-    return !isAccessorDescriptor() && !has(JSPROP_IGNORE_VALUE);
-  }
-  JS::HandleValue value() const {
-    return JS::HandleValue::fromMarkedLocation(&desc().value);
-  }
-
-  bool hasWritable() const {
-    return !isAccessorDescriptor() && !has(JSPROP_IGNORE_READONLY);
-  }
-  bool writable() const {
-    MOZ_ASSERT(hasWritable());
-    return !has(JSPROP_READONLY);
-  }
-
-  bool hasGetterObject() const { return has(JSPROP_GETTER); }
-  JS::HandleObject getterObject() const {
-    MOZ_ASSERT(hasGetterObject());
-    return JS::HandleObject::fromMarkedLocation(
-        reinterpret_cast<JSObject* const*>(&desc().getter));
-  }
-  bool hasSetterObject() const { return has(JSPROP_SETTER); }
-  JS::HandleObject setterObject() const {
-    MOZ_ASSERT(hasSetterObject());
-    return JS::HandleObject::fromMarkedLocation(
-        reinterpret_cast<JSObject* const*>(&desc().setter));
-  }
-
-  bool hasGetterOrSetter() const { return desc().getter || desc().setter; }
-
-  JS::HandleObject object() const {
-    return JS::HandleObject::fromMarkedLocation(&desc().obj);
-  }
-  unsigned attributes() const { return desc().attrs; }
-  JSGetterOp getter() const { return desc().getter; }
-  JSSetterOp setter() const { return desc().setter; }
-
-  void assertValid() const {
-#ifdef DEBUG
-    MOZ_ASSERT(
-        (attributes() &
-         ~(JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE | JSPROP_PERMANENT |
-           JSPROP_IGNORE_PERMANENT | JSPROP_READONLY | JSPROP_IGNORE_READONLY |
-           JSPROP_IGNORE_VALUE | JSPROP_GETTER | JSPROP_SETTER |
-           JSPROP_RESOLVING | JSPROP_INTERNAL_USE_BIT)) == 0);
-    MOZ_ASSERT(!hasAll(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE));
-    MOZ_ASSERT(!hasAll(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT));
-    if (isAccessorDescriptor()) {
-      MOZ_ASSERT(!has(JSPROP_READONLY));
-      MOZ_ASSERT(!has(JSPROP_IGNORE_READONLY));
-      MOZ_ASSERT(!has(JSPROP_IGNORE_VALUE));
-      MOZ_ASSERT(!has(JSPROP_INTERNAL_USE_BIT));
-      MOZ_ASSERT(value().isUndefined());
-      MOZ_ASSERT_IF(!has(JSPROP_GETTER), !getter());
-      MOZ_ASSERT_IF(!has(JSPROP_SETTER), !setter());
-    } else {
-      MOZ_ASSERT(!hasAll(JSPROP_IGNORE_READONLY | JSPROP_READONLY));
-      MOZ_ASSERT_IF(has(JSPROP_IGNORE_VALUE), value().isUndefined());
-    }
-
-    MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_ENUMERATE));
-    MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_PERMANENT));
-    MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_READONLY));
-    MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_VALUE));
-#endif
-  }
-
-  void assertComplete() const {
-#ifdef DEBUG
-    assertValid();
-    MOZ_ASSERT(
-        (attributes() & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT |
-                          JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER |
-                          JSPROP_RESOLVING | JSPROP_INTERNAL_USE_BIT)) == 0);
-    MOZ_ASSERT_IF(isAccessorDescriptor(),
-                  has(JSPROP_GETTER) && has(JSPROP_SETTER));
-#endif
-  }
-
-  void assertCompleteIfFound() const {
-#ifdef DEBUG
-    if (object()) {
-      assertComplete();
-    }
-#endif
-  }
-};
-
-template <typename Wrapper>
-class MutableWrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
-    : public js::WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
-  JS::PropertyDescriptor& desc() { return static_cast<Wrapper*>(this)->get(); }
-
- public:
-  void clear() {
-    object().set(nullptr);
-    setAttributes(0);
-    setGetter(nullptr);
-    setSetter(nullptr);
-    value().setUndefined();
-  }
-
-  void initFields(JS::HandleObject obj, JS::HandleValue v, unsigned attrs,
-                  JSGetterOp getterOp, JSSetterOp setterOp) {
-    object().set(obj);
-    value().set(v);
-    setAttributes(attrs);
-    setGetter(getterOp);
-    setSetter(setterOp);
-  }
-
-  void assign(JS::PropertyDescriptor& other) {
-    object().set(other.obj);
-    setAttributes(other.attrs);
-    setGetter(other.getter);
-    setSetter(other.setter);
-    value().set(other.value);
-  }
-
-  void setDataDescriptor(JS::HandleValue v, unsigned attrs) {
-    MOZ_ASSERT((attrs & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT |
-                          JSPROP_READONLY | JSPROP_IGNORE_ENUMERATE |
-                          JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_READONLY)) ==
-               0);
-    object().set(nullptr);
-    setAttributes(attrs);
-    setGetter(nullptr);
-    setSetter(nullptr);
-    value().set(v);
-  }
-
-  JS::MutableHandleObject object() {
-    return JS::MutableHandleObject::fromMarkedLocation(&desc().obj);
-  }
-  unsigned& attributesRef() { return desc().attrs; }
-  JSGetterOp& getter() { return desc().getter; }
-  JSSetterOp& setter() { return desc().setter; }
-  JS::MutableHandleValue value() {
-    return JS::MutableHandleValue::fromMarkedLocation(&desc().value);
-  }
-  void setValue(JS::HandleValue v) {
-    MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-    attributesRef() &= ~JSPROP_IGNORE_VALUE;
-    value().set(v);
-  }
-
-  void setConfigurable(bool configurable) {
-    setAttributes(
-        (desc().attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) |
-        (configurable ? 0 : JSPROP_PERMANENT));
-  }
-  void setEnumerable(bool enumerable) {
-    setAttributes(
-        (desc().attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) |
-        (enumerable ? JSPROP_ENUMERATE : 0));
-  }
-  void setWritable(bool writable) {
-    MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-    setAttributes((desc().attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) |
-                  (writable ? 0 : JSPROP_READONLY));
-  }
-  void setAttributes(unsigned attrs) { desc().attrs = attrs; }
-
-  void setGetter(JSGetterOp op) { desc().getter = op; }
-  void setSetter(JSSetterOp op) { desc().setter = op; }
-  void setGetterObject(JSObject* obj) {
-    desc().getter = reinterpret_cast<JSGetterOp>(obj);
-    desc().attrs &=
-        ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
-    desc().attrs |= JSPROP_GETTER;
-  }
-  void setSetterObject(JSObject* obj) {
-    desc().setter = reinterpret_cast<JSSetterOp>(obj);
-    desc().attrs &=
-        ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
-    desc().attrs |= JSPROP_SETTER;
-  }
-
-  JS::MutableHandleObject getterObject() {
-    MOZ_ASSERT(this->hasGetterObject());
-    return JS::MutableHandleObject::fromMarkedLocation(
-        reinterpret_cast<JSObject**>(&desc().getter));
-  }
-  JS::MutableHandleObject setterObject() {
-    MOZ_ASSERT(this->hasSetterObject());
-    return JS::MutableHandleObject::fromMarkedLocation(
-        reinterpret_cast<JSObject**>(&desc().setter));
-  }
-};
-
-}  // namespace js
-
-namespace JS {
-
-extern JS_PUBLIC_API bool ObjectToCompletePropertyDescriptor(
-    JSContext* cx, JS::HandleObject obj, JS::HandleValue descriptor,
-    JS::MutableHandle<PropertyDescriptor> desc);
-
-/*
- * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc).
- *
- * If desc.object() is null, then vp is set to undefined.
- */
-extern JS_PUBLIC_API bool FromPropertyDescriptor(
-    JSContext* cx, JS::Handle<JS::PropertyDescriptor> desc,
-    JS::MutableHandleValue vp);
-
-}  // namespace JS
-
 /*** Standard internal methods **********************************************
  *
  * The functions below are the fundamental operations on objects.
  *
  * ES6 specifies 14 internal methods that define how objects behave.  The
  * standard is actually quite good on this topic, though you may have to read
  * it a few times. See ES6 sections 6.1.7.2 and 6.1.7.3.
  *
@@ -2937,16 +2341,26 @@ extern JS_PUBLIC_API void JS_ReleaseMapp
 extern JS_PUBLIC_API JS::Value JS_GetReservedSlot(JSObject* obj,
                                                   uint32_t index);
 
 extern JS_PUBLIC_API void JS_SetReservedSlot(JSObject* obj, uint32_t index,
                                              const JS::Value& v);
 
 /************************************************************************/
 
+/* native that can be called as a ctor */
+static constexpr unsigned JSFUN_CONSTRUCTOR = 0x400;
+
+/* | of all the JSFUN_* flags */
+static constexpr unsigned JSFUN_FLAGS_MASK = 0x400;
+
+static_assert((JSPROP_FLAGS_MASK & JSFUN_FLAGS_MASK) == 0,
+              "JSFUN_* flags do not overlap JSPROP_* flags, because bits from "
+              "the two flag-sets appear in the same flag in some APIs");
+
 /*
  * Functions and scripts.
  */
 extern JS_PUBLIC_API JSFunction* JS_NewFunction(JSContext* cx, JSNative call,
                                                 unsigned nargs, unsigned flags,
                                                 const char* name);
 
 namespace JS {
@@ -3284,20 +2698,16 @@ extern JS_PUBLIC_API bool JS_DisableInte
 extern JS_PUBLIC_API void JS_ResetInterruptCallback(JSContext* cx, bool enable);
 
 extern JS_PUBLIC_API void JS_RequestInterruptCallback(JSContext* cx);
 
 extern JS_PUBLIC_API void JS_RequestInterruptCallbackCanWait(JSContext* cx);
 
 namespace JS {
 
-/* Vector of characters used for holding build ids. */
-
-typedef js::Vector<char, 0, js::SystemAllocPolicy> BuildIdCharVector;
-
 /**
  * The ConsumeStreamCallback is called from an active JSContext, passing a
  * StreamConsumer that wishes to consume the given host object as a stream of
  * bytes with the given MIME type. On failure, the embedding must report the
  * appropriate error on 'cx'. On success, the embedding must call
  * consumer->consumeChunk() repeatedly on any thread until exactly one of:
  *  - consumeChunk() returns false
  *  - the embedding calls consumer->streamEnd()
@@ -3311,19 +2721,19 @@ typedef js::Vector<char, 0, js::SystemAl
  * OptimizedEncodingListener*, indicating that there is a cache entry associated
  * with this stream that can store an optimized encoding of the bytes that were
  * just streamed at some point in the future by having SpiderMonkey call
  * storeOptimizedEncoding(). Until the optimized encoding is ready, SpiderMonkey
  * will hold an outstanding refcount to keep the listener alive.
  *
  * After storeOptimizedEncoding() is called, on cache hit, the embedding
  * may call consumeOptimizedEncoding() instead of consumeChunk()/streamEnd().
- * The embedding must ensure that the GetOptimizedEncodingBuildId() at the time
- * when an optimized encoding is created is the same as when it is later
- * consumed.
+ * The embedding must ensure that the GetOptimizedEncodingBuildId() (see
+ * js/BuildId.h) at the time when an optimized encoding is created is the same
+ * as when it is later consumed.
  */
 
 class OptimizedEncodingListener {
  protected:
   virtual ~OptimizedEncodingListener() {}
 
  public:
   // SpiderMonkey will hold an outstanding reference count as long as it holds
@@ -3331,19 +2741,16 @@ class OptimizedEncodingListener {
   virtual MozExternalRefCountType MOZ_XPCOM_ABI AddRef() = 0;
   virtual MozExternalRefCountType MOZ_XPCOM_ABI Release() = 0;
 
   // SpiderMonkey may optionally call storeOptimizedEncoding() after it has
   // finished processing a streamed resource.
   virtual void storeOptimizedEncoding(const uint8_t* bytes, size_t length) = 0;
 };
 
-extern MOZ_MUST_USE JS_PUBLIC_API bool GetOptimizedEncodingBuildId(
-    BuildIdCharVector* buildId);
-
 class JS_PUBLIC_API StreamConsumer {
  protected:
   // AsyncStreamConsumers are created and destroyed by SpiderMonkey.
   StreamConsumer() = default;
   virtual ~StreamConsumer() = default;
 
  public:
   // Called by the embedding as each chunk of bytes becomes available.
@@ -3987,35 +3394,16 @@ extern JS_PUBLIC_API bool SetEntries(JSC
                                      MutableHandleValue rval);
 
 extern JS_PUBLIC_API bool SetForEach(JSContext* cx, HandleObject obj,
                                      HandleValue callbackFn,
                                      HandleValue thisVal);
 
 } /* namespace JS */
 
-/*
- * Dates.
- */
-
-extern JS_PUBLIC_API JSObject* JS_NewDateObject(JSContext* cx, int year,
-                                                int mon, int mday, int hour,
-                                                int min, int sec);
-
-/**
- * On success, returns true, setting |*isDate| to true if |obj| is a Date
- * object or a wrapper around one, or to false if not.  Returns false on
- * failure.
- *
- * This method returns true with |*isDate == false| when passed an ES6 proxy
- * whose target is a Date, or when passed a revoked proxy.
- */
-extern JS_PUBLIC_API bool JS_ObjectIsDate(JSContext* cx, JS::HandleObject obj,
-                                          bool* isDate);
-
 /************************************************************************/
 
 /*
  * Regular Expressions.
  */
 #define JSREG_FOLD 0x01u      /* fold uppercase to lowercase */
 #define JSREG_GLOB 0x02u      /* global exec, creates array of matches */
 #define JSREG_MULTILINE 0x04u /* treat ^ and $ as begin and end of line */
@@ -4434,28 +3822,16 @@ struct AsmJSCacheOps {
   OpenAsmJSCacheEntryForWriteOp openEntryForWrite = nullptr;
   CloseAsmJSCacheEntryForWriteOp closeEntryForWrite = nullptr;
 };
 
 extern JS_PUBLIC_API void SetAsmJSCacheOps(JSContext* cx,
                                            const AsmJSCacheOps* callbacks);
 
 /**
- * Return the buildId (represented as a sequence of characters) associated with
- * the currently-executing build. If the JS engine is embedded such that a
- * single cache entry can be observed by different compiled versions of the JS
- * engine, it is critical that the buildId shall change for each new build of
- * the JS engine.
- */
-
-typedef bool (*BuildIdOp)(BuildIdCharVector* buildId);
-
-extern JS_PUBLIC_API void SetProcessBuildIdOp(BuildIdOp buildIdOp);
-
-/**
  * The WasmModule interface allows the embedding to hold a reference to the
  * underying C++ implementation of a JS WebAssembly.Module object for purposes
  * of efficient postMessage() and (de)serialization from a random thread.
  *
  * In particular, this allows postMessage() of a WebAssembly.Module:
  * GetWasmModule() is called when making a structured clone of a payload
  * containing a WebAssembly.Module object. The structured clone buffer holds a
  * refcount of the JS::WasmModule until createObject() is called in the target
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -24,24 +24,26 @@
 #include "mozilla/Sprintf.h"
 #include "mozilla/TextUtils.h"
 
 #include <ctype.h>
 #include <math.h>
 #include <string.h>
 
 #include "jsapi.h"
+#include "jsfriendapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "builtin/String.h"
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/LocaleSensitive.h"
+#include "js/PropertySpec.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
@@ -3351,16 +3353,22 @@ JSObject* js::NewDateObjectMsec(JSContex
   DateObject* obj = NewObjectWithClassProto<DateObject>(cx, proto);
   if (!obj) {
     return nullptr;
   }
   obj->setUTCTime(t);
   return obj;
 }
 
+JS_PUBLIC_API JSObject* JS::NewDateObject(JSContext* cx, ClippedTime time) {
+  AssertHeapIsIdle();
+  CHECK_THREAD(cx);
+  return NewDateObjectMsec(cx, time);
+}
+
 JS_FRIEND_API JSObject* js::NewDateObject(JSContext* cx, int year, int mon,
                                           int mday, int hour, int min,
                                           int sec) {
   MOZ_ASSERT(mon < 12);
   double msec_time =
       MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0.0));
   return NewDateObjectMsec(cx, TimeClip(UTC(msec_time)));
 }
@@ -3381,16 +3389,37 @@ JS_FRIEND_API bool js::DateIsValid(JSCon
   if (!Unbox(cx, obj, &unboxed)) {
     return false;
   }
 
   *isValid = !IsNaN(unboxed.toNumber());
   return true;
 }
 
+JS_PUBLIC_API JSObject* JS::NewDateObject(JSContext* cx, int year, int mon,
+                                          int mday, int hour, int min,
+                                          int sec) {
+  AssertHeapIsIdle();
+  CHECK_THREAD(cx);
+  return js::NewDateObject(cx, year, mon, mday, hour, min, sec);
+}
+
+JS_PUBLIC_API bool JS::ObjectIsDate(JSContext* cx, Handle<JSObject*> obj,
+                                    bool* isDate) {
+  cx->check(obj);
+
+  ESClass cls;
+  if (!GetBuiltinClass(cx, obj, &cls)) {
+    return false;
+  }
+
+  *isDate = cls == ESClass::Date;
+  return true;
+}
+
 JS_FRIEND_API bool js::DateGetMsecSinceEpoch(JSContext* cx, HandleObject obj,
                                              double* msecsSinceEpoch) {
   ESClass cls;
   if (!GetBuiltinClass(cx, obj, &cls)) {
     return false;
   }
 
   if (cls != ESClass::Date) {
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -19,16 +19,17 @@
 #include "jsapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "js/CharacterEncoding.h"
+#include "js/PropertySpec.h"
 #include "js/UniquePtr.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "vm/ErrorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1449,17 +1449,16 @@ static inline size_t byteSize(Type atype
       return 2;
     case Int32:
     case Uint32:
     case Float32:
       return 4;
     case Int64:
     case Float64:
       return 8;
-      return 16;
     default:
       MOZ_CRASH("invalid scalar type");
   }
 }
 
 static inline bool isSignedIntType(Type atype) {
   switch (atype) {
     case Int8:
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -20,16 +20,17 @@
 #include <cmath>
 
 #include "fdlibm.h"
 #include "jsapi.h"
 #include "jstypes.h"
 
 #include "jit/InlinableNatives.h"
 #include "js/Class.h"
+#include "js/PropertySpec.h"
 #include "util/Windows.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/Realm.h"
 #include "vm/Time.h"
 
 #include "vm/JSObject-inl.h"
 
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -27,16 +27,17 @@
 
 #include "builtin/String.h"
 #include "double-conversion/double-conversion.h"
 #include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
 #if !EXPOSE_INTL_API
 #include "js/LocaleSensitive.h"
 #endif
+#include "js/PropertySpec.h"
 #include "util/DoubleToString.h"
 #include "util/StringBuffer.h"
 #ifdef ENABLE_BIGINT
 #include "vm/BigIntType.h"
 #endif
 #include "vm/GlobalObject.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -108,25 +108,27 @@ EXPORTS += [
     'jsfriendapi.h',
     'jspubtd.h',
     'jstypes.h',
     'perf/jsperf.h',
 ]
 
 EXPORTS.js += [
     '../public/AllocPolicy.h',
+    '../public/BuildId.h',
     '../public/CallArgs.h',
     '../public/CallNonGenericMethod.h',
     '../public/CharacterEncoding.h',
     '../public/Class.h',
     '../public/CompilationAndEvaluation.h',
     '../public/CompileOptions.h',
     '../public/Conversions.h',
     '../public/Date.h',
     '../public/Debug.h',
+    '../public/Equality.h',
     '../public/ErrorReport.h',
     '../public/GCAnnotations.h',
     '../public/GCAPI.h',
     '../public/GCHashTable.h',
     '../public/GCPolicyAPI.h',
     '../public/GCVariant.h',
     '../public/GCVector.h',
     '../public/HashTable.h',
@@ -138,16 +140,18 @@ EXPORTS.js += [
     '../public/MemoryFunctions.h',
     '../public/MemoryMetrics.h',
     '../public/OffThreadScriptCompilation.h',
     '../public/Principals.h',
     '../public/Printf.h',
     '../public/ProfilingFrameIterator.h',
     '../public/ProfilingStack.h',
     '../public/Promise.h',
+    '../public/PropertyDescriptor.h',
+    '../public/PropertySpec.h',
     '../public/ProtoKey.h',
     '../public/Proxy.h',
     '../public/Realm.h',
     '../public/RefCounted.h',
     '../public/RequiredDefines.h',
     '../public/Result.h',
     '../public/RootingAPI.h',
     '../public/SavedFrameAPI.h',
@@ -244,28 +248,30 @@ UNIFIED_SOURCES += [
     'util/StructuredSpewer.cpp',
     'util/Text.cpp',
     'util/Unicode.cpp',
     'vm/ArgumentsObject.cpp',
     'vm/ArrayBufferObject.cpp',
     'vm/ArrayBufferViewObject.cpp',
     'vm/AsyncFunction.cpp',
     'vm/AsyncIteration.cpp',
+    'vm/BuildId.cpp',
     'vm/BytecodeUtil.cpp',
     'vm/Caches.cpp',
     'vm/CallNonGenericMethod.cpp',
     'vm/CharacterEncoding.cpp',
     'vm/CodeCoverage.cpp',
     'vm/Compartment.cpp',
     'vm/CompilationAndEvaluation.cpp',
     'vm/Compression.cpp',
     'vm/DateTime.cpp',
     'vm/Debugger.cpp',
     'vm/DebuggerMemory.cpp',
     'vm/EnvironmentObject.cpp',
+    'vm/EqualityOperations.cpp',
     'vm/ErrorObject.cpp',
     'vm/ErrorReporting.cpp',
     'vm/ForOfIterator.cpp',
     'vm/GeckoProfiler.cpp',
     'vm/GeneratorObject.cpp',
     'vm/GlobalObject.cpp',
     'vm/HelperThreads.cpp',
     'vm/Id.cpp',
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "perf/jsperf.h"
 
 #include "gc/FreeOp.h"
+#include "js/PropertySpec.h"
 #include "vm/JSContext.h" /* for error messages */
 #include "vm/JSObject.h"  /* for unwrapping without a context */
 
 using namespace js;
 using JS::PerfMeasurement;
 
 // You cannot forward-declare a static object in C++, so instead
 // we have to forward-declare the helper function that refers to it.
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -7,16 +7,17 @@
 #include "js/Proxy.h"
 
 #include "mozilla/Attributes.h"
 
 #include <string.h>
 
 #include "jsapi.h"
 
+#include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "proxy/DeadObjectProxy.h"
 #include "proxy/ScriptedProxyHandler.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/WrapperObject.h"
 
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.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 "proxy/ScriptedProxyHandler.h"
 
 #include "jsapi.h"
 
 #include "js/CharacterEncoding.h"
+#include "js/PropertyDescriptor.h"  // JS::FromPropertyDescriptor
+#include "vm/EqualityOperations.h"  // js::SameValue
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using JS::IsArrayAnswer;
 
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -22,16 +22,17 @@
 #include "jsapi.h"
 // For JSFunctionSpecWithHelp
 #include "jsfriendapi.h"
 
 #include "builtin/String.h"
 #include "gc/FreeOp.h"
 #include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
+#include "js/PropertySpec.h"
 #include "js/Wrapper.h"
 #include "shell/jsshell.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/JSObject.h"
 #include "vm/TypedArrayObject.h"
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -79,25 +79,28 @@
 #include "frontend/Parser.h"
 #include "gc/PublicIterators.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitRealm.h"
 #include "jit/shared/CodeGenerator-shared.h"
+#include "js/BuildId.h"  // JS::BuildIdCharVector, JS::SetProcessBuildIdOp
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Debug.h"
+#include "js/Equality.h"  // JS::SameValue
 #include "js/GCVector.h"
 #include "js/Initialization.h"
 #include "js/JSON.h"
 #include "js/MemoryFunctions.h"
 #include "js/Printf.h"
+#include "js/PropertySpec.h"
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/StructuredClone.h"
 #include "js/SweepingAPI.h"
 #include "js/Wrapper.h"
 #include "perf/jsperf.h"
 #include "shell/jsoptparse.h"
 #include "shell/jsshell.h"
@@ -2772,17 +2775,17 @@ static bool AssertEq(JSContext* cx, unsi
         (args.length() < 2)
             ? JSSMSG_NOT_ENOUGH_ARGS
             : (args.length() == 3) ? JSSMSG_INVALID_ARGS : JSSMSG_TOO_MANY_ARGS,
         "assertEq");
     return false;
   }
 
   bool same;
-  if (!JS_SameValue(cx, args[0], args[1], &same)) {
+  if (!JS::SameValue(cx, args[0], args[1], &same)) {
     return false;
   }
   if (!same) {
     UniqueChars bytes0, bytes1;
     const char* actual = ToSource(cx, args[0], &bytes0);
     const char* expected = ToSource(cx, args[1], &bytes1);
     if (args.length() == 2) {
       JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr,
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -30,16 +30,17 @@
 
 #include "builtin/Array.h"
 #include "builtin/DataViewObject.h"
 #include "gc/Barrier.h"
 #include "gc/FreeOp.h"
 #include "gc/Memory.h"
 #include "js/Conversions.h"
 #include "js/MemoryMetrics.h"
+#include "js/PropertySpec.h"
 #include "js/Wrapper.h"
 #include "util/Windows.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/WrapperObject.h"
@@ -419,17 +420,19 @@ static ArrayBufferObject::BufferContents
   uint8_t* p =
       cx->pod_callocCanGC<uint8_t>(nbytes, js::ArrayBufferContentsArena);
   return ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(p);
 }
 
 static void NoteViewBufferWasDetached(
     ArrayBufferViewObject* view, ArrayBufferObject::BufferContents newContents,
     JSContext* cx) {
-  view->notifyBufferDetached(cx, newContents.data());
+  MOZ_ASSERT(!view->isSharedMemory());
+
+  view->notifyBufferDetached(newContents.data());
 
   // Notify compiled jit code that the base pointer has moved.
   MarkObjectStateChange(cx, view);
 }
 
 /* static */ void ArrayBufferObject::detach(JSContext* cx,
                                             Handle<ArrayBufferObject*> buffer,
                                             BufferContents newContents) {
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -531,42 +531,42 @@ struct uint8_clamped {
   void staticAsserts() {
     static_assert(sizeof(uint8_clamped) == 1,
                   "uint8_clamped must be layout-compatible with uint8_t");
   }
 };
 
 /* Note that we can't use std::numeric_limits here due to uint8_clamped. */
 template <typename T>
-inline bool TypeIsFloatingPoint() {
+inline constexpr bool TypeIsFloatingPoint() {
   return false;
 }
 template <>
-inline bool TypeIsFloatingPoint<float>() {
+inline constexpr bool TypeIsFloatingPoint<float>() {
   return true;
 }
 template <>
-inline bool TypeIsFloatingPoint<double>() {
+inline constexpr bool TypeIsFloatingPoint<double>() {
   return true;
 }
 
 template <typename T>
-inline bool TypeIsUnsigned() {
+inline constexpr bool TypeIsUnsigned() {
   return false;
 }
 template <>
-inline bool TypeIsUnsigned<uint8_t>() {
+inline constexpr bool TypeIsUnsigned<uint8_t>() {
   return true;
 }
 template <>
-inline bool TypeIsUnsigned<uint16_t>() {
+inline constexpr bool TypeIsUnsigned<uint16_t>() {
   return true;
 }
 template <>
-inline bool TypeIsUnsigned<uint32_t>() {
+inline constexpr bool TypeIsUnsigned<uint32_t>() {
   return true;
 }
 
 // Per-compartment table that manages the relationship between array buffers
 // and the views that use their storage.
 class InnerViewTable {
  public:
   typedef Vector<JSObject*, 1, SystemAllocPolicy> ViewVector;
--- a/js/src/vm/ArrayBufferViewObject.cpp
+++ b/js/src/vm/ArrayBufferViewObject.cpp
@@ -53,36 +53,23 @@ using namespace js;
   }
 }
 
 template <>
 bool JSObject::is<js::ArrayBufferViewObject>() const {
   return is<DataViewObject>() || is<TypedArrayObject>();
 }
 
-void ArrayBufferViewObject::notifyBufferDetached(JSContext* cx, void* newData) {
-  if (isSharedMemory()) {
-    return;
-  }
+void ArrayBufferViewObject::notifyBufferDetached(void* newData) {
+  MOZ_ASSERT(!isSharedMemory());
+  MOZ_ASSERT(hasBuffer());
 
-  MOZ_ASSERT(!isSharedMemory());
   setFixedSlot(LENGTH_SLOT, Int32Value(0));
   setFixedSlot(BYTEOFFSET_SLOT, Int32Value(0));
 
-  // If the object is in the nursery, the buffer will be freed by the next
-  // nursery GC. Free the data slot pointer if the object has no inline data.
-  if (is<TypedArrayObject>()) {
-    TypedArrayObject& tarr = as<TypedArrayObject>();
-    Nursery& nursery = cx->nursery();
-    if (isTenured() && !hasBuffer() && !tarr.hasInlineElements() &&
-        !nursery.isInside(tarr.elements())) {
-      js_free(tarr.elements());
-    }
-  }
-
   setPrivate(newData);
 }
 
 uint8_t* ArrayBufferViewObject::dataPointerUnshared(
     const JS::AutoRequireNoGC& nogc) {
   return static_cast<uint8_t*>(dataPointerUnshared());
 }
 
--- a/js/src/vm/ArrayBufferViewObject.h
+++ b/js/src/vm/ArrayBufferViewObject.h
@@ -64,17 +64,17 @@ class ArrayBufferViewObject : public Nat
  public:
   MOZ_MUST_USE bool init(JSContext* cx, ArrayBufferObjectMaybeShared* buffer,
                          uint32_t byteOffset, uint32_t length,
                          uint32_t bytesPerElement);
 
   static ArrayBufferObjectMaybeShared* bufferObject(
       JSContext* cx, Handle<ArrayBufferViewObject*> obj);
 
-  void notifyBufferDetached(JSContext* cx, void* newData);
+  void notifyBufferDetached(void* newData);
 
   // By construction we only need unshared variants here.  See
   // comments in ArrayBufferObject.cpp.
   uint8_t* dataPointerUnshared(const JS::AutoRequireNoGC&);
   void setDataPointerUnshared(uint8_t* data);
 
   void initDataPointer(SharedMem<uint8_t*> viewData) {
     // Install a pointer to the buffer location that corresponds
--- a/js/src/vm/AsyncIteration.cpp
+++ b/js/src/vm/AsyncIteration.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/AsyncIteration.h"
 
 #include "builtin/Array.h"
 
 #include "builtin/Promise.h"
+#include "js/PropertySpec.h"
 #include "vm/GeneratorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Realm.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
--- a/js/src/vm/BigIntType.h
+++ b/js/src/vm/BigIntType.h
@@ -14,41 +14,48 @@
 
 #include "gc/Barrier.h"
 #include "gc/GC.h"
 #include "gc/Heap.h"
 #include "js/AllocPolicy.h"
 #include "js/GCHashTable.h"
 #include "js/Result.h"
 #include "js/RootingAPI.h"
+#include "js/TraceKind.h"
 #include "js/TypeDecls.h"
 #include "vm/StringType.h"
 #include "vm/Xdr.h"
 
+namespace JS {
+
+class BigInt;
+
+}  // namespace JS
+
 namespace js {
 
 template <typename CharT>
 static bool StringToBigIntImpl(const mozilla::Range<const CharT>& chars,
                                uint8_t radix, Handle<JS::BigInt*> res);
 
 template <XDRMode mode>
-XDRResult XDRBigInt(XDRState<mode>* xdr, MutableHandleBigInt bi);
+XDRResult XDRBigInt(XDRState<mode>* xdr, MutableHandle<JS::BigInt*> bi);
 
 }  // namespace js
 
 namespace JS {
 
 class BigInt final : public js::gc::TenuredCell {
   // StringToBigIntImpl modifies the num_ field of the res argument.
   template <typename CharT>
   friend bool js::StringToBigIntImpl(const mozilla::Range<const CharT>& chars,
                                      uint8_t radix, Handle<BigInt*> res);
   template <js::XDRMode mode>
   friend js::XDRResult js::XDRBigInt(js::XDRState<mode>* xdr,
-                                     MutableHandleBigInt bi);
+                                     MutableHandle<BigInt*> bi);
 
  protected:
   // Reserved word for Cell GC invariants. This also ensures minimum
   // structure size.
   uintptr_t reserved_;
 
  private:
   mpz_t num_;
@@ -99,18 +106,18 @@ class BigInt final : public js::gc::Tenu
   static BigInt* bitAnd(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
   static BigInt* bitXor(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
   static BigInt* bitOr(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
   static BigInt* bitNot(JSContext* cx, Handle<BigInt*> x);
 
   static int64_t toInt64(BigInt* x);
   static uint64_t toUint64(BigInt* x);
 
-  static BigInt* asIntN(JSContext* cx, HandleBigInt x, uint64_t bits);
-  static BigInt* asUintN(JSContext* cx, HandleBigInt x, uint64_t bits);
+  static BigInt* asIntN(JSContext* cx, Handle<BigInt*> x, uint64_t bits);
+  static BigInt* asUintN(JSContext* cx, Handle<BigInt*> x, uint64_t bits);
 
   // Type-checking versions of arithmetic operations. These methods
   // must be called with at least one BigInt operand. Binary
   // operations with throw a TypeError if one of the operands is not a
   // BigInt value.
   static bool add(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                   MutableHandle<Value> res);
   static bool sub(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
@@ -138,28 +145,28 @@ class BigInt final : public js::gc::Tenu
   static bool bitNot(JSContext* cx, Handle<Value> operand,
                      MutableHandle<Value> res);
 
   static double numberValue(BigInt* x);
   static JSLinearString* toString(JSContext* cx, BigInt* x, uint8_t radix);
 
   static bool equal(BigInt* lhs, BigInt* rhs);
   static bool equal(BigInt* lhs, double rhs);
-  static JS::Result<bool> looselyEqual(JSContext* cx, HandleBigInt lhs,
+  static JS::Result<bool> looselyEqual(JSContext* cx, Handle<BigInt*> lhs,
                                        HandleValue rhs);
 
   static bool lessThan(BigInt* x, BigInt* y);
 
   // These methods return Nothing when the non-BigInt operand is NaN
   // or a string that can't be interpreted as a BigInt.
   static mozilla::Maybe<bool> lessThan(BigInt* lhs, double rhs);
   static mozilla::Maybe<bool> lessThan(double lhs, BigInt* rhs);
-  static bool lessThan(JSContext* cx, HandleBigInt lhs, HandleString rhs,
+  static bool lessThan(JSContext* cx, Handle<BigInt*> lhs, HandleString rhs,
                        mozilla::Maybe<bool>& res);
-  static bool lessThan(JSContext* cx, HandleString lhs, HandleBigInt rhs,
+  static bool lessThan(JSContext* cx, HandleString lhs, Handle<BigInt*> rhs,
                        mozilla::Maybe<bool>& res);
   static bool lessThan(JSContext* cx, HandleValue lhs, HandleValue rhs,
                        mozilla::Maybe<bool>& res);
 
   // Return the length in bytes of the representation used by
   // writeBytes.
   static size_t byteLength(BigInt* x);
 
new file mode 100644
--- /dev/null
+++ b/js/src/vm/BuildId.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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/. */
+
+/* SpiderMonkey buildId-related functionality. */
+
+#include "js/BuildId.h"  // JS::BuildIdCharVector, JS::BuildIdOp, JS::GetOptimizedEncodingBuildId, JS::SetProcessBuildIdOp
+
+#include "mozilla/Atomics.h"  // mozilla::Atomic
+
+#include "jstypes.h"  // JS_PUBLIC_API
+
+#include "vm/Runtime.h"       // js::GetBuildId
+#include "wasm/WasmModule.h"  // js::wasm::GetOptimizedEncodingBuildId
+
+mozilla::Atomic<JS::BuildIdOp> js::GetBuildId;
+
+JS_PUBLIC_API void JS::SetProcessBuildIdOp(JS::BuildIdOp buildIdOp) {
+  js::GetBuildId = buildIdOp;
+}
+
+JS_PUBLIC_API bool JS::GetOptimizedEncodingBuildId(
+    JS::BuildIdCharVector* buildId) {
+  return js::wasm::GetOptimizedEncodingBuildId(buildId);
+}
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -22,16 +22,18 @@
 #include "gc/HashUtil.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
 #include "js/CharacterEncoding.h"
 #include "js/Date.h"
+#include "js/PropertyDescriptor.h"
+#include "js/PropertySpec.h"
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/Vector.h"
 #include "js/Wrapper.h"
 #include "proxy/ScriptedProxyHandler.h"
 #include "util/Text.h"
 #include "vm/ArgumentsObject.h"
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/Vector.h"
 
 #include <stdlib.h>
 
 #include "builtin/MapObject.h"
 #include "gc/Marking.h"
 #include "js/AllocPolicy.h"
 #include "js/Debug.h"
+#include "js/PropertySpec.h"
 #include "js/TracingAPI.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeCensus.h"
 #include "js/Utility.h"
 #include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Realm.h"
new file mode 100644
--- /dev/null
+++ b/js/src/vm/EqualityOperations.cpp
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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/EqualityOperations.h"  // js::LooselyEqual, js::StrictlyEqual, js::SameValue
+
+#include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_ASSERT_IF
+
+#include "jsapi.h"    // js::AssertHeapIsIdle
+#include "jsnum.h"    // js::StringToNumber
+#include "jstypes.h"  // JS_PUBLIC_API
+
+#include "js/Equality.h"  // JS::LooselyEqual, JS::StrictlyEqual, JS::SameValue
+#include "js/Result.h"    // JS_TRY_VAR_OR_RETURN_FALSE
+#include "js/RootingAPI.h"  // JS::Rooted
+#include "js/Value.h"       // JS::Int32Value, JS::SameType, JS::Value
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"  // JS::BigInt
+#endif                      // ENABLE_BIGINT
+#include "vm/JSContext.h"   // CHECK_THREAD
+#include "vm/JSObject.h"    // js::ToPrimitive
+#include "vm/StringType.h"  // js::EqualStrings
+
+#include "builtin/Boolean-inl.h"  // js::EmulatesUndefined
+#include "vm/JSContext-inl.h"     // JSContext::check
+
+static bool EqualGivenSameType(JSContext* cx, JS::Handle<JS::Value> lval,
+                               JS::Handle<JS::Value> rval, bool* equal) {
+  MOZ_ASSERT(JS::SameType(lval, rval));
+
+  if (lval.isString()) {
+    return js::EqualStrings(cx, lval.toString(), rval.toString(), equal);
+  }
+
+  if (lval.isDouble()) {
+    *equal = (lval.toDouble() == rval.toDouble());
+    return true;
+  }
+
+#ifdef ENABLE_BIGINT
+  if (lval.isBigInt()) {
+    *equal = JS::BigInt::equal(lval.toBigInt(), rval.toBigInt());
+    return true;
+  }
+#endif
+
+  if (lval.isGCThing()) {  // objects or symbols
+    *equal = (lval.toGCThing() == rval.toGCThing());
+    return true;
+  }
+
+  *equal = lval.get().payloadAsRawUint32() == rval.get().payloadAsRawUint32();
+  MOZ_ASSERT_IF(lval.isUndefined() || lval.isNull(), *equal);
+  return true;
+}
+
+static bool LooselyEqualBooleanAndOther(JSContext* cx,
+                                        JS::Handle<JS::Value> lval,
+                                        JS::Handle<JS::Value> rval,
+                                        bool* result) {
+  MOZ_ASSERT(!rval.isBoolean());
+
+  JS::Rooted<JS::Value> lvalue(cx, JS::Int32Value(lval.toBoolean() ? 1 : 0));
+
+  // The tail-call would end up in Step 3.
+  if (rval.isNumber()) {
+    *result = (lvalue.toNumber() == rval.toNumber());
+    return true;
+  }
+  // The tail-call would end up in Step 6.
+  if (rval.isString()) {
+    double num;
+    if (!StringToNumber(cx, rval.toString(), &num)) {
+      return false;
+    }
+    *result = (lvalue.toNumber() == num);
+    return true;
+  }
+
+  return js::LooselyEqual(cx, lvalue, rval, result);
+}
+
+// ES6 draft rev32 7.2.12 Abstract Equality Comparison
+bool js::LooselyEqual(JSContext* cx, JS::Handle<JS::Value> lval,
+                      JS::Handle<JS::Value> rval, bool* result) {
+  // Step 3.
+  if (JS::SameType(lval, rval)) {
+    return EqualGivenSameType(cx, lval, rval, result);
+  }
+
+  // Handle int32 x double.
+  if (lval.isNumber() && rval.isNumber()) {
+    *result = (lval.toNumber() == rval.toNumber());
+    return true;
+  }
+
+  // Step 4. This a bit more complex, because of the undefined emulating object.
+  if (lval.isNullOrUndefined()) {
+    // We can return early here, because null | undefined is only equal to the
+    // same set.
+    *result = rval.isNullOrUndefined() ||
+              (rval.isObject() && EmulatesUndefined(&rval.toObject()));
+    return true;
+  }
+
+  // Step 5.
+  if (rval.isNullOrUndefined()) {
+    MOZ_ASSERT(!lval.isNullOrUndefined());
+    *result = lval.isObject() && EmulatesUndefined(&lval.toObject());
+    return true;
+  }
+
+  // Step 6.
+  if (lval.isNumber() && rval.isString()) {
+    double num;
+    if (!StringToNumber(cx, rval.toString(), &num)) {
+      return false;
+    }
+    *result = (lval.toNumber() == num);
+    return true;
+  }
+
+  // Step 7.
+  if (lval.isString() && rval.isNumber()) {
+    double num;
+    if (!StringToNumber(cx, lval.toString(), &num)) {
+      return false;
+    }
+    *result = (num == rval.toNumber());
+    return true;
+  }
+
+  // Step 8.
+  if (lval.isBoolean()) {
+    return LooselyEqualBooleanAndOther(cx, lval, rval, result);
+  }
+
+  // Step 9.
+  if (rval.isBoolean()) {
+    return LooselyEqualBooleanAndOther(cx, rval, lval, result);
+  }
+
+  // Step 10.
+  if ((lval.isString() || lval.isNumber() || lval.isSymbol()) &&
+      rval.isObject()) {
+    JS::Rooted<JS::Value> rvalue(cx, rval);
+    if (!ToPrimitive(cx, &rvalue)) {
+      return false;
+    }
+    return js::LooselyEqual(cx, lval, rvalue, result);
+  }
+
+  // Step 11.
+  if (lval.isObject() &&
+      (rval.isString() || rval.isNumber() || rval.isSymbol())) {
+    JS::Rooted<JS::Value> lvalue(cx, lval);
+    if (!ToPrimitive(cx, &lvalue)) {
+      return false;
+    }
+    return js::LooselyEqual(cx, lvalue, rval, result);
+  }
+
+#ifdef ENABLE_BIGINT
+  if (lval.isBigInt()) {
+    JS::Rooted<JS::BigInt*> lbi(cx, lval.toBigInt());
+    bool tmpResult;
+    JS_TRY_VAR_OR_RETURN_FALSE(cx, tmpResult,
+                               JS::BigInt::looselyEqual(cx, lbi, rval));
+    *result = tmpResult;
+    return true;
+  }
+
+  if (rval.isBigInt()) {
+    JS::Rooted<JS::BigInt*> rbi(cx, rval.toBigInt());
+    bool tmpResult;
+    JS_TRY_VAR_OR_RETURN_FALSE(cx, tmpResult,
+                               JS::BigInt::looselyEqual(cx, rbi, lval));
+    *result = tmpResult;
+    return true;
+  }
+#endif
+
+  // Step 12.
+  *result = false;
+  return true;
+}
+
+JS_PUBLIC_API bool JS::LooselyEqual(JSContext* cx, Handle<Value> value1,
+                                    Handle<Value> value2, bool* equal) {
+  js::AssertHeapIsIdle();
+  CHECK_THREAD(cx);
+  cx->check(value1, value2);
+  MOZ_ASSERT(equal);
+  return js::LooselyEqual(cx, value1, value2, equal);
+}
+
+bool js::StrictlyEqual(JSContext* cx, JS::Handle<JS::Value> lval,
+                       JS::Handle<JS::Value> rval, bool* equal) {
+  if (SameType(lval, rval)) {
+    return EqualGivenSameType(cx, lval, rval, equal);
+  }
+
+  if (lval.isNumber() && rval.isNumber()) {
+    *equal = (lval.toNumber() == rval.toNumber());
+    return true;
+  }
+
+  *equal = false;
+  return true;
+}
+
+JS_PUBLIC_API bool JS::StrictlyEqual(JSContext* cx, Handle<Value> value1,
+                                     Handle<Value> value2, bool* equal) {
+  js::AssertHeapIsIdle();
+  CHECK_THREAD(cx);
+  cx->check(value1, value2);
+  MOZ_ASSERT(equal);
+  return js::StrictlyEqual(cx, value1, value2, equal);
+}
+
+static inline bool IsNegativeZero(const JS::Value& v) {
+  return v.isDouble() && mozilla::IsNegativeZero(v.toDouble());
+}
+
+static inline bool IsNaN(const JS::Value& v) {
+  return v.isDouble() && mozilla::IsNaN(v.toDouble());
+}
+
+bool js::SameValue(JSContext* cx, JS::Handle<JS::Value> v1,
+                   JS::Handle<JS::Value> v2, bool* same) {
+  if (IsNegativeZero(v1)) {
+    *same = IsNegativeZero(v2);
+    return true;
+  }
+
+  if (IsNegativeZero(v2)) {
+    *same = false;
+    return true;
+  }
+
+  if (IsNaN(v1) && IsNaN(v2)) {
+    *same = true;
+    return true;
+  }
+
+  return js::StrictlyEqual(cx, v1, v2, same);
+}
+
+JS_PUBLIC_API bool JS::SameValue(JSContext* cx, Handle<Value> value1,
+                                 Handle<Value> value2, bool* same) {
+  js::AssertHeapIsIdle();
+  CHECK_THREAD(cx);
+  cx->check(value1, value2);
+  MOZ_ASSERT(same);
+  return js::SameValue(cx, value1, value2, same);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/EqualityOperations.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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/. */
+
+/*
+ * The equality comparisons of js/Equality.h, but with extra efficiency for
+ * SpiderMonkey-internal callers.
+ *
+ * These functions, assuming they're passed C++-valid arguments, are identical
+ * to the same-named JS::-namespaced functions -- just with hidden linkage (so
+ * they're more efficient to call), and without various external-caller-focused
+ * JSAPI-usage assertions performed that SpiderMonkey users never come close to
+ * failing.
+ */
+
+#ifndef vm_EqualityOperations_h
+#define vm_EqualityOperations_h
+
+#include "js/RootingAPI.h"  // JS::Handle
+#include "js/Value.h"       // JS::Value
+
+struct JSContext;
+
+namespace js {
+
+/** Computes |lval === rval|. */
+extern bool StrictlyEqual(JSContext* cx, JS::Handle<JS::Value> lval,
+                          JS::Handle<JS::Value> rval, bool* equal);
+
+/** Computes |lval == rval|. */
+extern bool LooselyEqual(JSContext* cx, JS::Handle<JS::Value> lval,
+                         JS::Handle<JS::Value> rval, bool* equal);
+
+/**
+ * Computes |SameValue(v1, v2)| -- strict equality except that NaNs are
+ * considered equal and opposite-signed zeroes are considered unequal.
+ */
+extern bool SameValue(JSContext* cx, JS::Handle<JS::Value> v1,
+                      JS::Handle<JS::Value> v2, bool* same);
+
+}  // namespace js
+
+#endif  // vm_EqualityOperations_h
--- a/js/src/vm/ErrorReporting.cpp
+++ b/js/src/vm/ErrorReporting.cpp
@@ -53,17 +53,17 @@ void js::CompileError::throwError(JSCont
 
 bool js::ReportExceptionClosure::operator()(JSContext* cx) {
   cx->setPendingException(exn_);
   return false;
 }
 
 bool js::ReportCompileWarning(JSContext* cx, ErrorMetadata&& metadata,
                               UniquePtr<JSErrorNotes> notes, unsigned flags,
-                              unsigned errorNumber, va_list args) {
+                              unsigned errorNumber, va_list* args) {
   // On the main thread, report the error immediately. When compiling off
   // thread, save the error so that the thread finishing the parse can report
   // it later.
   CompileError tempErr;
   CompileError* err = &tempErr;
   if (cx->helperThread() && !cx->addPendingCompileError(&err)) {
     return false;
   }
@@ -78,30 +78,30 @@ bool js::ReportCompileWarning(JSContext*
   err->isMuted = metadata.isMuted;
 
   if (UniqueTwoByteChars lineOfContext = std::move(metadata.lineOfContext)) {
     err->initOwnedLinebuf(lineOfContext.release(), metadata.lineLength,
                           metadata.tokenOffset);
   }
 
   if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber,
-                              nullptr, ArgumentsAreLatin1, err, args)) {
+                              nullptr, ArgumentsAreLatin1, err, *args)) {
     return false;
   }
 
   if (!cx->helperThread()) {
     err->throwError(cx);
   }
 
   return true;
 }
 
 void js::ReportCompileError(JSContext* cx, ErrorMetadata&& metadata,
                             UniquePtr<JSErrorNotes> notes, unsigned flags,
-                            unsigned errorNumber, va_list args) {
+                            unsigned errorNumber, va_list* args) {
   // On the main thread, report the error immediately. When compiling off
   // thread, save the error so that the thread finishing the parse can report
   // it later.
   CompileError tempErr;
   CompileError* err = &tempErr;
   if (cx->helperThread() && !cx->addPendingCompileError(&err)) {
     return;
   }
@@ -116,17 +116,17 @@ void js::ReportCompileError(JSContext* c
   err->isMuted = metadata.isMuted;
 
   if (UniqueTwoByteChars lineOfContext = std::move(metadata.lineOfContext)) {
     err->initOwnedLinebuf(lineOfContext.release(), metadata.lineLength,
                           metadata.tokenOffset);
   }
 
   if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber,
-                              nullptr, ArgumentsAreLatin1, err, args)) {
+                              nullptr, ArgumentsAreLatin1, err, *args)) {
     return;
   }
 
   if (!cx->helperThread()) {
     err->throwError(cx);
   }
 }
 
--- a/js/src/vm/ErrorReporting.h
+++ b/js/src/vm/ErrorReporting.h
@@ -81,29 +81,29 @@ class MOZ_STACK_CLASS ReportExceptionClo
 extern void CallWarningReporter(JSContext* cx, JSErrorReport* report);
 
 /**
  * Report a compile error during script processing prior to execution of the
  * script.
  */
 extern void ReportCompileError(JSContext* cx, ErrorMetadata&& metadata,
                                UniquePtr<JSErrorNotes> notes, unsigned flags,
-                               unsigned errorNumber, va_list args);
+                               unsigned errorNumber, va_list* args);
 
 /**
  * Report a compile warning during script processing prior to execution of the
  * script.  Returns true if the warning was successfully reported, false if an
  * error occurred.
  *
  * This function DOES NOT respect an existing werror option.  If the caller
  * wishes such option to be respected, it must do so itself.
  */
 extern MOZ_MUST_USE bool ReportCompileWarning(
     JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
-    unsigned flags, unsigned errorNumber, va_list args);
+    unsigned flags, unsigned errorNumber, va_list* args);
 
 class GlobalObject;
 
 /**
  * Report the given error Value to the given global.  The JSContext is not
  * assumed to be in any particular realm, but the global and error are
  * expected to be same-compartment.
  */
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * 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/GeneratorObject.h"
 
+#include "js/PropertySpec.h"
 #include "vm/JSObject.h"
 
 #include "vm/ArrayObject-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Stack-inl.h"
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -35,16 +35,17 @@
 #include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #ifdef ENABLE_BIGINT
 #include "vm/BigIntType.h"
 #endif
 #include "vm/BytecodeUtil.h"
 #include "vm/Debugger.h"
+#include "vm/EqualityOperations.h"  // js::StrictlyEqual
 #include "vm/GeneratorObject.h"
 #include "vm/Iteration.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Opcodes.h"
@@ -858,209 +859,16 @@ bool js::HasInstance(JSContext* cx, Hand
   const Class* clasp = obj->getClass();
   RootedValue local(cx, v);
   if (JSHasInstanceOp hasInstance = clasp->getHasInstance()) {
     return hasInstance(cx, obj, &local, bp);
   }
   return JS::InstanceofOperator(cx, obj, local, bp);
 }
 
-static inline bool EqualGivenSameType(JSContext* cx, HandleValue lval,
-                                      HandleValue rval, bool* equal) {
-  MOZ_ASSERT(SameType(lval, rval));
-
-  if (lval.isString()) {
-    return EqualStrings(cx, lval.toString(), rval.toString(), equal);
-  }
-  if (lval.isDouble()) {
-    *equal = (lval.toDouble() == rval.toDouble());
-    return true;
-  }
-#ifdef ENABLE_BIGINT
-  if (lval.isBigInt()) {
-    *equal = BigInt::equal(lval.toBigInt(), rval.toBigInt());
-    return true;
-  }
-#endif
-  if (lval.isGCThing()) {  // objects or symbols
-    *equal = (lval.toGCThing() == rval.toGCThing());
-    return true;
-  }
-  *equal = lval.get().payloadAsRawUint32() == rval.get().payloadAsRawUint32();
-  MOZ_ASSERT_IF(lval.isUndefined() || lval.isNull(), *equal);
-  return true;
-}
-
-static inline bool LooselyEqualBooleanAndOther(JSContext* cx, HandleValue lval,
-                                               HandleValue rval, bool* result) {
-  MOZ_ASSERT(!rval.isBoolean());
-  RootedValue lvalue(cx, Int32Value(lval.toBoolean() ? 1 : 0));
-
-  // The tail-call would end up in Step 3.
-  if (rval.isNumber()) {
-    *result = (lvalue.toNumber() == rval.toNumber());
-    return true;
-  }
-  // The tail-call would end up in Step 6.
-  if (rval.isString()) {
-    double num;
-    if (!StringToNumber(cx, rval.toString(), &num)) {
-      return false;
-    }
-    *result = (lvalue.toNumber() == num);
-    return true;
-  }
-
-  return LooselyEqual(cx, lvalue, rval, result);
-}
-
-// ES6 draft rev32 7.2.12 Abstract Equality Comparison
-bool js::LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval,
-                      bool* result) {
-  // Step 3.
-  if (SameType(lval, rval)) {
-    return EqualGivenSameType(cx, lval, rval, result);
-  }
-
-  // Handle int32 x double.
-  if (lval.isNumber() && rval.isNumber()) {
-    *result = (lval.toNumber() == rval.toNumber());
-    return true;
-  }
-
-  // Step 4. This a bit more complex, because of the undefined emulating object.
-  if (lval.isNullOrUndefined()) {
-    // We can return early here, because null | undefined is only equal to the
-    // same set.
-    *result = rval.isNullOrUndefined() ||
-              (rval.isObject() && EmulatesUndefined(&rval.toObject()));
-    return true;
-  }
-
-  // Step 5.
-  if (rval.isNullOrUndefined()) {
-    MOZ_ASSERT(!lval.isNullOrUndefined());
-    *result = lval.isObject() && EmulatesUndefined(&lval.toObject());
-    return true;
-  }
-
-  // Step 6.
-  if (lval.isNumber() && rval.isString()) {
-    double num;
-    if (!StringToNumber(cx, rval.toString(), &num)) {
-      return false;
-    }
-    *result = (lval.toNumber() == num);
-    return true;
-  }
-
-  // Step 7.
-  if (lval.isString() && rval.isNumber()) {
-    double num;
-    if (!StringToNumber(cx, lval.toString(), &num)) {
-      return false;
-    }
-    *result = (num == rval.toNumber());
-    return true;
-  }
-
-  // Step 8.
-  if (lval.isBoolean()) {
-    return LooselyEqualBooleanAndOther(cx, lval, rval, result);
-  }
-
-  // Step 9.
-  if (rval.isBoolean()) {
-    return LooselyEqualBooleanAndOther(cx, rval, lval, result);
-  }
-
-  // Step 10.
-  if ((lval.isString() || lval.isNumber() || lval.isSymbol()) &&
-      rval.isObject()) {
-    RootedValue rvalue(cx, rval);
-    if (!ToPrimitive(cx, &rvalue)) {
-      return false;
-    }
-    return LooselyEqual(cx, lval, rvalue, result);
-  }
-
-  // Step 11.
-  if (lval.isObject() &&
-      (rval.isString() || rval.isNumber() || rval.isSymbol())) {
-    RootedValue lvalue(cx, lval);
-    if (!ToPrimitive(cx, &lvalue)) {
-      return false;
-    }
-    return LooselyEqual(cx, lvalue, rval, result);
-  }
-
-#ifdef ENABLE_BIGINT
-  if (lval.isBigInt()) {
-    RootedBigInt lbi(cx, lval.toBigInt());
-    bool tmpResult;
-    JS_TRY_VAR_OR_RETURN_FALSE(cx, tmpResult,
-                               BigInt::looselyEqual(cx, lbi, rval));
-    *result = tmpResult;
-    return true;
-  }
-
-  if (rval.isBigInt()) {
-    RootedBigInt rbi(cx, rval.toBigInt());
-    bool tmpResult;
-    JS_TRY_VAR_OR_RETURN_FALSE(cx, tmpResult,
-                               BigInt::looselyEqual(cx, rbi, lval));
-    *result = tmpResult;
-    return true;
-  }
-#endif
-
-  // Step 12.
-  *result = false;
-  return true;
-}
-
-bool js::StrictlyEqual(JSContext* cx, HandleValue lval, HandleValue rval,
-                       bool* equal) {
-  if (SameType(lval, rval)) {
-    return EqualGivenSameType(cx, lval, rval, equal);
-  }
-
-  if (lval.isNumber() && rval.isNumber()) {
-    *equal = (lval.toNumber() == rval.toNumber());
-    return true;
-  }
-
-  *equal = false;
-  return true;
-}
-
-static inline bool IsNegativeZero(const Value& v) {
-  return v.isDouble() && mozilla::IsNegativeZero(v.toDouble());
-}
-
-static inline bool IsNaN(const Value& v) {
-  return v.isDouble() && mozilla::IsNaN(v.toDouble());
-}
-
-bool js::SameValue(JSContext* cx, HandleValue v1, HandleValue v2, bool* same) {
-  if (IsNegativeZero(v1)) {
-    *same = IsNegativeZero(v2);
-    return true;
-  }
-  if (IsNegativeZero(v2)) {
-    *same = false;
-    return true;
-  }
-  if (IsNaN(v1) && IsNaN(v2)) {
-    *same = true;
-    return true;
-  }
-  return StrictlyEqual(cx, v1, v2, same);
-}
-
 JSType js::TypeOfObject(JSObject* obj) {
   if (EmulatesUndefined(obj)) {
     return JSTYPE_UNDEFINED;
   }
   if (obj->isCallable()) {
     return JSTYPE_FUNCTION;
   }
   return JSTYPE_OBJECT;
@@ -2643,24 +2451,26 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
 
     CASE(JSOP_NE) {
       if (!LooseEqualityOp<false>(cx, REGS)) {
         goto error;
       }
     }
     END_CASE(JSOP_NE)
 
-#define STRICT_EQUALITY_OP(OP, COND)                        \
-  JS_BEGIN_MACRO                                            \
-    HandleValue lval = REGS.stackHandleAt(-2);              \
-    HandleValue rval = REGS.stackHandleAt(-1);              \
-    bool equal;                                             \
-    if (!StrictlyEqual(cx, lval, rval, &equal)) goto error; \
-    (COND) = equal OP true;                                 \
-    REGS.sp--;                                              \
+#define STRICT_EQUALITY_OP(OP, COND)                  \
+  JS_BEGIN_MACRO                                      \
+    HandleValue lval = REGS.stackHandleAt(-2);        \
+    HandleValue rval = REGS.stackHandleAt(-1);        \
+    bool equal;                                       \
+    if (!js::StrictlyEqual(cx, lval, rval, &equal)) { \
+      goto error;                                     \
+    }                                                 \
+    (COND) = equal OP true;                           \
+    REGS.sp--;                                        \
   JS_END_MACRO
 
     CASE(JSOP_STRICTEQ) {
       bool cond;
       STRICT_EQUALITY_OP(==, cond);
       REGS.sp[-1].setBoolean(cond);
     }
     END_CASE(JSOP_STRICTEQ)
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -284,26 +284,16 @@ inline void RunState::setReturnValue(con
     asInvoke()->setReturnValue(v);
   } else {
     asExecute()->setReturnValue(v);
   }
 }
 
 extern bool RunScript(JSContext* cx, RunState& state);
 
-extern bool StrictlyEqual(JSContext* cx, HandleValue lval, HandleValue rval,
-                          bool* equal);
-
-extern bool LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval,
-                         bool* equal);
-
-/* === except that NaN is the same as NaN and -0 is not the same as +0. */
-extern bool SameValue(JSContext* cx, HandleValue v1, HandleValue v2,
-                      bool* same);
-
 extern JSType TypeOfObject(JSObject* obj);
 
 extern JSType TypeOfValue(const Value& v);
 
 extern bool HasInstance(JSContext* cx, HandleObject obj, HandleValue v,
                         bool* bp);
 
 // Unwind environment chain and iterator to match the scope corresponding to
--- a/js/src/vm/Iteration.cpp
+++ b/js/src/vm/Iteration.cpp
@@ -22,16 +22,17 @@
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "builtin/Array.h"
 #include "builtin/SelfHostingDefines.h"
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
+#include "js/PropertySpec.h"
 #include "js/Proxy.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/GeneratorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -30,16 +30,17 @@
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/CompileOptions.h"
+#include "js/PropertySpec.h"
 #include "js/Proxy.h"
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/Debugger.h"
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -32,16 +32,18 @@
 #include "builtin/Object.h"
 #include "builtin/String.h"
 #include "builtin/Symbol.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Policy.h"
 #include "jit/BaselineJIT.h"
 #include "js/CharacterEncoding.h"
 #include "js/MemoryMetrics.h"
+#include "js/PropertyDescriptor.h"  // JS::FromPropertyDescriptor
+#include "js/PropertySpec.h"
 #include "js/Proxy.h"
 #include "js/UbiNode.h"
 #include "js/UniquePtr.h"
 #include "js/Wrapper.h"
 #include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/BytecodeUtil.h"
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 
 #include "gc/Marking.h"
 #include "jit/BaselineIC.h"
 #include "js/CharacterEncoding.h"
 #include "js/Value.h"
 #include "vm/Debugger.h"
+#include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "gc/Nursery-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1506,20 +1506,20 @@ class NativeObject : public ShapedObject
   inline js::GlobalObject& global() const;
 
   /* JIT Accessors */
   static size_t offsetOfElements() { return offsetof(NativeObject, elements_); }
   static size_t offsetOfFixedElements() {
     return sizeof(NativeObject) + sizeof(ObjectElements);
   }
 
-  static size_t getFixedSlotOffset(size_t slot) {
+  static constexpr size_t getFixedSlotOffset(size_t slot) {
     return sizeof(NativeObject) + slot * sizeof(Value);
   }
-  static size_t getPrivateDataOffset(size_t nfixed) {
+  static constexpr size_t getPrivateDataOffset(size_t nfixed) {
     return getFixedSlotOffset(nfixed);
   }
   static size_t offsetOfSlots() { return offsetof(NativeObject, slots_); }
 };
 
 // Object class for plain native objects created using '{}' object literals,
 // 'new Object()', 'Object.create', etc.
 class PlainObject : public NativeObject {
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -61,17 +61,16 @@ using mozilla::Atomic;
 using mozilla::DebugOnly;
 using mozilla::NegativeInfinity;
 using mozilla::PodZero;
 using mozilla::PositiveInfinity;
 
 /* static */ MOZ_THREAD_LOCAL(JSContext*) js::TlsContext;
 /* static */ Atomic<size_t> JSRuntime::liveRuntimesCount;
 Atomic<JS::LargeAllocationFailureCallback> js::OnLargeAllocationFailure;
-Atomic<JS::BuildIdOp> js::GetBuildId;
 
 namespace js {
 bool gCanUseExtraThreads = true;
 }  // namespace js
 
 void js::DisableExtraThreads() { gCanUseExtraThreads = false; }
 
 const JSSecurityCallbacks js::NullSecurityCallbacks = {};
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -25,16 +25,17 @@
 #include "builtin/AtomicsObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "builtin/Promise.h"
 #include "frontend/BinSourceRuntimeSupport.h"
 #include "frontend/NameCollections.h"
 #include "gc/GCRuntime.h"
 #include "gc/Tracer.h"
 #include "irregexp/RegExpStack.h"
+#include "js/BuildId.h"  // JS::BuildIdOp
 #include "js/Debug.h"
 #include "js/GCVector.h"
 #include "js/HashTable.h"
 #ifdef DEBUG
 #include "js/Proxy.h"  // For AutoEnterPolicy
 #endif
 #include "js/Stream.h"
 #include "js/Symbol.h"
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -20,16 +20,17 @@
 #include "jsnum.h"
 
 #include "gc/FreeOp.h"
 #include "gc/HashUtil.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/Rooting.h"
 #include "js/CharacterEncoding.h"
+#include "js/PropertySpec.h"
 #include "js/SavedFrameAPI.h"
 #include "js/Vector.h"
 #include "util/StringBuffer.h"
 #include "vm/Debugger.h"
 #include "vm/GeckoProfiler.h"
 #include "vm/JSScript.h"
 #include "vm/Realm.h"
 #include "vm/SavedFrame.h"
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -33,16 +33,17 @@
 #include "builtin/WeakMapObject.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/Date.h"
+#include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Compression.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/Atomics.h"
 #include "mozilla/CheckedInt.h"
 
 #include "jsfriendapi.h"
 
 #include "gc/FreeOp.h"
 #include "jit/AtomicOperations.h"
+#include "js/PropertySpec.h"
 #include "js/Wrapper.h"
 #include "vm/SharedMem.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmTypes.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -3,17 +3,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/. */
 
 #include "vm/TypedArrayObject-inl.h"
 #include "vm/TypedArrayObject.h"
 
 #include "mozilla/Alignment.h"
-#include "mozilla/Casting.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TextUtils.h"
 
 #include <string.h>
 #ifndef XP_WIN
 #include <sys/mman.h>
 #endif
@@ -25,16 +24,17 @@
 
 #include "builtin/Array.h"
 #include "builtin/DataViewObject.h"
 #include "builtin/TypedObjectConstants.h"
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "jit/InlinableNatives.h"
 #include "js/Conversions.h"
+#include "js/PropertySpec.h"
 #include "js/UniquePtr.h"
 #include "js/Wrapper.h"
 #include "util/Windows.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
@@ -50,54 +50,44 @@
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::CanonicalizeNaN;
 using JS::ToInt32;
 using JS::ToUint32;
-using mozilla::AssertedCast;
 using mozilla::IsAsciiDigit;
 
 /*
  * TypedArrayObject
  *
  * The non-templated base class for the specific typed implementations.
  * This class holds all the member variables that are used by
  * the subclasses.
  */
 
-/* static */ int TypedArrayObject::lengthOffset() {
-  return NativeObject::getFixedSlotOffset(LENGTH_SLOT);
-}
-
-/* static */ int TypedArrayObject::dataOffset() {
-  return NativeObject::getPrivateDataOffset(DATA_SLOT);
-}
-
 /* static */ bool TypedArrayObject::is(HandleValue v) {
   return v.isObject() && v.toObject().is<TypedArrayObject>();
 }
 
 /* static */ bool TypedArrayObject::ensureHasBuffer(
     JSContext* cx, Handle<TypedArrayObject*> tarray) {
   if (tarray->hasBuffer()) {
     return true;
   }
 
   Rooted<ArrayBufferObject*> buffer(
       cx, ArrayBufferObject::create(cx, tarray->byteLength()));
   if (!buffer) {
     return false;
   }
 
-  if (!buffer->addView(cx, tarray)) {
-    return false;
-  }
+  // Attaching the first view to an array buffer is infallible.
+  MOZ_ALWAYS_TRUE(buffer->addView(cx, tarray));
 
   // tarray is not shared, because if it were it would have a buffer.
   memcpy(buffer->dataPointer(), tarray->dataPointerUnshared(),
          tarray->byteLength());
 
   // If the object is in the nursery, the buffer will be freed by the next
   // nursery GC. Free the data slot pointer if the object has no inline data.
   Nursery& nursery = cx->nursery();
@@ -125,17 +115,17 @@ void TypedArrayObject::assertZeroLengthA
 }
 #endif
 
 void TypedArrayObject::finalize(FreeOp* fop, JSObject* obj) {
   MOZ_ASSERT(!IsInsideNursery(obj));
   TypedArrayObject* curObj = &obj->as<TypedArrayObject>();
 
   // Template objects or discarded objects (which didn't have enough room
-  // for inner elements). Don't have anything to free.
+  // for inner elements) don't have anything to free.
   if (!curObj->elementsRaw()) {
     return;
   }
 
   curObj->assertZeroLengthArrayData();
 
   // Typed arrays with a buffer object do not need to be free'd
   if (curObj->hasBuffer()) {
@@ -146,17 +136,17 @@ void TypedArrayObject::finalize(FreeOp* 
   if (!curObj->hasInlineElements()) {
     js_free(curObj->elements());
   }
 }
 
 /* static */ size_t TypedArrayObject::objectMoved(JSObject* obj,
                                                   JSObject* old) {
   TypedArrayObject* newObj = &obj->as<TypedArrayObject>();
-  TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
+  const TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
   MOZ_ASSERT(newObj->elementsRaw() == oldObj->elementsRaw());
   MOZ_ASSERT(obj->isTenured());
 
   // Typed arrays with a buffer object do not need an update.
   if (oldObj->hasBuffer()) {
     return 0;
   }
 
@@ -175,29 +165,19 @@ void TypedArrayObject::finalize(FreeOp* 
   if (!nursery.isInside(buf)) {
     nursery.removeMallocedBuffer(buf);
     return 0;
   }
 
   // Determine if we can use inline data for the target array. If this is
   // possible, the nursery will have picked an allocation size that is large
   // enough.
-  size_t nbytes = 0;
-  switch (oldObj->type()) {
-#define OBJECT_MOVED_TYPED_ARRAY(T, N)     \
-  case Scalar::N:                          \
-    nbytes = oldObj->length() * sizeof(T); \
-    break;
-    JS_FOR_EACH_TYPED_ARRAY(OBJECT_MOVED_TYPED_ARRAY)
-#undef OBJECT_MOVED_TYPED_ARRAY
-    default:
-      MOZ_CRASH("Unsupported TypedArray type");
-  }
+  size_t nbytes = oldObj->byteLength();
 
-  size_t headerSize = dataOffset() + sizeof(HeapSlot);
+  constexpr size_t headerSize = dataOffset() + sizeof(HeapSlot);
 
   // See AllocKindForLazyBuffer.
   gc::AllocKind newAllocKind = obj->asTenured().getAllocKind();
   MOZ_ASSERT_IF(nbytes == 0,
                 headerSize + sizeof(uint8_t) <= GetGCKindBytes(newAllocKind));
 
   if (headerSize + nbytes <= GetGCKindBytes(newAllocKind)) {
     MOZ_ASSERT(oldObj->hasInlineElements());
@@ -273,39 +253,38 @@ uint32_t JS_FASTCALL js::ClampDoubleToUi
      * want.
      */
     return y & ~1;
   }
 
   return y;
 }
 
-template <typename ElementType>
-static inline JSObject* NewArray(JSContext* cx, uint32_t nelements);
-
 namespace {
 
 enum class SpeciesConstructorOverride { None, ArrayBuffer };
 
-enum class CreateSingleton { Yes, No };
+enum class CreateSingleton { No, Yes };
 
 template <typename NativeType>
 class TypedArrayObjectTemplate : public TypedArrayObject {
   friend class TypedArrayObject;
 
  public:
   static constexpr Scalar::Type ArrayTypeID() {
     return TypeIDOfType<NativeType>::id;
   }
-  static bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
-  static bool ArrayTypeIsFloatingPoint() {
+  static constexpr bool ArrayTypeIsUnsigned() {
+    return TypeIsUnsigned<NativeType>();
+  }
+  static constexpr bool ArrayTypeIsFloatingPoint() {
     return TypeIsFloatingPoint<NativeType>();
   }
 
-  static const size_t BYTES_PER_ELEMENT = sizeof(NativeType);
+  static constexpr size_t BYTES_PER_ELEMENT = sizeof(NativeType);
 
   static JSObject* createPrototype(JSContext* cx, JSProtoKey key) {
     Handle<GlobalObject*> global = cx->global();
     RootedObject typedArrayProto(
         cx, GlobalObject::getOrCreateTypedArrayPrototype(cx, global));
     if (!typedArrayProto) {
       return nullptr;
     }
@@ -409,21 +388,21 @@ class TypedArrayObjectTemplate : public 
 
     return &obj->as<TypedArrayObject>();
   }
 
   static TypedArrayObject* makeInstance(
       JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
       CreateSingleton createSingleton, uint32_t byteOffset, uint32_t len,
       HandleObject proto) {
-    MOZ_ASSERT(len < INT32_MAX / sizeof(NativeType));
+    MOZ_ASSERT(len < INT32_MAX / BYTES_PER_ELEMENT);
 
     gc::AllocKind allocKind =
         buffer ? gc::GetGCObjectKind(instanceClass())
-               : AllocKindForLazyBuffer(len * sizeof(NativeType));
+               : AllocKindForLazyBuffer(len * BYTES_PER_ELEMENT);
 
     // Subclassing mandates that we hand in the proto every time. Most of
     // the time, though, that [[Prototype]] will not be interesting. If
     // it isn't, we can do some more TI optimizations.
     RootedObject checkProto(cx);
     if (proto) {
       checkProto = GlobalObject::getOrCreatePrototype(
           cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()));
@@ -451,18 +430,16 @@ class TypedArrayObjectTemplate : public 
     size_t nbytes;
     MOZ_ALWAYS_TRUE(CalculateAllocSize<NativeType>(len, &nbytes));
     MOZ_ASSERT(nbytes < TypedArrayObject::SINGLETON_BYTE_LENGTH);
     NewObjectKind newKind = TenuredObject;
     bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
     const Class* clasp = instanceClass();
     gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(clasp)
                                           : AllocKindForLazyBuffer(nbytes);
-    MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, clasp));
-    allocKind = GetBackgroundAllocKind(allocKind);
 
     AutoSetNewObjectMetadata metadata(cx);
     jsbytecode* pc;
     RootedScript script(cx, cx->currentScript(&pc));
     if (script &&
         ObjectGroup::useSingletonForAllocationSite(script, pc, clasp)) {
       newKind = SingletonObject;
     }
@@ -484,20 +461,19 @@ class TypedArrayObjectTemplate : public 
       return nullptr;
     }
 
     return tarray;
   }
 
   static void initTypedArraySlots(TypedArrayObject* tarray, int32_t len) {
     MOZ_ASSERT(len >= 0);
-    tarray->setFixedSlot(TypedArrayObject::BUFFER_SLOT, NullValue());
-    tarray->setFixedSlot(TypedArrayObject::LENGTH_SLOT,
-                         Int32Value(AssertedCast<int32_t>(len)));
-    tarray->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(0));
+    tarray->initFixedSlot(TypedArrayObject::BUFFER_SLOT, NullValue());
+    tarray->initFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(len));
+    tarray->initFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(0));
 
     // Verify that the private slot is at the expected place.
     MOZ_ASSERT(tarray->numFixedSlots() == TypedArrayObject::DATA_SLOT);
 
 #ifdef DEBUG
     if (len == 0) {
       uint8_t* output = tarray->fixedData(TypedArrayObject::FIXED_DATA_START);
       output[0] = TypedArrayObject::ZeroLengthArrayData;
@@ -511,55 +487,52 @@ class TypedArrayObjectTemplate : public 
     if (buf) {
 #ifdef DEBUG
       Nursery& nursery = cx->nursery();
       MOZ_ASSERT_IF(!nursery.isInside(buf) && !tarray->hasInlineElements(),
                     tarray->isTenured());
 #endif
       tarray->initPrivate(buf);
     } else {
-      size_t nbytes = len * sizeof(NativeType);
+      size_t nbytes = len * BYTES_PER_ELEMENT;
 #ifdef DEBUG
-      size_t dataOffset = TypedArrayObject::dataOffset();
-      size_t offset = dataOffset + sizeof(HeapSlot);
+      constexpr size_t dataOffset = TypedArrayObject::dataOffset();
+      constexpr size_t offset = dataOffset + sizeof(HeapSlot);
       MOZ_ASSERT(offset + nbytes <= GetGCKindBytes(allocKind));
 #endif
 
       void* data = tarray->fixedData(FIXED_DATA_START);
       tarray->initPrivate(data);
       memset(data, 0, nbytes);
     }
   }
 
   static TypedArrayObject* makeTypedArrayWithTemplate(
       JSContext* cx, TypedArrayObject* templateObj, int32_t len) {
-    if (len < 0 || uint32_t(len) >= INT32_MAX / sizeof(NativeType)) {
+    if (len < 0 || uint32_t(len) >= INT32_MAX / BYTES_PER_ELEMENT) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_BAD_ARRAY_LENGTH);
       return nullptr;
     }
 
-    size_t nbytes;
-    MOZ_ALWAYS_TRUE(js::CalculateAllocSize<NativeType>(len, &nbytes));
-
+    size_t nbytes = len * BYTES_PER_ELEMENT;
     bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
 
     AutoSetNewObjectMetadata metadata(cx);
 
-    const Class* clasp = templateObj->group()->clasp();
-    gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(clasp)
+    gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(instanceClass())
                                           : AllocKindForLazyBuffer(nbytes);
-    MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, clasp));
-    allocKind = GetBackgroundAllocKind(allocKind);
     RootedObjectGroup group(cx, templateObj->group());
+    MOZ_ASSERT(group->clasp() == instanceClass());
 
     NewObjectKind newKind = TenuredObject;
 
     UniquePtr<void, JS::FreePolicy> buf;
-    if (!fitsInline && len > 0) {
+    if (!fitsInline) {
+      MOZ_ASSERT(len > 0);
       buf.reset(cx->pod_calloc<uint8_t>(nbytes));
       if (!buf) {
         return nullptr;
       }
     }
 
     TypedArrayObject* obj =
         NewObjectWithGroup<TypedArrayObject>(cx, group, allocKind, newKind);
@@ -638,17 +611,17 @@ class TypedArrayObjectTemplate : public 
     uint64_t byteOffset = 0;
     if (args.hasDefined(1)) {
       // Step 6.
       if (!ToIndex(cx, args[1], &byteOffset)) {
         return nullptr;
       }
 
       // Step 7.
-      if (byteOffset % sizeof(NativeType) != 0) {
+      if (byteOffset % BYTES_PER_ELEMENT != 0) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
         return nullptr;
       }
     }
 
     uint64_t length = UINT64_MAX;
     if (args.hasDefined(2)) {
@@ -668,17 +641,17 @@ class TypedArrayObjectTemplate : public 
   }
 
   // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
   // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
   // Steps 9-12.
   static bool computeAndCheckLength(
       JSContext* cx, HandleArrayBufferObjectMaybeShared bufferMaybeUnwrapped,
       uint64_t byteOffset, uint64_t lengthIndex, uint32_t* length) {
-    MOZ_ASSERT(byteOffset % sizeof(NativeType) == 0);
+    MOZ_ASSERT(byteOffset % BYTES_PER_ELEMENT == 0);
     MOZ_ASSERT(byteOffset < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
     MOZ_ASSERT_IF(lengthIndex != UINT64_MAX,
                   lengthIndex < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
 
     // Step 9.
     if (bufferMaybeUnwrapped->isDetached()) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_TYPED_ARRAY_DETACHED);
@@ -686,49 +659,49 @@ class TypedArrayObjectTemplate : public 
     }
 
     // Step 10.
     uint32_t bufferByteLength = bufferMaybeUnwrapped->byteLength();
 
     uint32_t len;
     if (lengthIndex == UINT64_MAX) {
       // Steps 11.a, 11.c.
-      if (bufferByteLength % sizeof(NativeType) != 0 ||
+      if (bufferByteLength % BYTES_PER_ELEMENT != 0 ||
           byteOffset > bufferByteLength) {
         // The given byte array doesn't map exactly to
-        // |sizeof(NativeType) * N| or |byteOffset| is invalid.
+        // |BYTES_PER_ELEMENT * N| or |byteOffset| is invalid.
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
         return false;
       }
 
       // Step 11.b.
       uint32_t newByteLength = bufferByteLength - uint32_t(byteOffset);
-      len = newByteLength / sizeof(NativeType);
+      len = newByteLength / BYTES_PER_ELEMENT;
     } else {
       // Step 12.a.
-      uint64_t newByteLength = lengthIndex * sizeof(NativeType);
+      uint64_t newByteLength = lengthIndex * BYTES_PER_ELEMENT;
 
       // Step 12.b.
       if (byteOffset + newByteLength > bufferByteLength) {
         // |byteOffset + newByteLength| is too big for the arraybuffer
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
         return false;
       }
 
       len = uint32_t(lengthIndex);
     }
 
     // ArrayBuffer is too large for TypedArrays:
     // Standalone ArrayBuffers can hold up to INT32_MAX bytes, whereas
     // buffers in TypedArrays must have less than or equal to
-    // |INT32_MAX - sizeof(NativeType) - INT32_MAX % sizeof(NativeType)|
+    // |INT32_MAX - BYTES_PER_ELEMENT - INT32_MAX % BYTES_PER_ELEMENT|
     // bytes.
-    if (len >= INT32_MAX / sizeof(NativeType)) {
+    if (len >= INT32_MAX / BYTES_PER_ELEMENT) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
       return false;
     }
     MOZ_ASSERT(byteOffset <= UINT32_MAX);
 
     *length = len;
     return true;
@@ -742,18 +715,17 @@ class TypedArrayObjectTemplate : public 
       uint64_t byteOffset, uint64_t lengthIndex, HandleObject proto) {
     // Steps 9-12.
     uint32_t length;
     if (!computeAndCheckLength(cx, buffer, byteOffset, lengthIndex, &length)) {
       return nullptr;
     }
 
     CreateSingleton createSingleton = CreateSingleton::No;
-    if (length * sizeof(NativeType) >=
-        TypedArrayObject::SINGLETON_BYTE_LENGTH) {
+    if (length * BYTES_PER_ELEMENT >= TypedArrayObject::SINGLETON_BYTE_LENGTH) {
       createSingleton = CreateSingleton::Yes;
     }
 
     // Steps 13-17.
     return makeInstance(cx, buffer, createSingleton, uint32_t(byteOffset),
                         length, proto);
   }
 
@@ -827,45 +799,44 @@ class TypedArrayObjectTemplate : public 
     }
 
     return typedArray;
   }
 
  public:
   static JSObject* fromBuffer(JSContext* cx, HandleObject bufobj,
                               uint32_t byteOffset, int32_t lengthInt) {
-    if (byteOffset % sizeof(NativeType) != 0) {
+    if (byteOffset % BYTES_PER_ELEMENT != 0) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
       return nullptr;  // invalid byteOffset
     }
 
     uint64_t lengthIndex = lengthInt >= 0 ? uint64_t(lengthInt) : UINT64_MAX;
     if (bufobj->is<ArrayBufferObjectMaybeShared>()) {
       HandleArrayBufferObjectMaybeShared buffer =
           bufobj.as<ArrayBufferObjectMaybeShared>();
       return fromBufferSameCompartment(cx, buffer, byteOffset, lengthIndex,
                                        nullptr);
     }
     return fromBufferWrapped(cx, bufobj, byteOffset, lengthIndex, nullptr);
   }
 
   static bool maybeCreateArrayBuffer(JSContext* cx, uint32_t count,
-                                     uint32_t unit,
                                      HandleObject nonDefaultProto,
                                      MutableHandle<ArrayBufferObject*> buffer) {
-    if (count >= INT32_MAX / unit) {
+    if (count >= INT32_MAX / BYTES_PER_ELEMENT) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_BAD_ARRAY_LENGTH);
       return false;
     }
-    uint32_t byteLength = count * unit;
+    uint32_t byteLength = count * BYTES_PER_ELEMENT;
 
     MOZ_ASSERT(byteLength < INT32_MAX);
-    static_assert(INLINE_BUFFER_LIMIT % sizeof(NativeType) == 0,
+    static_assert(INLINE_BUFFER_LIMIT % BYTES_PER_ELEMENT == 0,
                   "ArrayBuffer inline storage shouldn't waste any space");
 
     if (!nonDefaultProto && byteLength <= INLINE_BUFFER_LIMIT) {
       // The array's data can be inline, and the buffer created lazily.
       return true;
     }
 
     ArrayBufferObject* buf =
@@ -888,27 +859,26 @@ class TypedArrayObjectTemplate : public 
 
     if (nelements > UINT32_MAX) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_BAD_ARRAY_LENGTH);
       return nullptr;
     }
 
     Rooted<ArrayBufferObject*> buffer(cx);
-    if (!maybeCreateArrayBuffer(cx, uint32_t(nelements), BYTES_PER_ELEMENT,
-                                nullptr, &buffer)) {
+    if (!maybeCreateArrayBuffer(cx, uint32_t(nelements), nullptr, &buffer)) {
       return nullptr;
     }
 
     return makeInstance(cx, buffer, CreateSingleton::No, 0, uint32_t(nelements),
                         proto);
   }
 
   static bool AllocateArrayBuffer(JSContext* cx, HandleObject ctor,
-                                  uint32_t count, uint32_t unit,
+                                  uint32_t count,
                                   MutableHandle<ArrayBufferObject*> buffer);
 
   static JSObject* fromArray(JSContext* cx, HandleObject other,
                              HandleObject proto = nullptr);
 
   static JSObject* fromTypedArray(JSContext* cx, HandleObject other,
                                   bool isWrapped, HandleObject proto);
 
@@ -953,20 +923,20 @@ TypedArrayObject* js::TypedArrayCreateWi
 #undef CREATE_TYPED_ARRAY
     default:
       MOZ_CRASH("Unsupported TypedArray type");
   }
 }
 
 // ES2018 draft rev 2aea8f3e617b49df06414eb062ab44fad87661d3
 // 24.1.1.1 AllocateArrayBuffer ( constructor, byteLength )
-// byteLength = count * unit
+// byteLength = count * BYTES_PER_ELEMENT
 template <typename T>
 /* static */ bool TypedArrayObjectTemplate<T>::AllocateArrayBuffer(
-    JSContext* cx, HandleObject ctor, uint32_t count, uint32_t unit,
+    JSContext* cx, HandleObject ctor, uint32_t count,
     MutableHandle<ArrayBufferObject*> buffer) {
   // 24.1.1.1 step 1 (partially).
   RootedObject proto(cx);
 
   JSFunction* arrayBufferCtor =
       GlobalObject::getOrCreateArrayBufferConstructor(cx, cx->global());
   if (!arrayBufferCtor) {
     return false;
@@ -987,17 +957,17 @@ template <typename T>
 
     // Reset |proto| if it's the default %ArrayBufferPrototype%.
     if (proto == arrayBufferProto) {
       proto = nullptr;
     }
   }
 
   // 24.1.1.1 steps 1 (remaining part), 2-6.
-  if (!maybeCreateArrayBuffer(cx, count, unit, proto, buffer)) {
+  if (!maybeCreateArrayBuffer(cx, count, proto, buffer)) {
     return false;
   }
 
   return true;
 }
 
 static bool IsArrayBufferSpecies(JSContext* cx, JSFunction* species) {
   return IsSelfHostedFunctionWithName(species, cx->names().ArrayBufferSpecies);
@@ -1117,53 +1087,41 @@ template <typename T>
 
   // Step 7.
   if (srcArray->hasDetachedBuffer()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_TYPED_ARRAY_DETACHED);
     return nullptr;
   }
 
+  // Step 8 (skipped).
+
   // Step 9.
   uint32_t elementLength = srcArray->length();
 
-  // Steps 10-11.
-  Scalar::Type srcType = srcArray->type();
-
-  // Steps 12-13 (skipped).
+  // Steps 10-15 (skipped).
 
   // Steps 16-17.
   bool isShared = srcArray->isSharedMemory();
   SpeciesConstructorOverride override =
       isShared ? SpeciesConstructorOverride::ArrayBuffer
                : SpeciesConstructorOverride::None;
 
   RootedObject bufferCtor(
       cx, GetBufferSpeciesConstructor(cx, srcArray, isWrapped, override));
   if (!bufferCtor) {
     return nullptr;
   }
 
-  // Steps 8, 18-19.
+  // Steps 18-19.
   Rooted<ArrayBufferObject*> buffer(cx);
-  if (ArrayTypeID() == srcType) {
-    // Step 15.
-    uint32_t byteLength = srcArray->byteLength();
 
-    // Step 18.a.
-    // 24.1.1.4 CloneArrayBuffer(...), steps 1-3.
-    if (!AllocateArrayBuffer(cx, bufferCtor, byteLength, 1, &buffer)) {
-      return nullptr;
-    }
-  } else {
-    // Steps 14-15, 19.a.
-    if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, BYTES_PER_ELEMENT,
-                             &buffer)) {
-      return nullptr;
-    }
+  // Step 19.a or 18.a, 24.1.1.4 CloneArrayBuffer(...) steps 1-3.
+  if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, &buffer)) {
+    return nullptr;
   }
 
   // Step 19.b or 24.1.1.4 step 4.
   if (srcArray->hasDetachedBuffer()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_TYPED_ARRAY_DETACHED);
     return nullptr;
   }
@@ -1228,17 +1186,17 @@ template <typename T>
     // Step 6.a (We don't need to call IterableToList for the fast path).
     HandleArrayObject array = other.as<ArrayObject>();
 
     // Step 6.b.
     uint32_t len = array->getDenseInitializedLength();
 
     // Step 6.c.
     Rooted<ArrayBufferObject*> buffer(cx);
-    if (!maybeCreateArrayBuffer(cx, len, BYTES_PER_ELEMENT, nullptr, &buffer)) {
+    if (!maybeCreateArrayBuffer(cx, len, nullptr, &buffer)) {
       return nullptr;
     }
 
     Rooted<TypedArrayObject*> obj(
         cx, makeInstance(cx, buffer, CreateSingleton::No, 0, len, proto));
     if (!obj) {
       return nullptr;
     }
@@ -1303,17 +1261,17 @@ template <typename T>
   // Step 9.
   uint32_t len;
   if (!GetLengthProperty(cx, arrayLike, &len)) {
     return nullptr;
   }
 
   // Step 10.
   Rooted<ArrayBufferObject*> buffer(cx);
-  if (!maybeCreateArrayBuffer(cx, len, BYTES_PER_ELEMENT, nullptr, &buffer)) {
+  if (!maybeCreateArrayBuffer(cx, len, nullptr, &buffer)) {
     return nullptr;
   }
 
   Rooted<TypedArrayObject*> obj(
       cx, makeInstance(cx, buffer, CreateSingleton::No, 0, len, proto));
   if (!obj) {
     return nullptr;
   }
@@ -1653,30 +1611,30 @@ static const ClassSpec TypedArrayObjectS
     TypedArrayObject::protoAccessors,
     nullptr,
     ClassSpec::DontDefineConstructor};
 
 /* static */ const Class TypedArrayObject::sharedTypedArrayPrototypeClass = {
     "TypedArrayPrototype", JSCLASS_HAS_CACHED_PROTO(JSProto_TypedArray),
     JS_NULL_CLASS_OPS, &TypedArrayObjectSharedTypedArrayPrototypeClassSpec};
 
+namespace {
+
 // this default implementation is only valid for integer types
 // less than 32-bits in size.
 template <typename NativeType>
 Value TypedArrayObjectTemplate<NativeType>::getIndexValue(
     TypedArrayObject* tarray, uint32_t index) {
   static_assert(sizeof(NativeType) < 4,
                 "this method must only handle NativeType values that are "
                 "always exact int32_t values");
 
   return Int32Value(getIndex(tarray, index));
 }
 
-namespace {
-
 // and we need to specialize for 32-bit integers and floats
 template <>
 Value TypedArrayObjectTemplate<int32_t>::getIndexValue(TypedArrayObject* tarray,
                                                        uint32_t index) {
   return Int32Value(getIndex(tarray, index));
 }
 
 template <>
@@ -1815,86 +1773,16 @@ void TypedArrayObject::getElements(Value
 /***
  *** JS impl
  ***/
 
 /*
  * TypedArrayObject boilerplate
  */
 
-#define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name, NativeType)                \
-  JS_FRIEND_API JSObject* JS_New##Name##Array(JSContext* cx,                 \
-                                              uint32_t nelements) {          \
-    return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements);  \
-  }                                                                          \
-  JS_FRIEND_API JSObject* JS_New##Name##ArrayFromArray(JSContext* cx,        \
-                                                       HandleObject other) { \
-    return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other);       \
-  }                                                                          \
-  JS_FRIEND_API JSObject* JS_New##Name##ArrayWithBuffer(                     \
-      JSContext* cx, HandleObject arrayBuffer, uint32_t byteOffset,          \
-      int32_t length) {                                                      \
-    return TypedArrayObjectTemplate<NativeType>::fromBuffer(                 \
-        cx, arrayBuffer, byteOffset, length);                                \
-  }                                                                          \
-  JS_FRIEND_API bool JS_Is##Name##Array(JSObject* obj) {                     \
-    if (!(obj = CheckedUnwrap(obj))) return false;                           \
-    const Class* clasp = obj->getClass();                                    \
-    return clasp == TypedArrayObjectTemplate<NativeType>::instanceClass();   \
-  }                                                                          \
-  JS_FRIEND_API JSObject* js::Unwrap##Name##Array(JSObject* obj) {           \
-    obj = CheckedUnwrap(obj);                                                \
-    if (!obj) return nullptr;                                                \
-    const Class* clasp = obj->getClass();                                    \
-    if (clasp == TypedArrayObjectTemplate<NativeType>::instanceClass())      \
-      return obj;                                                            \
-    return nullptr;                                                          \
-  }                                                                          \
-  const js::Class* const js::detail::Name##ArrayClassPtr =                   \
-      &js::TypedArrayObject::classes                                         \
-          [TypedArrayObjectTemplate<NativeType>::ArrayTypeID()];
-
-IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t)
-IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t)
-IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8Clamped, uint8_clamped)
-IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int16, int16_t)
-IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint16, uint16_t)
-IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int32, int32_t)
-IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint32, uint32_t)
-IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float32, float)
-IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double)
-
-#define IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType) \
-  JS_FRIEND_API JSObject* JS_GetObjectAs##Name##Array(                         \
-      JSObject* obj, uint32_t* length, bool* isShared, ExternalType** data) {  \
-    if (!(obj = CheckedUnwrap(obj))) return nullptr;                           \
-                                                                               \
-    const Class* clasp = obj->getClass();                                      \
-    if (clasp != TypedArrayObjectTemplate<InternalType>::instanceClass())      \
-      return nullptr;                                                          \
-                                                                               \
-    TypedArrayObject* tarr = &obj->as<TypedArrayObject>();                     \
-    *length = tarr->length();                                                  \
-    *isShared = tarr->isSharedMemory();                                        \
-    *data = static_cast<ExternalType*>(tarr->dataPointerEither().unwrap(       \
-        /*safe - caller sees isShared flag*/));                                \
-                                                                               \
-    return obj;                                                                \
-  }
-
-IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int8, int8_t, int8_t)
-IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8, uint8_t, uint8_t)
-IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8Clamped, uint8_t, uint8_clamped)
-IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int16, int16_t, int16_t)
-IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint16, uint16_t, uint16_t)
-IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int32, int32_t, int32_t)
-IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t)
-IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float)
-IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double)
-
 static const ClassOps TypedArrayClassOps = {
     nullptr,                      /* addProperty */
     nullptr,                      /* delProperty */
     nullptr,                      /* enumerate   */
     nullptr,                      /* newEnumerate */
     nullptr,                      /* resolve     */
     nullptr,                      /* mayResolve  */
     TypedArrayObject::finalize,   /* finalize    */
@@ -1903,105 +1791,78 @@ static const ClassOps TypedArrayClassOps
     nullptr,                      /* construct   */
     ArrayBufferViewObject::trace, /* trace  */
 };
 
 static const ClassExtension TypedArrayClassExtension = {
     TypedArrayObject::objectMoved,
 };
 
-#define IMPL_TYPED_ARRAY_PROPERTIES(_type)                            \
-  {                                                                   \
-    JS_INT32_PS("BYTES_PER_ELEMENT", _type##Array::BYTES_PER_ELEMENT, \
-                JSPROP_READONLY | JSPROP_PERMANENT),                  \
-        JS_PS_END                                                     \
-  }
-
 static const JSPropertySpec
     static_prototype_properties[Scalar::MaxTypedArrayViewType][2] = {
-        IMPL_TYPED_ARRAY_PROPERTIES(Int8),
-        IMPL_TYPED_ARRAY_PROPERTIES(Uint8),
-        IMPL_TYPED_ARRAY_PROPERTIES(Int16),
-        IMPL_TYPED_ARRAY_PROPERTIES(Uint16),
-        IMPL_TYPED_ARRAY_PROPERTIES(Int32),
-        IMPL_TYPED_ARRAY_PROPERTIES(Uint32),
-        IMPL_TYPED_ARRAY_PROPERTIES(Float32),
-        IMPL_TYPED_ARRAY_PROPERTIES(Float64),
-        IMPL_TYPED_ARRAY_PROPERTIES(Uint8Clamped)};
+#define IMPL_TYPED_ARRAY_PROPERTIES(NativeType, Name)               \
+  {JS_INT32_PS("BYTES_PER_ELEMENT", Name##Array::BYTES_PER_ELEMENT, \
+               JSPROP_READONLY | JSPROP_PERMANENT),                 \
+   JS_PS_END},
 
-#define IMPL_TYPED_ARRAY_CLASS_SPEC(_type)                                   \
-  {                                                                          \
-    _type##Array::createConstructor, _type##Array::createPrototype, nullptr, \
-        static_prototype_properties[Scalar::Type::_type], nullptr,           \
-        static_prototype_properties[Scalar::Type::_type], nullptr,           \
-        JSProto_TypedArray                                                   \
-  }
+        JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_PROPERTIES)
+#undef IMPL_TYPED_ARRAY_PROPERTIES
+};
 
 static const ClassSpec
     TypedArrayObjectClassSpecs[Scalar::MaxTypedArrayViewType] = {
-        IMPL_TYPED_ARRAY_CLASS_SPEC(Int8),
-        IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8),
-        IMPL_TYPED_ARRAY_CLASS_SPEC(Int16),
-        IMPL_TYPED_ARRAY_CLASS_SPEC(Uint16),
-        IMPL_TYPED_ARRAY_CLASS_SPEC(Int32),
-        IMPL_TYPED_ARRAY_CLASS_SPEC(Uint32),
-        IMPL_TYPED_ARRAY_CLASS_SPEC(Float32),
-        IMPL_TYPED_ARRAY_CLASS_SPEC(Float64),
-        IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8Clamped)};
+#define IMPL_TYPED_ARRAY_CLASS_SPEC(NativeType, Name) \
+  {Name##Array::createConstructor,                    \
+   Name##Array::createPrototype,                      \
+   nullptr,                                           \
+   static_prototype_properties[Scalar::Type::Name],   \
+   nullptr,                                           \
+   static_prototype_properties[Scalar::Type::Name],   \
+   nullptr,                                           \
+   JSProto_TypedArray},
 
-#define IMPL_TYPED_ARRAY_CLASS(_type)                                        \
-  {                                                                          \
-#_type "Array",                                                          \
-        JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |       \
-            JSCLASS_HAS_PRIVATE |                                            \
-            JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array) |               \
-            JSCLASS_DELAY_METADATA_BUILDER | JSCLASS_SKIP_NURSERY_FINALIZE | \
-            JSCLASS_BACKGROUND_FINALIZE,                                     \
-        &TypedArrayClassOps,                                                 \
-        &TypedArrayObjectClassSpecs[Scalar::Type::_type],                    \
-                                    &TypedArrayClassExtension                \
-  }
+        JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS_SPEC)
+#undef IMPL_TYPED_ARRAY_CLASS_SPEC
+};
 
 const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = {
-    IMPL_TYPED_ARRAY_CLASS(Int8),        IMPL_TYPED_ARRAY_CLASS(Uint8),
-    IMPL_TYPED_ARRAY_CLASS(Int16),       IMPL_TYPED_ARRAY_CLASS(Uint16),
-    IMPL_TYPED_ARRAY_CLASS(Int32),       IMPL_TYPED_ARRAY_CLASS(Uint32),
-    IMPL_TYPED_ARRAY_CLASS(Float32),     IMPL_TYPED_ARRAY_CLASS(Float64),
-    IMPL_TYPED_ARRAY_CLASS(Uint8Clamped)};
+#define IMPL_TYPED_ARRAY_CLASS(NativeType, Name)                               \
+  {#Name "Array",                                                              \
+   JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |              \
+       JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array) | \
+       JSCLASS_DELAY_METADATA_BUILDER | JSCLASS_SKIP_NURSERY_FINALIZE |        \
+       JSCLASS_BACKGROUND_FINALIZE,                                            \
+   &TypedArrayClassOps, &TypedArrayObjectClassSpecs[Scalar::Type::Name],       \
+   &TypedArrayClassExtension},
+
+    JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS)
+#undef IMPL_TYPED_ARRAY_CLASS
+};
 
 // The various typed array prototypes are supposed to 1) be normal objects,
 // 2) stringify to "[object <name of constructor>]", and 3) (Gecko-specific)
 // be xrayable.  The first and second requirements mandate (in the absence of
 // @@toStringTag) a custom class.  The third requirement mandates that each
 // prototype's class have the relevant typed array's cached JSProtoKey in them.
 // Thus we need one class with cached prototype per kind of typed array, with a
 // delegated ClassSpec.
-#define IMPL_TYPED_ARRAY_PROTO_CLASS(_type)                                    \
-  {                                                                            \
-/*                                                                             \
- * Actually ({}).toString.call(Uint8Array.prototype) should throw, because     \
- * Uint8Array.prototype lacks the the typed array internal slots.  (Same as    \
- * with %TypedArray%.prototype.)  It's not clear this is desirable (see        \
- * above), but it's what we've always done, so keep doing it till we           \
- * implement @@toStringTag or ES6 changes.                                     \
- */                                                                            \
-#_type "ArrayPrototype", JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array), \
-        JS_NULL_CLASS_OPS, &TypedArrayObjectClassSpecs[Scalar::Type::_type]    \
-  }
+//
+// Actually ({}).toString.call(Uint8Array.prototype) should throw, because
+// Uint8Array.prototype lacks the the typed array internal slots.  (Same as
+// with %TypedArray%.prototype.)  It's not clear this is desirable (see
+// above), but it's what we've always done, so keep doing it till we
+// implement @@toStringTag or ES6 changes.
+const Class TypedArrayObject::protoClasses[Scalar::MaxTypedArrayViewType] = {
+#define IMPL_TYPED_ARRAY_PROTO_CLASS(NativeType, Name)                      \
+  {#Name "ArrayPrototype", JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array), \
+   JS_NULL_CLASS_OPS, &TypedArrayObjectClassSpecs[Scalar::Type::Name]},
 
-const Class TypedArrayObject::protoClasses[Scalar::MaxTypedArrayViewType] = {
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Int8),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Int16),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Int32),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Float32),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Float64),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Clamped)};
+    JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_PROTO_CLASS)
+#undef IMPL_TYPED_ARRAY_PROTO_CLASS
+};
 
 /* static */ bool TypedArrayObject::isOriginalLengthGetter(Native native) {
   return native == TypedArray_lengthGetter;
 }
 
 bool js::IsTypedArrayConstructor(HandleValue v, uint32_t type) {
   switch (type) {
     case Scalar::Int8:
@@ -2185,16 +2046,96 @@ bool js::DefineTypedArrayElement(JSConte
   }
 
   // Step xii.
   return result.succeed();
 }
 
 /* JS Friend API */
 
+template <typename NativeType>
+struct ExternalTypeOf {
+  using Type = NativeType;
+};
+
+template <>
+struct ExternalTypeOf<uint8_clamped> {
+  using Type = uint8_t;
+};
+
+#define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(NativeType, Name)                \
+  JS_FRIEND_API JSObject* JS_New##Name##Array(JSContext* cx,                 \
+                                              uint32_t nelements) {          \
+    return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements);  \
+  }                                                                          \
+                                                                             \
+  JS_FRIEND_API JSObject* JS_New##Name##ArrayFromArray(JSContext* cx,        \
+                                                       HandleObject other) { \
+    return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other);       \
+  }                                                                          \
+                                                                             \
+  JS_FRIEND_API JSObject* JS_New##Name##ArrayWithBuffer(                     \
+      JSContext* cx, HandleObject arrayBuffer, uint32_t byteOffset,          \
+      int32_t length) {                                                      \
+    return TypedArrayObjectTemplate<NativeType>::fromBuffer(                 \
+        cx, arrayBuffer, byteOffset, length);                                \
+  }                                                                          \
+                                                                             \
+  JS_FRIEND_API JSObject* js::Unwrap##Name##Array(JSObject* obj) {           \
+    obj = CheckedUnwrap(obj);                                                \
+    if (!obj) {                                                              \
+      return nullptr;                                                        \
+    }                                                                        \
+    const Class* clasp = obj->getClass();                                    \
+    if (clasp != TypedArrayObjectTemplate<NativeType>::instanceClass()) {    \
+      return nullptr;                                                        \
+    }                                                                        \
+    return obj;                                                              \
+  }                                                                          \
+                                                                             \
+  JS_FRIEND_API bool JS_Is##Name##Array(JSObject* obj) {                     \
+    return js::Unwrap##Name##Array(obj) != nullptr;                          \
+  }                                                                          \
+                                                                             \
+  const js::Class* const js::detail::Name##ArrayClassPtr =                   \
+      &js::TypedArrayObject::classes                                         \
+          [TypedArrayObjectTemplate<NativeType>::ArrayTypeID()];             \
+                                                                             \
+  JS_FRIEND_API JSObject* JS_GetObjectAs##Name##Array(                       \
+      JSObject* obj, uint32_t* length, bool* isShared,                       \
+      ExternalTypeOf<NativeType>::Type** data) {                             \
+    obj = js::Unwrap##Name##Array(obj);                                      \
+    if (!obj) {                                                              \
+      return nullptr;                                                        \
+    }                                                                        \
+    TypedArrayObject* tarr = &obj->as<TypedArrayObject>();                   \
+    *length = tarr->length();                                                \
+    *isShared = tarr->isSharedMemory();                                      \
+    *data = static_cast<ExternalTypeOf<NativeType>::Type*>(                  \
+        tarr->dataPointerEither().unwrap(                                    \
+            /*safe - caller sees isShared flag*/));                          \
+    return obj;                                                              \
+  }                                                                          \
+                                                                             \
+  JS_FRIEND_API ExternalTypeOf<NativeType>::Type* JS_Get##Name##ArrayData(   \
+      JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&) {     \
+    obj = CheckedUnwrap(obj);                                                \
+    if (!obj) {                                                              \
+      return nullptr;                                                        \
+    }                                                                        \
+    TypedArrayObject* tarr = &obj->as<TypedArrayObject>();                   \
+    MOZ_ASSERT(tarr->type() == TypeIDOfType<NativeType>::id);                \
+    *isSharedMemory = tarr->isSharedMemory();                                \
+    return static_cast<ExternalTypeOf<NativeType>::Type*>(                   \
+        tarr->dataPointerEither().unwrap(/*safe - caller sees isShared*/));  \
+  }
+
+JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS)
+#undef IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS
+
 JS_FRIEND_API bool JS_IsTypedArrayObject(JSObject* obj) {
   return obj->canUnwrapAs<TypedArrayObject>();
 }
 
 JS_FRIEND_API uint32_t JS_GetTypedArrayLength(JSObject* obj) {
   obj = CheckedUnwrap(obj);
   if (!obj) {
     return 0;
@@ -2235,129 +2176,8 @@ JS_FRIEND_API js::Scalar::Type JS_GetArr
   if (obj->is<TypedArrayObject>()) {
     return obj->as<TypedArrayObject>().type();
   }
   if (obj->is<DataViewObject>()) {
     return Scalar::MaxTypedArrayViewType;
   }
   MOZ_CRASH("invalid ArrayBufferView type");
 }
-
-JS_FRIEND_API int8_t* JS_GetInt8ArrayData(JSObject* obj, bool* isSharedMemory,
-                                          const JS::AutoRequireNoGC&) {
-  obj = CheckedUnwrap(obj);
-  if (!obj) {
-    return nullptr;
-  }
-  TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
-  MOZ_ASSERT((int32_t)tarr->type() == Scalar::Int8);
-  *isSharedMemory = tarr->isSharedMemory();
-  return static_cast<int8_t*>(
-      tarr->dataPointerEither().unwrap(/*safe - caller sees isShared*/));
-}
-
-JS_FRIEND_API uint8_t* JS_GetUint8ArrayData(JSObject* obj, bool* isSharedMemory,
-                                            const JS::AutoRequireNoGC&) {
-  obj = CheckedUnwrap(obj);
-  if (!obj) {
-    return nullptr;
-  }
-  TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
-  MOZ_ASSERT((int32_t)tarr->type() == Scalar::Uint8);
-  *isSharedMemory = tarr->isSharedMemory();
-  return static_cast<uint8_t*>(
-      tarr->dataPointerEither().unwrap(/*safe - caller sees isSharedMemory*/));
-}
-
-JS_FRIEND_API uint8_t* JS_GetUint8ClampedArrayData(JSObject* obj,
-                                                   bool* isSharedMemory,
-                                                   const JS::AutoRequireNoGC&) {
-  obj = CheckedUnwrap(obj);
-  if (!obj) {
-    return nullptr;
-  }
-  TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
-  MOZ_ASSERT((int32_t)tarr->type() == Scalar::Uint8Clamped);
-  *isSharedMemory = tarr->isSharedMemory();
-  return static_cast<uint8_t*>(
-      tarr->dataPointerEither().unwrap(/*safe - caller sees isSharedMemory*/));
-}
-
-JS_FRIEND_API int16_t* JS_GetInt16ArrayData(JSObject* obj, bool* isSharedMemory,
-                                            const JS::AutoRequireNoGC&) {
-  obj = CheckedUnwrap(obj);
-  if (!obj) {
-    return nullptr;
-  }
-  TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
-  MOZ_ASSERT((int32_t)tarr->type() == Scalar::Int16);
-  *isSharedMemory = tarr->isSharedMemory();
-  return static_cast<int16_t*>(
-      tarr->dataPointerEither().unwrap(/*safe - caller sees isSharedMemory*/));
-}
-
-JS_FRIEND_API uint16_t* JS_GetUint16ArrayData(JSObject* obj,
-                                              bool* isSharedMemory,
-                                              const JS::AutoRequireNoGC&) {
-  obj = CheckedUnwrap(obj);
-  if (!obj) {
-    return nullptr;
-  }
-  TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
-  MOZ_ASSERT((int32_t)tarr->type() == Scalar::Uint16);
-  *isSharedMemory = tarr->isSharedMemory();
-  return static_cast<uint16_t*>(
-      tarr->dataPointerEither().unwrap(/*safe - caller sees isSharedMemory*/));
-}
-
-JS_FRIEND_API int32_t* JS_GetInt32ArrayData(JSObject* obj, bool* isSharedMemory,
-                                            const JS::AutoRequireNoGC&) {
-  obj = CheckedUnwrap(obj);
-  if (!obj) {
-    return nullptr;
-  }
-  TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
-  MOZ_ASSERT((int32_t)tarr->type() == Scalar::Int32);
-  *isSharedMemory = tarr->isSharedMemory();
-  return static_cast<int32_t*>(
-      tarr->dataPointerEither().unwrap(/*safe - caller sees isSharedMemory*/));
-}
-
-JS_FRIEND_API uint32_t* JS_GetUint32ArrayData(JSObject* obj,
-                                              bool* isSharedMemory,
-                                              const JS::AutoRequireNoGC&) {
-  obj = CheckedUnwrap(obj);
-  if (!obj) {
-    return nullptr;
-  }
-  TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
-  MOZ_ASSERT((int32_t)tarr->type() == Scalar::Uint32);
-  *isSharedMemory = tarr->isSharedMemory();
-  return static_cast<uint32_t*>(
-      tarr->dataPointerEither().unwrap(/*safe - caller sees isSharedMemory*/));
-}
-
-JS_FRIEND_API float* JS_GetFloat32ArrayData(JSObject* obj, bool* isSharedMemory,
-                                            const JS::AutoRequireNoGC&) {
-  obj = CheckedUnwrap(obj);
-  if (!obj) {
-    return nullptr;
-  }
-  TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
-  MOZ_ASSERT((int32_t)tarr->type() == Scalar::Float32);
-  *isSharedMemory = tarr->isSharedMemory();
-  return static_cast<float*>(
-      tarr->dataPointerEither().unwrap(/*safe - caller sees isSharedMemory*/));
-}
-
-JS_FRIEND_API double* JS_GetFloat64ArrayData(JSObject* obj,
-                                             bool* isSharedMemory,
-                                             const JS::AutoRequireNoGC&) {
-  obj = CheckedUnwrap(obj);
-  if (!obj) {
-    return nullptr;
-  }
-  TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
-  MOZ_ASSERT((int32_t)tarr->type() == Scalar::Float64);
-  *isSharedMemory = tarr->isSharedMemory();
-  return static_cast<double*>(
-      tarr->dataPointerEither().unwrap(/*safe - caller sees isSharedMemory*/));
-}
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -12,36 +12,45 @@
 
 #include "gc/Barrier.h"
 #include "js/Class.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/ArrayBufferViewObject.h"
 #include "vm/JSObject.h"
 #include "vm/SharedArrayObject.h"
 
-#define JS_FOR_EACH_TYPED_ARRAY(macro)                                      \
-  macro(int8_t, Int8) macro(uint8_t, Uint8) macro(int16_t, Int16)           \
-      macro(uint16_t, Uint16) macro(int32_t, Int32) macro(uint32_t, Uint32) \
-          macro(float, Float32) macro(double, Float64)                      \
-              macro(uint8_clamped, Uint8Clamped)
+#define JS_FOR_EACH_TYPED_ARRAY(MACRO) \
+  MACRO(int8_t, Int8)                  \
+  MACRO(uint8_t, Uint8)                \
+  MACRO(int16_t, Int16)                \
+  MACRO(uint16_t, Uint16)              \
+  MACRO(int32_t, Int32)                \
+  MACRO(uint32_t, Uint32)              \
+  MACRO(float, Float32)                \
+  MACRO(double, Float64)               \
+  MACRO(uint8_clamped, Uint8Clamped)
 
 namespace js {
 
 /*
  * TypedArrayObject
  *
  * The non-templated base class for the specific typed implementations.
  * This class holds all the member variables that are used by
  * the subclasses.
  */
 
 class TypedArrayObject : public ArrayBufferViewObject {
  public:
-  static int lengthOffset();
-  static int dataOffset();
+  static constexpr int lengthOffset() {
+    return NativeObject::getFixedSlotOffset(LENGTH_SLOT);
+  }
+  static constexpr int dataOffset() {
+    return NativeObject::getPrivateDataOffset(DATA_SLOT);
+  }
 
   static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT,
                 "bad inlined constant in jsfriendapi.h");
 
   static bool sameBuffer(Handle<TypedArrayObject*> a,
                          Handle<TypedArrayObject*> b) {
     // Inline buffers.
     if (!a->hasBuffer() || !b->hasBuffer()) {
@@ -65,21 +74,21 @@ class TypedArrayObject : public ArrayBuf
     return &classes[type];
   }
 
   static const Class* protoClassForType(Scalar::Type type) {
     MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType);
     return &protoClasses[type];
   }
 
-  static const size_t FIXED_DATA_START = DATA_SLOT + 1;
+  static constexpr size_t FIXED_DATA_START = DATA_SLOT + 1;
 
   // For typed arrays which can store their data inline, the array buffer
   // object is created lazily.
-  static const uint32_t INLINE_BUFFER_LIMIT =
+  static constexpr uint32_t INLINE_BUFFER_LIMIT =
       (NativeObject::MAX_FIXED_SLOTS - FIXED_DATA_START) * sizeof(Value);
 
   static inline gc::AllocKind AllocKindForLazyBuffer(size_t nbytes);
 
   inline Scalar::Type type() const;
   inline size_t bytesPerElement() const;
 
   static Value byteOffsetValue(const TypedArrayObject* tarr) {
@@ -129,17 +138,17 @@ class TypedArrayObject : public ArrayBuf
   static bool GetTemplateObjectForNative(JSContext* cx, Native native,
                                          uint32_t len, MutableHandleObject res);
 
   /*
    * Byte length above which created typed arrays will have singleton types
    * regardless of the context in which they are created. This only applies to
    * typed arrays created with an existing ArrayBuffer.
    */
-  static const uint32_t SINGLETON_BYTE_LENGTH = 1024 * 1024 * 10;
+  static constexpr uint32_t SINGLETON_BYTE_LENGTH = 1024 * 1024 * 10;
 
   static bool isOriginalLengthGetter(Native native);
 
   static void finalize(FreeOp* fop, JSObject* obj);
   static size_t objectMoved(JSObject* obj, JSObject* old);
 
   /* Initialization bits */
 
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -14,16 +14,17 @@
 #include <algorithm>  // std::transform
 #include <string.h>
 #include <type_traits>  // std::is_same
 #include <utility>      // std::move
 
 #include "jsapi.h"
 #include "jsutil.h"
 
+#include "js/BuildId.h"  // JS::BuildIdCharVector
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSScript.h"
 #include "vm/TraceLogging.h"
 
 using namespace js;
 
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -19,26 +19,28 @@
 #include "wasm/AsmJS.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Compression.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Unused.h"
+#include "mozilla/Variant.h"
 
 #include <new>
 
 #include "jsmath.h"
 #include "jsutil.h"
 
 #include "builtin/String.h"
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "gc/Policy.h"
+#include "js/BuildId.h"  // JS::BuildIdCharVector
 #include "js/MemoryMetrics.h"
 #include "js/Printf.h"
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/ErrorReporting.h"
@@ -62,17 +64,19 @@ using namespace js::frontend;
 using namespace js::jit;
 using namespace js::wasm;
 
 using JS::AsmJSOption;
 using JS::AutoStableStringChars;
 using JS::GenericNaN;
 using JS::SourceOwnership;
 using JS::SourceText;
+using mozilla::Abs;
 using mozilla::ArrayEqual;
+using mozilla::AsVariant;
 using mozilla::CeilingLog2;
 using mozilla::HashGeneric;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 using mozilla::IsPositiveZero;
 using mozilla::IsPowerOfTwo;
 using mozilla::PodZero;
 using mozilla::PositiveInfinity;
@@ -197,16 +201,18 @@ class AsmJSGlobal {
       struct {
         ConstantKind kind_;
         double value_;
       } constant;
     } u;
   } pod;
   CacheableChars field_;
 
+  friend class ModuleValidatorShared;
+  template <typename Unit>
   friend class ModuleValidator;
 
  public:
   AsmJSGlobal() = default;
   AsmJSGlobal(Which which, UniqueChars field) {
     mozilla::PodZero(&pod);  // zero padding for Valgrind
     pod.which_ = which;
     field_ = std::move(field);
@@ -638,48 +644,52 @@ static inline ParseNode* SkipEmptyStatem
   }
   return pn;
 }
 
 static inline ParseNode* NextNonEmptyStatement(ParseNode* pn) {
   return SkipEmptyStatements(pn->pn_next);
 }
 
-static bool GetToken(AsmJSParser& parser, TokenKind* tkp) {
+template <typename Unit>
+static bool GetToken(AsmJSParser<Unit>& parser, TokenKind* tkp) {
   auto& ts = parser.tokenStream;
   TokenKind tk;
   while (true) {
     if (!ts.getToken(&tk, TokenStreamShared::Operand)) {
       return false;
     }
     if (tk != TokenKind::Semi) {
       break;
     }
   }
   *tkp = tk;
   return true;
 }
 
-static bool PeekToken(AsmJSParser& parser, TokenKind* tkp) {
+template <typename Unit>
+static bool PeekToken(AsmJSParser<Unit>& parser, TokenKind* tkp) {
   auto& ts = parser.tokenStream;
   TokenKind tk;
   while (true) {
     if (!ts.peekToken(&tk, TokenStream::Operand)) {
       return false;
     }
     if (tk != TokenKind::Semi) {
       break;
     }
     ts.consumeKnownToken(TokenKind::Semi, TokenStreamShared::Operand);
   }
   *tkp = tk;
   return true;
 }
 
-static bool ParseVarOrConstStatement(AsmJSParser& parser, ParseNode** var) {
+template <typename Unit>
+static bool ParseVarOrConstStatement(AsmJSParser<Unit>& parser,
+                                     ParseNode** var) {
   TokenKind tk;
   if (!PeekToken(parser, &tk)) {
     return false;
   }
   if (tk != TokenKind::Var && tk != TokenKind::Const) {
     *var = nullptr;
     return true;
   }
@@ -1031,31 +1041,17 @@ class Type {
         return "void";
     }
     MOZ_CRASH("Invalid Type");
   }
 };
 
 static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 
-// The ModuleValidator encapsulates the entire validation of an asm.js module.
-// Its lifetime goes from the validation of the top components of an asm.js
-// module (all the globals), the emission of bytecode for all the functions in
-// the module and the validation of function's pointer tables. It also finishes
-// the compilation of all the module's stubs.
-//
-// Rooting note: ModuleValidator is a stack class that contains unrooted
-// PropertyName (JSAtom) pointers.  This is safe because it cannot be
-// constructed without a TokenStream reference.  TokenStream is itself a stack
-// class that cannot be constructed without an AutoKeepAtoms being live on the
-// stack, which prevents collection of atoms.
-//
-// ModuleValidator is marked as rooted in the rooting analysis.  Don't add
-// non-JSAtom pointers, or this will break!
-class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator {
+class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidatorShared {
  public:
   class Func {
     PropertyName* name_;
     uint32_t sigIndex_;
     uint32_t firstUse_;
     uint32_t funcDefIndex_;
 
     bool defined_;
@@ -1113,18 +1109,18 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
       return bytes_;
     }
     Uint32Vector& callSiteLineNums() {
       MOZ_ASSERT(defined_);
       return callSiteLineNums_;
     }
   };
 
-  typedef Vector<const Func*> ConstFuncVector;
-  typedef Vector<Func> FuncVector;
+  using ConstFuncVector = Vector<const Func*>;
+  using FuncVector = Vector<Func>;
 
   class Table {
     uint32_t sigIndex_;
     PropertyName* name_;
     uint32_t firstUse_;
     uint32_t mask_;
     bool defined_;
 
@@ -1145,17 +1141,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
     unsigned mask() const { return mask_; }
     bool defined() const { return defined_; }
     void define() {
       MOZ_ASSERT(!defined_);
       defined_ = true;
     }
   };
 
-  typedef Vector<Table*> TableVector;
+  using TableVector = Vector<Table*>;
 
   class Global {
    public:
     enum Which {
       Variable,
       ConstantLiteral,
       ConstantImport,
       Function,
@@ -1202,16 +1198,18 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
       // |varOrConst|, through |varOrConst.literalValue_|, has a
       // non-trivial constructor and therefore MUST be placement-new'd
       // into existence.
       MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
       U() : funcDefIndex_(0) {}
       MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
     } u;
 
+    friend class ModuleValidatorShared;
+    template <typename Unit>
     friend class ModuleValidator;
     friend class js::LifoAlloc;
 
     explicit Global(Which which) : which_(which) {}
 
    public:
     Which which() const { return which_; }
     Type varOrConstType() const {
@@ -1274,29 +1272,29 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
 
   struct ArrayView {
     ArrayView(PropertyName* name, Scalar::Type type) : name(name), type(type) {}
 
     PropertyName* name;
     Scalar::Type type;
   };
 
- private:
+ protected:
   class HashableSig {
     uint32_t sigIndex_;
     const TypeDefVector& types_;
 
    public:
     HashableSig(uint32_t sigIndex, const TypeDefVector& types)
         : sigIndex_(sigIndex), types_(types) {}
     uint32_t sigIndex() const { return sigIndex_; }
     const FuncType& funcType() const { return types_[sigIndex_].funcType(); }
 
     // Implement HashPolicy:
-    typedef const FuncType& Lookup;
+    using Lookup = const FuncType&;
     static HashNumber hash(Lookup l) { return l.hash(); }
     static bool match(HashableSig lhs, Lookup rhs) {
       return lhs.funcType() == rhs;
     }
   };
 
   class NamedSig : public HashableSig {
     PropertyName* name_;
@@ -1316,29 +1314,29 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
     static HashNumber hash(Lookup l) {
       return HashGeneric(l.name, l.funcType.hash());
     }
     static bool match(NamedSig lhs, Lookup rhs) {
       return lhs.name() == rhs.name && lhs.funcType() == rhs.funcType;
     }
   };
 
-  typedef HashSet<HashableSig, HashableSig> SigSet;
-  typedef HashMap<NamedSig, uint32_t, NamedSig> FuncImportMap;
-  typedef HashMap<PropertyName*, Global*> GlobalMap;
-  typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
-  typedef Vector<ArrayView> ArrayViewVector;
-
+  using SigSet = HashSet<HashableSig, HashableSig>;
+  using FuncImportMap = HashMap<NamedSig, uint32_t, NamedSig>;
+  using GlobalMap = HashMap<PropertyName*, Global*>;
+  using MathNameMap = HashMap<PropertyName*, MathBuiltin>;
+  using ArrayViewVector = Vector<ArrayView>;
+
+protected:
   JSContext* cx_;
-  AsmJSParser& parser_;
   CodeNode* moduleFunctionNode_;
   PropertyName* moduleFunctionName_;
-  PropertyName* globalArgumentName_;
-  PropertyName* importArgumentName_;
-  PropertyName* bufferArgumentName_;
+  PropertyName* globalArgumentName_ = nullptr;
+  PropertyName* importArgumentName_ = nullptr;
+  PropertyName* bufferArgumentName_ = nullptr;
   MathNameMap standardLibraryMathNames_;
   RootedFunction dummyFunction_;
 
   // Validation-internal state:
   LifoAlloc validationLifo_;
   FuncVector funcDefs_;
   TableVector tables_;
   GlobalMap globalMap_;
@@ -1347,195 +1345,130 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
   ArrayViewVector arrayViews_;
 
   // State used to build the AsmJSModule in finish():
   CompilerEnvironment compilerEnv_;
   ModuleEnvironment env_;
   MutableAsmJSMetadata asmJSMetadata_;
 
   // Error reporting:
-  UniqueChars errorString_;
-  uint32_t errorOffset_;
-  bool errorOverRecursed_;
-
-  // Helpers:
-  bool addStandardLibraryMathName(const char* name,
-                                  AsmJSMathBuiltinFunction func) {
-    JSAtom* atom = Atomize(cx_, name, strlen(name));
-    if (!atom) {
-      return false;
-    }
-    MathBuiltin builtin(func);
-    return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
-  }
-  bool addStandardLibraryMathName(const char* name, double cst) {
-    JSAtom* atom = Atomize(cx_, name, strlen(name));
-    if (!atom) {
-      return false;
-    }
-    MathBuiltin builtin(cst);
-    return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
-  }
-  bool newSig(FuncType&& sig, uint32_t* sigIndex) {
-    if (env_.types.length() >= MaxTypes) {
-      return failCurrentOffset("too many signatures");
-    }
-
-    *sigIndex = env_.types.length();
-    return env_.types.append(std::move(sig));
-  }
-  bool declareSig(FuncType&& sig, uint32_t* sigIndex) {
-    SigSet::AddPtr p = sigSet_.lookupForAdd(sig);
-    if (p) {
-      *sigIndex = p->sigIndex();
-      MOZ_ASSERT(env_.types[*sigIndex].funcType() == sig);
-      return true;
-    }
-
-    return newSig(std::move(sig), sigIndex) &&
-           sigSet_.add(p, HashableSig(*sigIndex, env_.types));
-  }
-
- public:
-  ModuleValidator(JSContext* cx, AsmJSParser& parser,
-                  CodeNode* moduleFunctionNode)
+  UniqueChars errorString_ = nullptr;
+  uint32_t errorOffset_ = UINT32_MAX;
+  bool errorOverRecursed_ = false;
+
+ protected:
+  ModuleValidatorShared(JSContext* cx, CodeNode* moduleFunctionNode)
       : cx_(cx),
-        parser_(parser),
         moduleFunctionNode_(moduleFunctionNode),
         moduleFunctionName_(FunctionName(moduleFunctionNode)),
-        globalArgumentName_(nullptr),
-        importArgumentName_(nullptr),
-        bufferArgumentName_(nullptr),
         standardLibraryMathNames_(cx),
         dummyFunction_(cx),
         validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
         funcDefs_(cx),
         tables_(cx),
         globalMap_(cx),
         sigSet_(cx),
         funcImportMap_(cx),
         arrayViews_(cx),
         compilerEnv_(CompileMode::Once, Tier::Optimized, OptimizedBackend::Ion,
                      DebugEnabled::False, HasGcTypes::False),
         env_(HasGcTypes::False, &compilerEnv_, Shareable::False,
-             ModuleKind::AsmJS),
-        errorString_(nullptr),
-        errorOffset_(UINT32_MAX),
-        errorOverRecursed_(false) {
+             ModuleKind::AsmJS) {
     compilerEnv_.computeParameters(HasGcTypes::False);
     env_.minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
   }
 
-  ~ModuleValidator() {
-    if (errorString_) {
-      MOZ_ASSERT(errorOffset_ != UINT32_MAX);
-      typeFailure(errorOffset_, errorString_.get());
-    }
-    if (errorOverRecursed_) {
-      ReportOverRecursed(cx_);
-    }
-  }
-
- private:
-  void typeFailure(uint32_t offset, ...) {
-    va_list args;
-    va_start(args, offset);
-
-    auto& ts = tokenStream();
-    ErrorMetadata metadata;
-    if (ts.computeErrorMetadata(&metadata, offset)) {
-      if (ts.anyCharsAccess().options().throwOnAsmJSValidationFailureOption) {
-        ReportCompileError(cx_, std::move(metadata), nullptr, JSREPORT_ERROR,
-                           JSMSG_USE_ASM_TYPE_FAIL, args);
-      } else {
-        // asm.js type failure is indicated by calling one of the fail*
-        // functions below.  These functions always return false to
-        // halt asm.js parsing.  Whether normal parsing is attempted as
-        // fallback, depends whether an exception is also set.
-        //
-        // If warning succeeds, no exception is set.  If warning fails,
-        // an exception is set and execution will halt.  Thus it's safe
-        // and correct to ignore the return value here.
-        Unused << ts.anyCharsAccess().compileWarning(
-            std::move(metadata), nullptr, JSREPORT_WARNING,
-            JSMSG_USE_ASM_TYPE_FAIL, args);
+ protected:
+  MOZ_MUST_USE bool addStandardLibraryMathInfo() {
+    static constexpr struct {
+      const char* name;
+      AsmJSMathBuiltinFunction func;
+    } functions[] = {
+        {"sin", AsmJSMathBuiltin_sin},       {"cos", AsmJSMathBuiltin_cos},
+        {"tan", AsmJSMathBuiltin_tan},       {"asin", AsmJSMathBuiltin_asin},
+        {"acos", AsmJSMathBuiltin_acos},     {"atan", AsmJSMathBuiltin_atan},
+        {"ceil", AsmJSMathBuiltin_ceil},     {"floor", AsmJSMathBuiltin_floor},
+        {"exp", AsmJSMathBuiltin_exp},       {"log", AsmJSMathBuiltin_log},
+        {"pow", AsmJSMathBuiltin_pow},       {"sqrt", AsmJSMathBuiltin_sqrt},
+        {"abs", AsmJSMathBuiltin_abs},       {"atan2", AsmJSMathBuiltin_atan2},
+        {"imul", AsmJSMathBuiltin_imul},     {"clz32", AsmJSMathBuiltin_clz32},
+        {"fround", AsmJSMathBuiltin_fround}, {"min", AsmJSMathBuiltin_min},
+        {"max", AsmJSMathBuiltin_max},
+    };
+
+    auto AddMathFunction = [this](const char* name,