Merge mozilla-central to autoland. a=merge CLOSED TREE
authorOana Pop Rus <opoprus@mozilla.com>
Tue, 08 Jan 2019 12:27:21 +0200
changeset 509978 eb1db8a519dfc5c5c2347c912583ebfb26862b8c
parent 509977 b5ae2787288ee433c71da4afcb64613d010e6307 (current diff)
parent 509965 60aa2498320da118cb73c2375c201ecf4f47567b (diff)
child 509979 922a9d4b4b49b29db15340b14fa42741a9a0ad25
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
dom/svg/nsSVGBoolean.cpp
dom/svg/nsSVGBoolean.h
dom/svg/nsSVGInteger.cpp
dom/svg/nsSVGInteger.h
--- 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,
+                                  AsmJSMathBuiltinFunction func) {
+      JSAtom* atom = Atomize(cx_, name, strlen(name));
+      if (!atom) {
+        return false;
+      }
+      MathBuiltin builtin(func);
+      return this->standardLibraryMathNames_.putNew(atom->asPropertyName(),
+                                                    builtin);
+    };
+
+    for (const auto& info : functions) {
+      if (!AddMathFunction(info.name, info.func)) {
+        return false;
       }
     }
 
-    va_end(args);
-  }
-
- public:
-  bool init() {
-    asmJSMetadata_ = cx_->new_<AsmJSMetadata>();
-    if (!asmJSMetadata_) {
-      return false;
-    }
-
-    asmJSMetadata_->toStringStart =
-        moduleFunctionNode_->funbox()->toStringStart;
-    asmJSMetadata_->srcStart = moduleFunctionNode_->body()->pn_pos.begin;
-    asmJSMetadata_->strict =
-        parser_.pc->sc()->strict() && !parser_.pc->sc()->hasExplicitUseStrict();
-    asmJSMetadata_->scriptSource.reset(parser_.ss);
-
-    if (!addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) ||
-        !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) ||
-        !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) ||
-        !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) ||
-        !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) ||
-        !addStandardLibraryMathName("atan", AsmJSMathBuiltin_atan) ||
-        !addStandardLibraryMathName("ceil", AsmJSMathBuiltin_ceil) ||
-        !addStandardLibraryMathName("floor", AsmJSMathBuiltin_floor) ||
-        !addStandardLibraryMathName("exp", AsmJSMathBuiltin_exp) ||
-        !addStandardLibraryMathName("log", AsmJSMathBuiltin_log) ||
-        !addStandardLibraryMathName("pow", AsmJSMathBuiltin_pow) ||
-        !addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) ||
-        !addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) ||
-        !addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) ||
-        !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) ||