Merge m-i to m-c
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 02 Feb 2014 09:16:37 -0800
changeset 182549 5f88d54c28e03d8cab01968ff2d54d85ab521ef1
parent 182479 2918a9e625b4afb867d4afc860eee18932514232 (current diff)
parent 182548 fac849dd7be970a24d48cb0d4a703e964d39470b (diff)
child 182560 83a3ef9b21449e2c97bc39d226a28d16b28a6e4a
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.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 m-i to m-c
js/src/jit-test/tests/auto-regress/bug770954.js
security/manager/ssl/src/PSMContentDownloader.h
--- a/build/unix/build-gcc/build-gcc.sh
+++ b/build/unix/build-gcc/build-gcc.sh
@@ -1,13 +1,15 @@
 #!/bin/bash
 
 gcc_version=4.7.3
 binutils_version=2.23.1
-gcc_bt_patch=$(readlink -f $(dirname $0))/gcc-bt.patch
+this_path=$(readlink -f $(dirname $0))
+gcc_bt_patch=${this_path}/gcc-bt.patch
+gcc_pr55650_patch=${this_path}/gcc48-pr55650.patch
 make_flags='-j12'
 
 root_dir=$(mktemp -d)
 cd $root_dir
 
 if test -z $TMPDIR; then
   TMPDIR=/tmp/
 fi
@@ -25,16 +27,18 @@ cd ..
 tar xjf $TMPDIR/gcc-$gcc_version.tar.bz2
 cd gcc-$gcc_version
 
 ./contrib/download_prerequisites
 
 # gcc 4.7 doesn't dump a stack on ICE so hack that in
 patch -p1 < $gcc_bt_patch || exit 1
 
+patch -p0 < $gcc_pr55650_patch || exit 1
+
 cd ..
 mkdir gcc-objdir
 cd gcc-objdir
 ../gcc-$gcc_version/configure --prefix=/tools/gcc --enable-languages=c,c++  --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro || exit 1
 make $make_flags || exit 1
 make $make_flags install DESTDIR=$root_dir || exit 1
 
 cd $root_dir/tools
new file mode 100644
--- /dev/null
+++ b/build/unix/build-gcc/gcc48-pr55650.patch
@@ -0,0 +1,52 @@
+2012-12-12  Jakub Jelinek  <jakub@redhat.com>
+
+	PR gcov-profile/55650
+	* coverage.c (coverage_obj_init): Return false if no functions
+	are being emitted.
+
+	* g++.dg/other/pr55650.C: New test.
+	* g++.dg/other/pr55650.cc: New file.
+
+--- gcc/coverage.c.jj	2012-11-19 14:41:24.000000000 +0100
++++ gcc/coverage.c	2012-12-12 08:54:35.005180211 +0100
+@@ -999,6 +999,9 @@ coverage_obj_init (void)
+       /* The function is not being emitted, remove from list.  */
+       *fn_prev = fn->next;
+ 
++  if (functions_head == NULL)
++    return false;
++
+   for (ix = 0; ix != GCOV_COUNTERS; ix++)
+     if ((1u << ix) & prg_ctr_mask)
+       n_counters++;
+--- gcc/testsuite/g++.dg/other/pr55650.C.jj	2012-12-12 09:03:53.342876593 +0100
++++ gcc/testsuite/g++.dg/other/pr55650.C	2012-12-12 09:03:11.000000000 +0100
+@@ -0,0 +1,21 @@
++// PR gcov-profile/55650
++// { dg-do link }
++// { dg-options "-O2 -fprofile-generate" }
++// { dg-additional-sources "pr55650.cc" }
++
++struct A
++{
++  virtual void foo ();
++};
++
++struct B : public A
++{
++  B ();
++  void foo () {}
++};
++
++inline A *
++bar ()
++{
++  return new B;
++}
+--- gcc/testsuite/g++.dg/other/pr55650.cc.jj	2012-12-12 09:03:56.329858741 +0100
++++ gcc/testsuite/g++.dg/other/pr55650.cc	2012-12-12 09:03:48.982900718 +0100
+@@ -0,0 +1,4 @@
++int
++main ()
++{
++}
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -1448,16 +1448,27 @@ HTMLInputElement::AfterSetAttr(int32_t a
         GetValue(value);
         SetValueInternal(value, false, false);
         MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                    "HTML5 spec does not allow this");
       }
     } else if (aName == nsGkAtoms::dir &&
                aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
       SetDirectionIfAuto(true, aNotify);
+    } else if (aName == nsGkAtoms::lang) {
+      if (mType == NS_FORM_INPUT_NUMBER) {
+        // Update the value that is displayed to the user to the new locale:
+        nsAutoString value;
+        GetValueInternal(value);
+        nsNumberControlFrame* numberControlFrame =
+          do_QueryFrame(GetPrimaryFrame());
+        if (numberControlFrame) {
+          numberControlFrame->SetValueOfAnonTextControl(value);
+        }
+      }
     }
 
     UpdateState(aNotify);
   }
 
   return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName,
                                                          aValue, aNotify);
 }
@@ -1660,17 +1671,18 @@ HTMLInputElement::IsValueEmpty() const
 
 void
 HTMLInputElement::ClearFiles(bool aSetValueChanged)
 {
   nsTArray<nsCOMPtr<nsIDOMFile> > files;
   SetFiles(files, aSetValueChanged);
 }
 
-static Decimal StringToDecimal(nsAString& aValue)
+/* static */ Decimal
+HTMLInputElement::StringToDecimal(const nsAString& aValue)
 {
   if (!IsASCII(aValue)) {
     return Decimal::nan();
   }
   NS_LossyConvertUTF16toASCII asciiString(aValue);
   std::string stdString = asciiString.get();
   return Decimal::fromString(stdString);
 }
@@ -2768,17 +2780,17 @@ HTMLInputElement::SetValueInternal(const
           SetValueChanged(true);
         }
         OnValueChanged(!mParserCreating);
 
         if (mType == NS_FORM_INPUT_NUMBER) {
           nsNumberControlFrame* numberControlFrame =
             do_QueryFrame(GetPrimaryFrame());
           if (numberControlFrame) {
-            numberControlFrame->UpdateForValueChange(value);
+            numberControlFrame->SetValueOfAnonTextControl(value);
           }
         }
       }
 
       // Call parent's SetAttr for color input so its control frame is notified
       // and updated
       if (mType == NS_FORM_INPUT_COLOR) {
         return nsGenericHTMLFormElement::SetAttr(kNameSpaceID_None,
@@ -3450,19 +3462,19 @@ HTMLInputElement::PreHandleEvent(nsEvent
     nsNumberControlFrame* numberControlFrame =
       do_QueryFrame(GetPrimaryFrame());
     if (numberControlFrame) {
       textControl = numberControlFrame->GetAnonTextControl();
     }
     if (textControl && aVisitor.mEvent->originalTarget == textControl) {
       if (aVisitor.mEvent->message == NS_FORM_INPUT) {
         // Propogate the anon text control's new value to our HTMLInputElement:
+        nsAutoString value;
+        numberControlFrame->GetValueOfAnonTextControl(value);
         numberControlFrame->HandlingInputEvent(true);
-        nsAutoString value;
-        textControl->GetValue(value);
         nsWeakFrame weakNumberControlFrame(numberControlFrame);
         SetValueInternal(value, false, true);
         if (weakNumberControlFrame.IsAlive()) {
           numberControlFrame->HandlingInputEvent(false);
         }
       }
       else if (aVisitor.mEvent->message == NS_FORM_CHANGE) {
         // We cancel the DOM 'change' event that is fired for any change to our
--- a/content/html/content/src/HTMLInputElement.h
+++ b/content/html/content/src/HTMLInputElement.h
@@ -710,16 +710,27 @@ public:
   bool MozIsTextField(bool aExcludePassword);
 
   nsIEditor* GetEditor();
 
   // XPCOM SetUserInput() is OK
 
   // XPCOM GetPhonetic() is OK
 
+  /**
+   * If aValue contains a valid floating-point number in the format specified
+   * by the HTML 5 spec:
+   *
+   *   http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#floating-point-numbers
+   *
+   * then this function will return the number parsed as a Decimal, otherwise
+   * it will return a Decimal for which Decimal::isFinite() will return false.
+   */
+  static Decimal StringToDecimal(const nsAString& aValue);
+
 protected:
   virtual JSObject* WrapNode(JSContext* aCx,
                              JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   // Pull IsSingleLineTextControl into our scope, otherwise it'd be hidden
   // by the nsITextControlElement version.
   using nsGenericHTMLFormElementWithState::IsSingleLineTextControl;
 
--- a/content/html/content/test/forms/mochitest.ini
+++ b/content/html/content/test/forms/mochitest.ini
@@ -18,16 +18,19 @@ support-files =
 [test_input_color_input_change_events.html]
 [test_input_color_picker_initial.html]
 [test_input_color_picker_popup.html]
 [test_input_color_picker_update.html]
 [test_input_email.html]
 [test_input_event.html]
 [test_input_file_picker.html]
 [test_input_list_attribute.html]
+[test_input_number_l10n.html]
+# We don't build ICU for Firefox for Android or Firefox OS:
+skip-if = os == "android" || appname == "b2g"
 [test_input_number_key_events.html]
 [test_input_number_mouse_events.html]
 # Not run on Firefox OS and Firefox for Android where the spin buttons are hidden:
 skip-if = os == "android" || appname == "b2g"
 [test_input_number_rounding.html]
 skip-if = os == "android"
 [test_input_range_attr_order.html]
 [test_input_range_key_events.html]
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/forms/test_input_number_l10n.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=844744
+-->
+<head>
+  <title>Test localization of number control input</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=844744">Mozilla Bug 844744</a>
+<p id="display"></p>
+<div id="content">
+  <input id="input" type="number" step="any">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 844744
+ * This test checks that localized input that is typed into <input type=number>
+ * is correctly handled.
+ **/
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+  startTests();
+  SimpleTest.finish();
+});
+
+var tests = [
+  { desc: "British English",
+    langTag: "en-GB", inputWithGrouping: "123,456.78",
+    inputWithoutGrouping: "123456.78", value: 123456.78
+  },
+  { desc: "Farsi",
+    langTag: "fa", inputWithGrouping: "۱۲۳٬۴۵۶٫۷۸",
+    inputWithoutGrouping: "۱۲۳۴۵۶٫۷۸", value: 123456.78
+  },
+  { desc: "French",
+    langTag: "fr-FR", inputWithGrouping: "123 456,78",
+    inputWithoutGrouping: "123456,78", value: 123456.78
+  },
+  { desc: "German",
+    langTag: "de", inputWithGrouping: "123.456,78",
+    inputWithoutGrouping: "123456,78", value: 123456.78
+  },
+  { desc: "Hebrew",
+    langTag: "he", inputWithGrouping: "123,456.78",
+    inputWithoutGrouping: "123456.78", value: 123456.78
+  },
+];
+
+var elem;
+
+function runTest(test) {
+  elem.lang = test.langTag;
+  elem.value = 0;
+  elem.focus();
+  elem.select();
+  sendString(test.inputWithGrouping);
+  is(elem.value, test.value, "Test " + test.desc + " ('" + test.langTag +
+                             "') localization with grouping separator");
+  elem.value = 0;
+  elem.select();
+  sendString(test.inputWithoutGrouping);
+  is(elem.value, test.value, "Test " + test.desc + " ('" + test.langTag +
+                             "') localization without grouping separator");
+}
+
+function startTests() {
+  elem = document.getElementById("input");
+  for (var test of tests) {
+    runTest(test, elem);
+  }
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -1626,146 +1626,16 @@ this.DOMApplicationRegistry = {
     function sendError(aError) {
       aData.error = aError;
       aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
     }
 
     let id = this._appIdForManifestURL(aData.manifestURL);
     let app = this.webapps[id];
 
-    function updatePackagedApp(aManifest) {
-      debug("updatePackagedApp");
-
-      // if the app manifestURL has a app:// scheme, we can't have an
-      // update.
-      if (app.manifestURL.startsWith("app://")) {
-        aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
-        return;
-      }
-
-      // Store the new update manifest.
-      let dir = this._getAppDir(id).path;
-      let manFile = OS.Path.join(dir, "staged-update.webapp");
-      this._writeFile(manFile, JSON.stringify(aManifest));
-
-      let manifest = new ManifestHelper(aManifest, app.manifestURL);
-      // A package is available: set downloadAvailable to fire the matching
-      // event.
-      app.downloadAvailable = true;
-      app.downloadSize = manifest.size;
-      app.updateManifest = aManifest;
-      this._saveApps().then(() => {
-        this.broadcastMessage("Webapps:UpdateState", {
-          app: app,
-          manifestURL: app.manifestURL
-        });
-        this.broadcastMessage("Webapps:FireEvent", {
-          eventType: "downloadavailable",
-          manifestURL: app.manifestURL,
-          requestID: aData.requestID
-        });
-      });
-    }
-
-    // A hosted app is updated if the app manifest or the appcache needs
-    // updating. Even if the app manifest has not changed, we still check
-    // for changes in the app cache.
-    // 'aNewManifest' would contain the updated app manifest if
-    // it has actually been updated, while 'aOldManifest' contains the
-    // stored app manifest.
-    function updateHostedApp(aOldManifest, aNewManifest) {
-      debug("updateHostedApp " + aData.manifestURL);
-
-      // Clean up the deprecated manifest cache if needed.
-      if (id in this._manifestCache) {
-        delete this._manifestCache[id];
-      }
-
-      app.manifest = aNewManifest || aOldManifest;
-
-      let manifest;
-      if (aNewManifest) {
-        this.updateAppHandlers(aOldManifest, aNewManifest, app);
-
-        // Store the new manifest.
-        let dir = this._getAppDir(id).path;
-        let manFile = OS.Path.join(dir, "manifest.webapp");
-        this._writeFile(manFile, JSON.stringify(aNewManifest));
-        manifest = new ManifestHelper(aNewManifest, app.origin);
-
-        if (supportUseCurrentProfile()) {
-          // Update the permissions for this app.
-          PermissionsInstaller.installPermissions({
-            manifest: app.manifest,
-            origin: app.origin,
-            manifestURL: aData.manifestURL
-          }, true);
-        }
-
-        this.updateDataStore(this.webapps[id].localId, app.origin,
-                             app.manifestURL, app.manifest, app.appStatus);
-
-        app.name = manifest.name;
-        app.csp = manifest.csp || "";
-        app.role = manifest.role || "";
-        app.updateTime = Date.now();
-      } else {
-        manifest = new ManifestHelper(aOldManifest, app.origin);
-      }
-
-      // Update the registry.
-      this.webapps[id] = app;
-      this._saveApps().then(() => {
-        let reg = DOMApplicationRegistry;
-        if (!manifest.appcache_path) {
-          reg.broadcastMessage("Webapps:UpdateState", {
-            app: app,
-            manifest: app.manifest,
-            manifestURL: app.manifestURL
-          });
-          reg.broadcastMessage("Webapps:FireEvent", {
-            eventType: "downloadapplied",
-            manifestURL: app.manifestURL,
-            requestID: aData.requestID
-          });
-        } else {
-          // Check if the appcache is updatable, and send "downloadavailable" or
-          // "downloadapplied".
-          let updateObserver = {
-            observe: function(aSubject, aTopic, aObsData) {
-              debug("updateHostedApp: updateSvc.checkForUpdate return for " +
-                    app.manifestURL + " - event is " + aTopic);
-              let eventType =
-                aTopic == "offline-cache-update-available" ? "downloadavailable"
-                                                           : "downloadapplied";
-              app.downloadAvailable = (eventType == "downloadavailable");
-              reg._saveApps().then(() => {
-                reg.broadcastMessage("Webapps:UpdateState", {
-                  app: app,
-                  manifest: app.manifest,
-                  manifestURL: app.manifestURL
-                });
-                reg.broadcastMessage("Webapps:FireEvent", {
-                  eventType: eventType,
-                  manifestURL: app.manifestURL,
-                  requestID: aData.requestID
-                });
-              });
-            }
-          };
-          debug("updateHostedApp: updateSvc.checkForUpdate for " +
-                manifest.fullAppcachePath());
-          updateSvc.checkForUpdate(Services.io.newURI(manifest.fullAppcachePath(), null, null),
-                                   app.localId, false, updateObserver);
-        }
-        delete app.manifest;
-      });
-    }
-
-
     // We cannot update an app that does not exists.
     if (!app) {
       sendError("NO_SUCH_APP");
       return;
     }
 
     // We cannot update an app that is not fully installed.
     if (app.installState !== "installed") {
@@ -1774,16 +1644,25 @@ this.DOMApplicationRegistry = {
     }
 
     // We may be able to remove this when Bug 839071 is fixed.
     if (app.downloading) {
       sendError("APP_IS_DOWNLOADING");
       return;
     }
 
+    // If the app is packaged and its manifestURL has an app:// scheme,
+    // then we can't have an update.
+    if (app.origin.startsWith("app://") &&
+        app.manifestURL.startsWith("app://")) {
+      aData.error = "NOT_UPDATABLE";
+      aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+      return;
+    }
+
     // For non-removable hosted apps that lives in the core apps dir we
     // only check the appcache because we can't modify the manifest even
     // if it has changed.
     let onlyCheckAppCache = false;
 
 #ifdef MOZ_WIDGET_GONK
     let appDir = FileUtils.getDir("coreAppsDir", ["webapps"], false);
     onlyCheckAppCache = (app.basePath == appDir.path);
@@ -1874,17 +1753,17 @@ this.DOMApplicationRegistry = {
           } else {
             app.manifestHash = hash;
             app.etag = xhr.getResponseHeader("Etag");
           }
 
           app.lastCheckedUpdate = Date.now();
           if (isPackage) {
             if (oldHash != hash) {
-              updatePackagedApp.call(this, manifest);
+              this.updatePackagedApp(aData, id, app, manifest);
             } else {
               this._saveApps().then(() => {
                 // Like if we got a 304, just send a 'downloadapplied'
                 // or downloadavailable event.
                 let eventType = app.downloadAvailable ? "downloadavailable"
                                                       : "downloadapplied";
                 aMm.sendAsyncMessage("Webapps:UpdateState", {
                   app: app,
@@ -1895,17 +1774,17 @@ this.DOMApplicationRegistry = {
                   manifestURL: app.manifestURL,
                   requestID: aData.requestID
                 });
               });
             }
           } else {
             // Update only the appcache if the manifest has not changed
             // based on the hash value.
-            updateHostedApp.call(this, oldManifest,
+            this.updateHostedApp(aData, id, app, oldManifest,
                                  oldHash == hash ? null : manifest);
           }
         }
       } else if (xhr.status == 304) {
         // The manifest has not changed.
         if (isPackage) {
           app.lastCheckedUpdate = Date.now();
           this._saveApps().then(() => {
@@ -1921,17 +1800,17 @@ this.DOMApplicationRegistry = {
               eventType: eventType,
               manifestURL: app.manifestURL,
               requestID: aData.requestID
             });
           });
         } else {
           // For hosted apps, even if the manifest has not changed, we check
           // for offline cache updates.
-          updateHostedApp.call(this, oldManifest, null);
+          this.updateHostedApp(aData, id, app, oldManifest, null);
         }
       } else {
         sendError("MANIFEST_URL_ERROR");
       }
     }
 
     // Try to download a new manifest.
     function doRequest(oldManifest, headers) {
@@ -2001,16 +1880,138 @@ this.DOMApplicationRegistry = {
        getInterface: function(iid) {
          if (iid.equals(Ci.nsILoadContext))
            return this;
          throw Cr.NS_ERROR_NO_INTERFACE;
        }
      }
   },
 
+  updatePackagedApp: function(aData, aId, aApp, aNewManifest) {
+    debug("updatePackagedApp");
+
+    // Store the new update manifest.
+    let dir = this._getAppDir(aId).path;
+    let manFile = OS.Path.join(dir, "staged-update.webapp");
+    this._writeFile(manFile, JSON.stringify(aNewManifest));
+
+    let manifest = new ManifestHelper(aNewManifest, aApp.manifestURL);
+    // A package is available: set downloadAvailable to fire the matching
+    // event.
+    aApp.downloadAvailable = true;
+    aApp.downloadSize = manifest.size;
+    aApp.updateManifest = aNewManifest;
+    this._saveApps().then(() => {
+      this.broadcastMessage("Webapps:UpdateState", {
+        app: aApp,
+        manifestURL: aApp.manifestURL
+      });
+      this.broadcastMessage("Webapps:FireEvent", {
+        eventType: "downloadavailable",
+        manifestURL: aApp.manifestURL,
+        requestID: aData.requestID
+      });
+    });
+  },
+
+  // A hosted app is updated if the app manifest or the appcache needs
+  // updating. Even if the app manifest has not changed, we still check
+  // for changes in the app cache.
+  // 'aNewManifest' would contain the updated app manifest if
+  // it has actually been updated, while 'aOldManifest' contains the
+  // stored app manifest.
+  updateHostedApp: function(aData, aId, aApp, aOldManifest, aNewManifest) {
+    debug("updateHostedApp " + aData.manifestURL);
+
+    // Clean up the deprecated manifest cache if needed.
+    if (aId in this._manifestCache) {
+      delete this._manifestCache[aId];
+    }
+
+    aApp.manifest = aNewManifest || aOldManifest;
+
+    let manifest;
+    if (aNewManifest) {
+      this.updateAppHandlers(aOldManifest, aNewManifest, aApp);
+
+      // Store the new manifest.
+      let dir = this._getAppDir(aId).path;
+      let manFile = OS.Path.join(dir, "manifest.webapp");
+      this._writeFile(manFile, JSON.stringify(aNewManifest));
+      manifest = new ManifestHelper(aNewManifest, aApp.origin);
+
+      if (supportUseCurrentProfile()) {
+        // Update the permissions for this app.
+        PermissionsInstaller.installPermissions({
+          manifest: aApp.manifest,
+          origin: aApp.origin,
+          manifestURL: aData.manifestURL
+        }, true);
+      }
+
+      this.updateDataStore(this.webapps[aId].localId, aApp.origin,
+                           aApp.manifestURL, aApp.manifest, aApp.appStatus);
+
+      aApp.name = manifest.name;
+      aApp.csp = manifest.csp || "";
+      aApp.role = manifest.role || "";
+      aApp.updateTime = Date.now();
+    } else {
+      manifest = new ManifestHelper(aOldManifest, aApp.origin);
+    }
+
+    // Update the registry.
+    this.webapps[aId] = aApp;
+    this._saveApps().then(() => {
+      let reg = DOMApplicationRegistry;
+      if (!manifest.appcache_path) {
+        reg.broadcastMessage("Webapps:UpdateState", {
+          app: aApp,
+          manifest: aApp.manifest,
+          manifestURL: aApp.manifestURL
+        });
+        reg.broadcastMessage("Webapps:FireEvent", {
+          eventType: "downloadapplied",
+          manifestURL: aApp.manifestURL,
+          requestID: aData.requestID
+        });
+      } else {
+        // Check if the appcache is updatable, and send "downloadavailable" or
+        // "downloadapplied".
+        let updateObserver = {
+          observe: function(aSubject, aTopic, aObsData) {
+            debug("updateHostedApp: updateSvc.checkForUpdate return for " +
+                  aApp.manifestURL + " - event is " + aTopic);
+            let eventType =
+              aTopic == "offline-cache-update-available" ? "downloadavailable"
+                                                         : "downloadapplied";
+            aApp.downloadAvailable = (eventType == "downloadavailable");
+            reg._saveApps().then(() => {
+              reg.broadcastMessage("Webapps:UpdateState", {
+                app: aApp,
+                manifest: aApp.manifest,
+                manifestURL: aApp.manifestURL
+              });
+              reg.broadcastMessage("Webapps:FireEvent", {
+                eventType: eventType,
+                manifestURL: aApp.manifestURL,
+                requestID: aData.requestID
+              });
+            });
+          }
+        };
+        debug("updateHostedApp: updateSvc.checkForUpdate for " +
+              manifest.fullAppcachePath());
+        updateSvc.checkForUpdate(Services.io.newURI(manifest.fullAppcachePath(), null, null),
+                                 aApp.localId, false, updateObserver);
+      }
+      delete aApp.manifest;
+    });
+  },
+
   // Downloads the manifest and run checks, then eventually triggers the
   // installation UI.
   doInstall: function doInstall(aData, aMm) {
     let app = aData.app;
 
     let sendError = function sendError(aError) {
       aData.error = aError;
       aMm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -3615,17 +3615,17 @@ nsGenericArraySH::NewResolve(nsIXPConnec
     }
   }
 
   return NS_OK;
 }
 
 nsresult
 nsGenericArraySH::GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                             JSObject *obj, uint32_t *length)
+                            JS::Handle<JSObject*> obj, uint32_t *length)
 {
   *length = 0;
 
   JS::Rooted<JS::Value> lenval(cx);
   if (!JS_GetProperty(cx, obj, "length", &lenval)) {
     return NS_ERROR_UNEXPECTED;
   }
 
@@ -3990,17 +3990,17 @@ nsHTMLDocumentSH::CallToGetPropMapper(JS
   // Convert all types to string.
   JS::Rooted<JSString*> str(cx, JS::ToString(cx, args[0]));
   if (!str) {
     return false;
   }
 
   // If we are called via document.all(id) instead of document.all.item(i) or
   // another method, use the document.all callee object as self.
-  JSObject *self;
+  JS::Rooted<JSObject*> self(cx);
   if (args.calleev().isObject() &&
       JS_GetClass(&args.calleev().toObject()) == &sHTMLDocumentAllClass) {
     self = &args.calleev().toObject();
   } else {
     self = JS_THIS_OBJECT(cx, vp);
     if (!self)
       return false;
   }
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -323,26 +323,26 @@ class nsGenericArraySH : public nsDOMCla
 protected:
   nsGenericArraySH(nsDOMClassInfoData* aData) : nsDOMClassInfo(aData)
   {
   }
 
   virtual ~nsGenericArraySH()
   {
   }
-  
+
 public:
   NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsid id, uint32_t flags,
                         JSObject **objp, bool *_retval) MOZ_OVERRIDE;
   NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                        JSObject *obj, bool *_retval) MOZ_OVERRIDE;
-  
+
   virtual nsresult GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                             JSObject *obj, uint32_t *length);
+                             JS::Handle<JSObject*> obj, uint32_t *length);
 
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsGenericArraySH(aData);
   }
 };
 
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -5996,97 +5996,24 @@ nsGlobalWindow::CanMoveResizeWindows()
     if (ds) {
       gDragServiceDisabled = true;
       ds->Suppress();
     }
   }
   return true;
 }
 
-void
-nsGlobalWindow::Alert(const nsAString& aMessage, mozilla::ErrorResult& aError)
-{
-  FORWARD_TO_OUTER_OR_THROW(Alert, (aMessage, aError), aError, );
-
-  if (!AreDialogsEnabled()) {
-    aError.Throw(NS_ERROR_NOT_AVAILABLE);
-    return;
-  }
-
-  // Reset popup state while opening a modal dialog, and firing events
-  // about the dialog, to prevent the current state from being active
-  // the whole time a modal dialog is open.
-  nsAutoPopupStatePusher popupStatePusher(openAbused, true);
-
-  // Before bringing up the window, unsuppress painting and flush
-  // pending reflows.
-  EnsureReflowFlushAndPaint();
-
-  nsAutoString title;
-  MakeScriptDialogTitle(title);
-
-  // Remove non-terminating null characters from the 
-  // string. See bug #310037. 
-  nsAutoString final;
-  nsContentUtils::StripNullChars(aMessage, final);
-
-  // Check if we're being called at a point where we can't use tab-modal
-  // prompts, because something doesn't want reentrancy.
-  bool allowTabModal = GetIsTabModalPromptAllowed();
-
-  nsresult rv;
-  nsCOMPtr<nsIPromptFactory> promptFac =
-    do_GetService("@mozilla.org/prompter;1", &rv);
-  if (NS_FAILED(rv)) {
-    aError.Throw(rv);
-    return;
-  }
-
-  nsCOMPtr<nsIPrompt> prompt;
-  aError = promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt),
-                                reinterpret_cast<void**>(&prompt));
-  if (aError.Failed()) {
-    return;
-  }
-
-  nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt);
-  if (promptBag)
-    promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), allowTabModal);
-
-  nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ? 
-                             GetCurrentInnerWindowInternal()->mDoc :
-                             nullptr);
-  if (ShouldPromptToBlockDialogs()) {
-    bool disallowDialog = false;
-    nsXPIDLString label;
-    nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
-                                       "ScriptDialogLabel", label);
-
-    aError = prompt->AlertCheck(title.get(), final.get(), label.get(),
-                                &disallowDialog);
-    if (disallowDialog)
-      DisableDialogs();
-  } else {
-    aError = prompt->Alert(title.get(), final.get());
-  }
-}
-
-NS_IMETHODIMP
-nsGlobalWindow::Alert(const nsAString& aString)
-{
-  ErrorResult rv;
-  Alert(aString, rv);
-
-  return rv.ErrorCode();
-}
-
 bool
-nsGlobalWindow::Confirm(const nsAString& aMessage, ErrorResult& aError)
-{
-  FORWARD_TO_OUTER_OR_THROW(Confirm, (aMessage, aError), aError, false);
+nsGlobalWindow::AlertOrConfirm(bool aAlert,
+                               const nsAString& aMessage,
+                               mozilla::ErrorResult& aError)
+{
+  // XXX This method is very similar to nsGlobalWindow::Prompt, make
+  // sure any modifications here don't need to happen over there!
+  MOZ_ASSERT(IsOuterWindow());
 
   if (!AreDialogsEnabled()) {
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return false;
   }
 
   // Reset popup state while opening a modal dialog, and firing events
   // about the dialog, to prevent the current state from being active
@@ -6114,59 +6041,89 @@ nsGlobalWindow::Confirm(const nsAString&
     do_GetService("@mozilla.org/prompter;1", &rv);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
     return false;
   }
 
   nsCOMPtr<nsIPrompt> prompt;
   aError = promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt),
-                                reinterpret_cast<void**>(&prompt));
+                                getter_AddRefs(prompt));
   if (aError.Failed()) {
     return false;
   }
 
   nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt);
   if (promptBag)
     promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), allowTabModal);
 
   bool result = false;
-  nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ? 
-                             GetCurrentInnerWindowInternal()->mDoc :
-                             nullptr);
+  nsAutoSyncOperation sync(mDoc);
   if (ShouldPromptToBlockDialogs()) {
     bool disallowDialog = false;
     nsXPIDLString label;
     nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
                                        "ScriptDialogLabel", label);
 
-    aError = prompt->ConfirmCheck(title.get(), final.get(), label.get(),
-                                  &disallowDialog, &result);
+    aError = aAlert ?
+               prompt->AlertCheck(title.get(), final.get(), label.get(),
+                                  &disallowDialog) :
+               prompt->ConfirmCheck(title.get(), final.get(), label.get(),
+                                    &disallowDialog, &result);
+
     if (disallowDialog)
       DisableDialogs();
   } else {
-    aError = prompt->Confirm(title.get(), final.get(), &result);
+    aError = aAlert ?
+               prompt->Alert(title.get(), final.get()) :
+               prompt->Confirm(title.get(), final.get(), &result);
   }
 
   return result;
 }
 
+void
+nsGlobalWindow::Alert(const nsAString& aMessage, mozilla::ErrorResult& aError)
+{
+  FORWARD_TO_OUTER_OR_THROW(Alert, (aMessage, aError), aError, );
+  AlertOrConfirm(/* aAlert = */ true, aMessage, aError);
+}
+
+NS_IMETHODIMP
+nsGlobalWindow::Alert(const nsAString& aString)
+{
+  ErrorResult rv;
+  Alert(aString, rv);
+
+  return rv.ErrorCode();
+}
+
+bool
+nsGlobalWindow::Confirm(const nsAString& aMessage, ErrorResult& aError)
+{
+  FORWARD_TO_OUTER_OR_THROW(Confirm, (aMessage, aError), aError, false);
+
+  return AlertOrConfirm(/* aAlert = */ false, aMessage, aError);
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::Confirm(const nsAString& aString, bool* aReturn)
 {
   ErrorResult rv;
   *aReturn = Confirm(aString, rv);
 
   return rv.ErrorCode();
 }
 
 void
 nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
                        nsAString& aReturn, ErrorResult& aError)
 {
+  // XXX This method is very similar to nsGlobalWindow::AlertOrConfirm, make
+  // sure any modifications here don't need to happen over there!
   FORWARD_TO_OUTER_OR_THROW(Prompt, (aMessage, aInitial, aReturn, aError),
                             aError, );
 
   SetDOMStringToNull(aReturn);
 
   if (!AreDialogsEnabled()) {
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
@@ -6199,17 +6156,17 @@ nsGlobalWindow::Prompt(const nsAString& 
     do_GetService("@mozilla.org/prompter;1", &rv);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
     return;
   }
 
   nsCOMPtr<nsIPrompt> prompt;
   aError = promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt),
-                                reinterpret_cast<void**>(&prompt));
+                                getter_AddRefs(prompt));
   if (aError.Failed()) {
     return;
   }
 
   nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt);
   if (promptBag)
     promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), allowTabModal);
 
@@ -6218,19 +6175,17 @@ nsGlobalWindow::Prompt(const nsAString& 
   bool disallowDialog = false;
 
   nsXPIDLString label;
   if (ShouldPromptToBlockDialogs()) {
     nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
                                        "ScriptDialogLabel", label);
   }
 
-  nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ? 
-                             GetCurrentInnerWindowInternal()->mDoc :
-                             nullptr);
+  nsAutoSyncOperation sync(mDoc);
   bool ok;
   aError = prompt->Prompt(title.get(), fixedMessage.get(),
                           &inoutValue, label.get(), &disallowDialog, &ok);
 
   if (disallowDialog) {
     DisableDialogs();
   }
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -806,16 +806,22 @@ public:
   already_AddRefed<nsIDOMWindow> GetParent(mozilla::ErrorResult& aError);
   mozilla::dom::Element* GetFrameElement(mozilla::ErrorResult& aError);
   already_AddRefed<nsIDOMWindow> Open(const nsAString& aUrl,
                                       const nsAString& aName,
                                       const nsAString& aOptions,
                                       mozilla::ErrorResult& aError);
   mozilla::dom::Navigator* GetNavigator(mozilla::ErrorResult& aError);
   nsIDOMOfflineResourceList* GetApplicationCache(mozilla::ErrorResult& aError);
+
+protected:
+  bool AlertOrConfirm(bool aAlert, const nsAString& aMessage,
+                      mozilla::ErrorResult& aError);
+
+public:
   void Alert(const nsAString& aMessage, mozilla::ErrorResult& aError);
   bool Confirm(const nsAString& aMessage, mozilla::ErrorResult& aError);
   void Prompt(const nsAString& aMessage, const nsAString& aInitial,
               nsAString& aReturn, mozilla::ErrorResult& aError);
   void Print(mozilla::ErrorResult& aError);
   JS::Value ShowModalDialog(JSContext* aCx, const nsAString& aUrl, const mozilla::dom::Optional<JS::Handle<JS::Value> >& aArgument, const nsAString& aOptions, mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const nsAString& aTargetOrigin,
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1493,36 +1493,36 @@ AppendNamedPropertyIds(JSContext* cx, JS
   return true;
 }
 
 JSObject*
 GetXrayExpandoChain(JSObject* obj)
 {
   const js::Class* clasp = js::GetObjectClass(obj);
   JS::Value v;
-  if (IsDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
+  if (IsNonProxyDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
     v = js::GetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT);
-  } else if (js::IsProxyClass(clasp)) {
+  } else if (clasp->isProxy()) {
     MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
     v = js::GetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO);
   } else {
     MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
     v = js::GetFunctionNativeReserved(obj, CONSTRUCTOR_XRAY_EXPANDO_SLOT);
   }
   return v.isUndefined() ? nullptr : &v.toObject();
 }
 
 void
 SetXrayExpandoChain(JSObject* obj, JSObject* chain)
 {
   JS::Value v = chain ? JS::ObjectValue(*chain) : JSVAL_VOID;
   const js::Class* clasp = js::GetObjectClass(obj);
-  if (IsDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
+  if (IsNonProxyDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
     js::SetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT, v);
-  } else if (js::IsProxyClass(clasp)) {
+  } else if (clasp->isProxy()) {
     MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
     js::SetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO, v);
   } else {
     MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
     js::SetFunctionNativeReserved(obj, CONSTRUCTOR_XRAY_EXPANDO_SLOT, v);
   }
 }
 
@@ -1591,32 +1591,28 @@ NativeToString(JSContext* cx, JS::Handle
       JS::Rooted<JS::Value> toStringResult(cx);
       if (JS_CallFunctionValue(cx, obj, toString, 0, nullptr,
                                toStringResult.address())) {
         str = toStringResult.toString();
       } else {
         str = nullptr;
       }
     } else {
-      if (IsDOMProxy(obj)) {
-        str = JS_BasicObjectToString(cx, obj);
+      const js::Class* clasp = js::GetObjectClass(obj);
+      if (IsDOMClass(clasp)) {
+        str = JS_NewStringCopyZ(cx, clasp->name);
+        str = ConcatJSString(cx, "[object ", str, "]");
+      } else if (IsDOMIfaceAndProtoClass(clasp)) {
+        const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
+          DOMIfaceAndProtoJSClass::FromJSClass(clasp);
+        str = JS_NewStringCopyZ(cx, ifaceAndProtoJSClass->mToString);
       } else {
-        const js::Class* clasp = js::GetObjectClass(obj);
-        if (IsDOMClass(clasp)) {
-          str = JS_NewStringCopyZ(cx, clasp->name);
-          str = ConcatJSString(cx, "[object ", str, "]");
-        } else if (IsDOMIfaceAndProtoClass(clasp)) {
-          const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
-            DOMIfaceAndProtoJSClass::FromJSClass(clasp);
-          str = JS_NewStringCopyZ(cx, ifaceAndProtoJSClass->mToString);
-        } else {
-          MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
-          JS::Rooted<JSFunction*> fun(cx, JS_GetObjectFunction(obj));
-          str = JS_DecompileFunction(cx, fun, 0);
-        }
+        MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
+        JS::Rooted<JSFunction*> fun(cx, JS_GetObjectFunction(obj));
+        str = JS_DecompileFunction(cx, fun, 0);
       }
       str = ConcatJSString(cx, pre, str, post);
     }
   }
 
   if (!str) {
     return false;
   }
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -125,16 +125,29 @@ IsDOMClass(const JSClass* clasp)
 }
 
 inline bool
 IsDOMClass(const js::Class* clasp)
 {
   return IsDOMClass(Jsvalify(clasp));
 }
 
+// Return true if the JSClass is used for non-proxy DOM objects.
+inline bool
+IsNonProxyDOMClass(const js::Class* clasp)
+{
+  return IsDOMClass(clasp) && !clasp->isProxy();
+}
+
+inline bool
+IsNonProxyDOMClass(const JSClass* clasp)
+{
+  return IsNonProxyDOMClass(js::Valueify(clasp));
+}
+
 // Returns true if the JSClass is used for DOM interface and interface 
 // prototype objects.
 inline bool
 IsDOMIfaceAndProtoClass(const JSClass* clasp)
 {
   return clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS;
 }
 
@@ -146,38 +159,30 @@ IsDOMIfaceAndProtoClass(const js::Class*
 
 static_assert(DOM_OBJECT_SLOT == js::PROXY_PRIVATE_SLOT,
               "js::PROXY_PRIVATE_SLOT doesn't match DOM_OBJECT_SLOT.  "
               "Expect bad things");
 template <class T>
 inline T*
 UnwrapDOMObject(JSObject* obj)
 {
-  MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)) || IsDOMProxy(obj),
+  MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
              "Don't pass non-DOM objects to this function");
 
   JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
   return static_cast<T*>(val.toPrivate());
 }
 
 inline const DOMClass*
 GetDOMClass(JSObject* obj)
 {
   const js::Class* clasp = js::GetObjectClass(obj);
   if (IsDOMClass(clasp)) {
     return &DOMJSClass::FromJSClass(clasp)->mClass;
   }
-
-  if (js::IsProxyClass(clasp)) {
-    js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
-    if (handler->family() == ProxyFamily()) {
-      return &static_cast<DOMProxyHandler*>(handler)->mClass;
-    }
-  }
-
   return nullptr;
 }
 
 inline nsISupports*
 UnwrapDOMObjectToISupports(JSObject* aObject)
 {
   const DOMClass* clasp = GetDOMClass(aObject);
   if (!clasp || !clasp->mDOMObjectIsISupports) {
@@ -185,18 +190,17 @@ UnwrapDOMObjectToISupports(JSObject* aOb
   }
  
   return UnwrapDOMObject<nsISupports>(aObject);
 }
 
 inline bool
 IsDOMObject(JSObject* obj)
 {
-  const js::Class* clasp = js::GetObjectClass(obj);
-  return IsDOMClass(clasp) || IsDOMProxy(obj, clasp);
+  return IsDOMClass(js::GetObjectClass(obj));
 }
 
 #define UNWRAP_OBJECT(Interface, obj, value)                                 \
   mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface,        \
     mozilla::dom::Interface##Binding::NativeType>(obj, value)
 
 // Some callers don't want to set an exception when unwrapping fails
 // (for example, overload resolution uses unwrapping to tell what sort
@@ -503,42 +507,44 @@ CouldBeDOMBinding(nsWrapperCache* aCache
 
 // The DOM_OBJECT_SLOT_SOW slot contains a JS::ObjectValue which points to the
 // cached system object wrapper (SOW) or JS::UndefinedValue if this class
 // doesn't need SOWs.
 
 inline const JS::Value&
 GetSystemOnlyWrapperSlot(JSObject* obj)
 {
-  MOZ_ASSERT(IsDOMClass(js::GetObjectJSClass(obj)) &&
+  MOZ_ASSERT(IsNonProxyDOMClass(js::GetObjectJSClass(obj)) &&
              !(js::GetObjectJSClass(obj)->flags & JSCLASS_DOM_GLOBAL));
   return js::GetReservedSlot(obj, DOM_OBJECT_SLOT_SOW);
 }
 inline void
 SetSystemOnlyWrapperSlot(JSObject* obj, const JS::Value& v)
 {
-  MOZ_ASSERT(IsDOMClass(js::GetObjectJSClass(obj)) &&
+  MOZ_ASSERT(IsNonProxyDOMClass(js::GetObjectJSClass(obj)) &&
              !(js::GetObjectJSClass(obj)->flags & JSCLASS_DOM_GLOBAL));
   js::SetReservedSlot(obj, DOM_OBJECT_SLOT_SOW, v);
 }
 
 inline bool
 GetSameCompartmentWrapperForDOMBinding(JSObject*& obj)
 {
   const js::Class* clasp = js::GetObjectClass(obj);
   if (dom::IsDOMClass(clasp)) {
-    if (!(clasp->flags & JSCLASS_DOM_GLOBAL)) {
+    if (!clasp->isProxy() &&
+        !(clasp->flags & JSCLASS_DOM_GLOBAL))
+    {
       JS::Value v = GetSystemOnlyWrapperSlot(obj);
       if (v.isObject()) {
         obj = &v.toObject();
       }
     }
     return true;
   }
-  return IsDOMProxy(obj, clasp);
+  return false;
 }
 
 inline void
 SetSystemOnlyWrapper(JSObject* obj, nsWrapperCache* cache, JSObject& wrapper)
 {
   SetSystemOnlyWrapperSlot(obj, JS::ObjectValue(wrapper));
   cache->SetHasSystemOnlyWrapper();
 }
@@ -1949,17 +1955,16 @@ enum {
 bool
 Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 inline bool
 UseDOMXray(JSObject* obj)
 {
   const js::Class* clasp = js::GetObjectClass(obj);
   return IsDOMClass(clasp) ||
-         IsDOMProxy(obj, clasp) ||
          JS_IsNativeFunction(obj, Constructor) ||
          IsDOMIfaceAndProtoClass(clasp);
 }
 
 #ifdef DEBUG
 inline bool
 HasConstructor(JSObject* obj)
 {
@@ -2417,17 +2422,16 @@ CreateGlobal(JSContext* aCx, T* aObject,
 class InternedStringId
 {
   jsid id;
 
  public:
   InternedStringId() : id(JSID_VOID) {}
 
   bool init(JSContext *cx, const char *string) {
-    MOZ_ASSERT(id == JSID_VOID);
     JSString* str = JS_InternString(cx, string);
     if (!str)
       return false;
     id = INTERNED_STRING_TO_JSID(cx, str);
     return true;
   }
 
   operator const jsid& () {
--- a/dom/bindings/CallbackInterface.cpp
+++ b/dom/bindings/CallbackInterface.cpp
@@ -11,17 +11,17 @@
 
 namespace mozilla {
 namespace dom {
 
 bool
 CallbackInterface::GetCallableProperty(JSContext* cx, const char* aPropName,
                                        JS::MutableHandle<JS::Value> aCallable)
 {
-  if (!JS_GetProperty(cx, mCallback, aPropName, aCallable)) {
+  if (!JS_GetProperty(cx, CallbackPreserveColor(), aPropName, aCallable)) {
     return false;
   }
   if (!aCallable.isObject() ||
       !JS_ObjectIsCallable(cx, &aCallable.toObject())) {
     nsPrintfCString description("Property '%s'", aPropName);
     ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get());
     return false;
   }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -200,26 +200,49 @@ static const DOMJSClass Class = {
     %s, /* enumerate */
     %s, /* resolve */
     JS_ConvertStub,
     %s, /* finalize */
     %s, /* call */
     nullptr,               /* hasInstance */
     nullptr,               /* construct */
     %s, /* trace */
-    JSCLASS_NO_INTERNAL_MEMBERS
+    JS_NULL_CLASS_EXT,
+    JS_NULL_OBJECT_OPS
   },
 %s
 };
 """ % (self.descriptor.interface.identifier.name,
        classFlags,
        ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'JS_PropertyStub',
        enumerateHook, newResolveHook, FINALIZE_HOOK_NAME, callHook, traceHook,
        CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
 
+class CGDOMProxyJSClass(CGThing):
+    """
+    Generate a DOMJSClass for a given proxy descriptor
+    """
+    def __init__(self, descriptor):
+        CGThing.__init__(self)
+        self.descriptor = descriptor
+    def declare(self):
+        return ""
+    def define(self):
+        return """
+static const DOMJSClass Class = {
+  PROXY_CLASS_DEF("%s",
+                  0, /* extra slots */
+                  JSCLASS_IS_DOMJSCLASS,
+                  nullptr, /* call */
+                  nullptr  /* construct */),
+%s
+};
+""" % (self.descriptor.interface.identifier.name,
+       CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
+
 def PrototypeIDAndDepth(descriptor):
     prototypeID = "prototypes::id::"
     if descriptor.interface.hasInterfacePrototypeObject():
         prototypeID += descriptor.interface.identifier.name
         if descriptor.workers:
             prototypeID += "_workers"
         depth = "PrototypeTraits<%s>::Depth" % prototypeID
     else:
@@ -1049,37 +1072,37 @@ class CGAddPropertyHook(CGAbstractClassH
 
 def DeferredFinalizeSmartPtr(descriptor):
     if descriptor.nativeOwnership == 'owned':
         smartPtr = 'nsAutoPtr'
     else:
         smartPtr = 'nsRefPtr'
     return smartPtr
 
-def finalizeHook(descriptor, hookName, context):
+def finalizeHook(descriptor, hookName, freeOp):
     if descriptor.customFinalize:
-        finalize = "self->%s(%s);" % (hookName, context)
+        finalize = "self->%s(CastToJSFreeOp(%s));" % (hookName, freeOp)
     else:
         finalize = "JSBindingFinalized<%s>::Finalized(self);\n" % descriptor.nativeType
         if descriptor.wrapperCache:
             finalize += "ClearWrapper(self, self);\n"
         if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
             finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n"
         if descriptor.interface.getExtendedAttribute("Global"):
-            finalize += "mozilla::dom::FinalizeGlobal(fop, obj);\n"
+            finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp
         finalize += ("AddForDeferredFinalization<%s, %s >(self);" %
             (descriptor.nativeType, DeferredFinalizeSmartPtr(descriptor)))
     return CGIfWrapper(CGGeneric(finalize), "self")
 
 class CGClassFinalizeHook(CGAbstractClassHook):
     """
     A hook for finalize, used to release our native object.
     """
     def __init__(self, descriptor):
-        args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'obj')]
+        args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')]
         CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
                                      'void', args)
 
     def generate_code(self):
         return CGIndenter(finalizeHook(self.descriptor, self.name, self.args[0].name)).define()
 
 class CGClassConstructor(CGAbstractStaticMethod):
     """
@@ -1921,20 +1944,17 @@ if (!unforgeableHolder) {
             interfaceCache = "&aProtoAndIfaceArray[constructors::id::%s]" % self.descriptor.name
         else:
             # We don't have slots to store the named constructors.
             assert len(self.descriptor.interface.namedConstructors) == 0
             interfaceClass = "nullptr"
             interfaceCache = "nullptr"
 
         if self.descriptor.concrete:
-            if self.descriptor.proxy:
-                domClass = "&Class"
-            else:
-                domClass = "&Class.mClass"
+            domClass = "&Class.mClass"
         else:
             domClass = "nullptr"
 
         if self.properties.hasNonChromeOnly():
             properties = "&sNativeProperties"
         else:
             properties = "nullptr"
         if self.properties.hasChromeOnly():
@@ -2143,30 +2163,32 @@ class CGConstructorEnabledViaFunc(CGAbst
         return "  return %s(cx, obj);" % func[0]
 
 def CreateBindingJSObject(descriptor, properties, parent):
     # We don't always need to root obj, but there are a variety
     # of cases where we do, so for simplicity, just always root it.
     objDecl = "  JS::Rooted<JSObject*> obj(aCx);\n"
     if descriptor.proxy:
         create = """  JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aObject));
+  js::ProxyOptions options;
+  options.setClass(&Class.mBase);
   obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
-                       proxyPrivateVal, proto, %s);
+                       proxyPrivateVal, proto, %s, options);
   if (!obj) {
     return nullptr;
   }
 
 """
         if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
             create += """  js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO,
                     JS::PrivateValue(&aObject->mExpandoAndGeneration));
 
 """
     else:
-        create = """  obj = JS_NewObject(aCx, &Class.mBase, proto, %s);
+        create = """  obj = JS_NewObject(aCx, Class.ToJSClass(), proto, %s);
   if (!obj) {
     return nullptr;
   }
 
   js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
 """
         if "Window" in descriptor.interface.identifier.name:
             create = """  MOZ_ASSERT(false,
@@ -2403,17 +2425,17 @@ class CGWrapGlobalMethod(CGAbstractMetho
         return """%s
   MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
              "nsISupports must be on our primary inheritance chain");
 
   JS::Rooted<JSObject*> obj(aCx);
   obj = CreateGlobal<%s, GetProtoObject>(aCx,
                                          aObject,
                                          aCache,
-                                         &Class.mBase,
+                                         Class.ToJSClass(),
                                          aOptions,
                                          aPrincipal);
 
 %s
 
   // XXXkhuey can't do this yet until workers can lazy resolve.
   // JS_FireOnNewGlobalObject(aCx, obj);
 
@@ -8087,33 +8109,16 @@ class CGProxyUnwrap(CGAbstractMethod):
         return """  MOZ_ASSERT(js::IsProxy(obj));
   if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) {
     MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj));
     obj = js::UncheckedUnwrap(obj);
   }
   MOZ_ASSERT(IsProxy(obj));
   return static_cast<%s*>(js::GetProxyPrivate(obj).toPrivate());""" % (self.descriptor.nativeType)
 
-class CGDOMJSProxyHandlerDOMClass(CGThing):
-    def __init__(self, descriptor):
-        CGThing.__init__(self)
-        self.descriptor = descriptor
-    def declare(self):
-        return ""
-    def define(self):
-        return """
-static const DOMClass Class = """ + DOMClass(self.descriptor) + """;
-
-"""
-
-class CGDOMJSProxyHandler_CGDOMJSProxyHandler(ClassConstructor):
-    def __init__(self):
-        ClassConstructor.__init__(self, [], inline=True, visibility="private",
-                                  baseConstructors=["mozilla::dom::DOMProxyHandler(Class)"])
-
 class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JS::Handle<JSObject*>', 'proxy'),
                 Argument('JS::Handle<jsid>', 'id'),
                 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'),
                 Argument('unsigned', 'flags')]
         ClassMethod.__init__(self, "getOwnPropertyDescriptor", "bool", args)
         self.descriptor = descriptor
@@ -8649,17 +8654,16 @@ class CGDOMJSProxyHandler_getInstance(Cl
     def getBody(self):
         return """static DOMProxyHandler instance;
 return &instance;"""
 
 class CGDOMJSProxyHandler(CGClass):
     def __init__(self, descriptor):
         assert (descriptor.supportsIndexedProperties() or
                 descriptor.supportsNamedProperties())
-        constructors = [CGDOMJSProxyHandler_CGDOMJSProxyHandler()]
         methods = [CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor),
                    CGDOMJSProxyHandler_defineProperty(descriptor),
                    ClassUsingDeclaration("mozilla::dom::DOMProxyHandler",
                                          "defineProperty"),
                    CGDOMJSProxyHandler_getOwnPropertyNames(descriptor),
                    CGDOMJSProxyHandler_hasOwn(descriptor),
                    CGDOMJSProxyHandler_get(descriptor),
                    CGDOMJSProxyHandler_className(descriptor),
@@ -8667,17 +8671,16 @@ class CGDOMJSProxyHandler(CGClass):
                    CGDOMJSProxyHandler_finalize(descriptor),
                    CGDOMJSProxyHandler_getInstance(),
                    CGDOMJSProxyHandler_delete(descriptor)]
         if descriptor.supportsIndexedProperties():
             methods.append(CGDOMJSProxyHandler_slice(descriptor))
 
         CGClass.__init__(self, 'DOMProxyHandler',
                          bases=[ClassBase('mozilla::dom::DOMProxyHandler')],
-                         constructors=constructors,
                          methods=methods)
 
 class CGDOMJSProxyHandlerDeclarer(CGThing):
     """
     A class for declaring a DOMProxyHandler.
     """
     def __init__(self, handlerThing):
         self.handlerThing = handlerThing
@@ -8903,18 +8906,18 @@ class CGDescriptor(CGThing):
 """ % descriptor.nativeType))
                 if not descriptor.wrapperCache:
                     raise TypeError("We need a wrappercache to support expandos for proxy-based "
                                     "bindings (" + descriptor.name + ")")
                 handlerThing = CGDOMJSProxyHandler(descriptor)
                 cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing))
                 cgThings.append(CGProxyIsProxy(descriptor))
                 cgThings.append(CGProxyUnwrap(descriptor))
-                cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
                 cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing))
+                cgThings.append(CGDOMProxyJSClass(descriptor))
             else:
                 cgThings.append(CGDOMJSClass(descriptor))
                 cgThings.append(CGGetJSClassMethod(descriptor))
                 if descriptor.interface.hasMembersInSlots():
                     if descriptor.interface.hasChildInterfaces():
                         raise TypeError("We don't support members in slots on "
                                         "non-leaf interfaces like %s" %
                                         descriptor.interface.identifier.name)
@@ -9097,19 +9100,21 @@ if (cx) {
                 "}\n"
                 "\n")
 
         memberInits = [self.getMemberConversion(m).define()
                        for m in self.memberInfo]
         if memberInits:
             body += (
                 "bool isNull = val.isNullOrUndefined();\n"
-                "// We only need |temp| if !isNull, in which case we have |cx|.\n"
+                "// We only need these if !isNull, in which case we have |cx|.\n"
+                "Maybe<JS::Rooted<JSObject *> > object;\n"
                 "Maybe<JS::Rooted<JS::Value> > temp;\n"
                 "if (!isNull) {\n"
+                "  object.construct(cx, &val.toObject());\n"
                 "  temp.construct(cx);\n"
                 "}\n")
             body += "\n\n".join(memberInits) + "\n"
 
         body += "return true;"
 
         return ClassMethod("Init", "bool", [
             Argument('JSContext*', 'cx'),
@@ -9165,17 +9170,17 @@ if (!*reinterpret_cast<jsid**>(atomsCach
         return ClassMethod("ToObject", "bool", [
             Argument('JSContext*', 'cx'),
             Argument('JS::Handle<JSObject*>', 'parentObject'),
             Argument('JS::MutableHandle<JS::Value>', 'rval'),
         ], const=True, body=body)
 
     def initIdsMethod(self):
         assert self.needToInitIds
-        idinit = [CGGeneric('!InternJSString(cx, atomsCache->%s, "%s")' %
+        idinit = [CGGeneric('!atomsCache->%s.init(cx, "%s")' %
                             (m.identifier.name + "_id", m.identifier.name))
                   for m in self.dictionary.members]
         idinit.reverse();
         idinit = CGList(idinit, " ||\n")
         idinit = CGWrapper(idinit, pre="""
 // Initialize these in reverse order so that any failure leaves the first one
 // uninitialized.
 if (""",
@@ -9350,17 +9355,17 @@ if (""",
         # We can't handle having a holderType here
         assert conversionInfo.holderType is None
         if conversionInfo.dealWithOptional:
             replacements["declName"] = "(" + replacements["declName"] + ".Value())"
         if member.defaultValue:
             replacements["haveValue"] = "!isNull && !temp.ref().isUndefined()"
 
         propId = self.makeIdName(member.identifier.name);
-        propGet = ("JS_GetPropertyById(cx, &val.toObject(), atomsCache->%s, &temp.ref())" %
+        propGet = ("JS_GetPropertyById(cx, object.ref(), atomsCache->%s, &temp.ref())" %
                    propId)
 
         conversionReplacements = {
             "prop": self.makeMemberName(member.identifier.name),
             "convert": string.Template(conversionInfo.template).substitute(replacements),
             "propGet": propGet
             }
         conversion = ("if (!isNull && !${propGet}) {\n"
@@ -11427,17 +11432,18 @@ class CallbackGetter(CallbackAccessor):
 
     def getCall(self):
         replacements = {
             "errorReturn" : self.getDefaultRetval(),
             "attrName": self.descriptorProvider.binaryNames.get(self.attrName,
                                                                 self.attrName)
             }
         return string.Template(
-            'if (!JS_GetProperty(cx, mCallback, "${attrName}", &rval)) {\n'
+            'JS::Rooted<JSObject *> callback(cx, mCallback);\n'
+            'if (!JS_GetProperty(cx, callback, "${attrName}", &rval)) {\n'
             '  aRv.Throw(NS_ERROR_UNEXPECTED);\n'
             '  return${errorReturn};\n'
             '}\n').substitute(replacements);
 
 class CallbackSetter(CallbackAccessor):
     def __init__(self, attr, descriptor):
         CallbackAccessor.__init__(self, attr,
                                   (BuiltinTypes[IDLBuiltinType.Types.void],
@@ -11494,19 +11500,18 @@ class GlobalGenRoots():
         structs = []
 
         for dict in dictionaries:
             dictMembers = dict.members
             if len(dictMembers) == 0:
                 continue
 
             classMembers = [ClassMember(m.identifier.name + "_id",
-                                        "jsid",
-                                        visibility="public",
-                                        body="JSID_VOID") for m in dictMembers]
+                                        "InternedStringId",
+                                        visibility="public") for m in dictMembers]
 
             structName = dict.identifier.name + "Atoms"
             structs.append((structName,
                             CGWrapper(CGClass(structName,
                 bases=None,
                 isStruct=True,
                 members=classMembers), post='\n')))
 
@@ -11520,16 +11525,21 @@ class GlobalGenRoots():
 
         structs = CGList(generatedStructs + [mainStruct])
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom'],
                                   CGWrapper(structs, pre='\n'))
         curr = CGWrapper(curr, post='\n')
 
+        # Add include statement for InternedStringId.
+        declareIncludes = ['mozilla/dom/BindingUtils.h']
+        curr = CGHeaders([], [], [], [], declareIncludes, [], 'GeneratedAtomList',
+                         curr)
+
         # Add include guards.
         curr = CGIncludeGuard('GeneratedAtomList', curr)
 
         # Add the auto-generated comment.
         curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
 
         # Done.
         return curr
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -163,30 +163,30 @@ struct DOMClass
 };
 
 // Special JSClass for reflected DOM objects.
 struct DOMJSClass
 {
   // It would be nice to just inherit from JSClass, but that precludes pure
   // compile-time initialization of the form |DOMJSClass = {...};|, since C++
   // only allows brace initialization for aggregate/POD types.
-  const JSClass mBase;
+  const js::Class mBase;
 
   const DOMClass mClass;
 
   static const DOMJSClass* FromJSClass(const JSClass* base) {
     MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
     return reinterpret_cast<const DOMJSClass*>(base);
   }
 
   static const DOMJSClass* FromJSClass(const js::Class* base) {
     return FromJSClass(Jsvalify(base));
   }
 
-  const JSClass* ToJSClass() const { return &mBase; }
+  const JSClass* ToJSClass() const { return Jsvalify(&mBase); }
 };
 
 // Special JSClass for DOM interface and interface prototype objects.
 struct DOMIfaceAndProtoJSClass
 {
   // It would be nice to just inherit from JSClass, but that precludes pure
   // compile-time initialization of the form
   // |DOMJSInterfaceAndPrototypeClass = {...};|, since C++ only allows brace
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -26,26 +26,21 @@ enum {
 };
 
 template<typename T> struct Prefable;
 
 // This variable exists solely to provide a unique address for use as an identifier.
 extern const char HandlerFamily;
 inline const void* ProxyFamily() { return &HandlerFamily; }
 
-inline bool IsDOMProxy(JSObject *obj, const js::Class* clasp)
-{
-    MOZ_ASSERT(js::GetObjectClass(obj) == clasp);
-    return js::IsProxyClass(clasp) &&
-           js::GetProxyHandler(obj)->family() == ProxyFamily();
-}
-
 inline bool IsDOMProxy(JSObject *obj)
 {
-    return IsDOMProxy(obj, js::GetObjectClass(obj));
+    const js::Class* clasp = js::GetObjectClass(obj);
+    return clasp->isProxy() &&
+           js::GetProxyHandler(obj)->family() == ProxyFamily();
 }
 
 class BaseDOMProxyHandler : public js::BaseProxyHandler
 {
 public:
   BaseDOMProxyHandler(const void* aProxyFamily)
     : js::BaseProxyHandler(aProxyFamily)
   {}
@@ -63,19 +58,18 @@ public:
              JS::Handle<JSObject*> callable) MOZ_OVERRIDE;
   bool unwatch(JSContext* cx, JS::Handle<JSObject*> proxy,
                JS::Handle<jsid> id) MOZ_OVERRIDE;
 };
 
 class DOMProxyHandler : public BaseDOMProxyHandler
 {
 public:
-  DOMProxyHandler(const DOMClass& aClass)
-    : BaseDOMProxyHandler(ProxyFamily()),
-      mClass(aClass)
+  DOMProxyHandler()
+    : BaseDOMProxyHandler(ProxyFamily())
   {
   }
 
   bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) MOZ_OVERRIDE;
   bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                       JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE
   {
     bool unused;
@@ -104,18 +98,16 @@ public:
       static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
     v = expandoAndGeneration->expando;
     return v.isUndefined() ? nullptr : &v.toObject();
   }
   /* GetAndClearExpandoObject does not DROP or clear the preserving wrapper flag. */
   static JSObject* GetAndClearExpandoObject(JSObject* obj);
   static JSObject* EnsureExpandoObject(JSContext* cx,
                                        JS::Handle<JSObject*> obj);
-
-  const DOMClass& mClass;
 };
 
 extern jsid s_length_id;
 
 int32_t IdToInt32(JSContext* cx, JS::Handle<jsid> id);
 
 // XXXbz this should really return uint32_t, with the maximum value
 // meaning "not an index"...
--- a/dom/bluetooth/BluetoothService.cpp
+++ b/dom/bluetooth/BluetoothService.cpp
@@ -600,20 +600,20 @@ BluetoothService::HandleSettingsChanged(
   if (!JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)) {
     return JS_ReportPendingException(cx) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (!val.isObject()) {
     return NS_OK;
   }
 
-  JSObject& obj(val.toObject());
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
 
   JS::Rooted<JS::Value> key(cx);
-  if (!JS_GetProperty(cx, &obj, "key", &key)) {
+  if (!JS_GetProperty(cx, obj, "key", &key)) {
     MOZ_ASSERT(!JS_IsExceptionPending(cx));
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (!key.isString()) {
     return NS_OK;
   }
 
@@ -621,17 +621,17 @@ BluetoothService::HandleSettingsChanged(
   bool match;
   if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_DEBUGGING_SETTING, &match)) {
     MOZ_ASSERT(!JS_IsExceptionPending(cx));
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (match) {
     JS::Rooted<JS::Value> value(cx);
-    if (!JS_GetProperty(cx, &obj, "value", &value)) {
+    if (!JS_GetProperty(cx, obj, "value", &value)) {
       MOZ_ASSERT(!JS_IsExceptionPending(cx));
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     if (!value.isBoolean()) {
       MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!");
       return NS_ERROR_UNEXPECTED;
     }
@@ -644,17 +644,17 @@ BluetoothService::HandleSettingsChanged(
   // Second, check if the string is BLUETOOTH_ENABLED_SETTING
   if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_ENABLED_SETTING, &match)) {
     MOZ_ASSERT(!JS_IsExceptionPending(cx));
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (match) {
     JS::Rooted<JS::Value> value(cx);
-    if (!JS_GetProperty(cx, &obj, "value", &value)) {
+    if (!JS_GetProperty(cx, obj, "value", &value)) {
       MOZ_ASSERT(!JS_IsExceptionPending(cx));
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     if (!value.isBoolean()) {
       MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.enabled'!");
       return NS_ERROR_UNEXPECTED;
     }
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -135,17 +135,17 @@ protected:
                  mCursor->mType == IDBCursor::OBJECTSTOREKEY ||
                  !mObjectKey.IsUnset());
 
       // Set new values.
       mCursor->mKey = mKey;
       mCursor->mObjectKey = mObjectKey;
       mCursor->mContinueToKey.Unset();
 
-      mCursor->mCloneReadInfo.Swap(mCloneReadInfo);
+      mCursor->mCloneReadInfo = Move(mCloneReadInfo);
       mCloneReadInfo.mCloneBuffer.clear();
     }
   }
 
   int32_t mCount;
   Key mKey;
   Key mObjectKey;
   StructuredCloneReadInfo mCloneReadInfo;
@@ -223,30 +223,30 @@ already_AddRefed<IDBCursor>
 IDBCursor::Create(IDBRequest* aRequest,
                   IDBTransaction* aTransaction,
                   IDBObjectStore* aObjectStore,
                   Direction aDirection,
                   const Key& aRangeKey,
                   const nsACString& aContinueQuery,
                   const nsACString& aContinueToQuery,
                   const Key& aKey,
-                  StructuredCloneReadInfo& aCloneReadInfo)
+                  StructuredCloneReadInfo&& aCloneReadInfo)
 {
   NS_ASSERTION(aObjectStore, "Null pointer!");
   NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection,
                             aRangeKey, aContinueQuery, aContinueToQuery);
   NS_ASSERTION(cursor, "This shouldn't fail!");
 
   cursor->mObjectStore = aObjectStore;
   cursor->mType = OBJECTSTORE;
   cursor->mKey = aKey;
-  cursor->mCloneReadInfo.Swap(aCloneReadInfo);
+  cursor->mCloneReadInfo = Move(aCloneReadInfo);
 
   return cursor.forget();
 }
 
 // static
 already_AddRefed<IDBCursor>
 IDBCursor::Create(IDBRequest* aRequest,
                   IDBTransaction* aTransaction,
@@ -308,33 +308,33 @@ IDBCursor::Create(IDBRequest* aRequest,
                   IDBTransaction* aTransaction,
                   IDBIndex* aIndex,
                   Direction aDirection,
                   const Key& aRangeKey,
                   const nsACString& aContinueQuery,
                   const nsACString& aContinueToQuery,
                   const Key& aKey,
                   const Key& aObjectKey,
-                  StructuredCloneReadInfo& aCloneReadInfo)
+                  StructuredCloneReadInfo&& aCloneReadInfo)
 {
   NS_ASSERTION(aIndex, "Null pointer!");
   NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(),
                             aDirection, aRangeKey, aContinueQuery,
                             aContinueToQuery);
   NS_ASSERTION(cursor, "This shouldn't fail!");
 
   cursor->mObjectStore = aIndex->ObjectStore();
   cursor->mIndex = aIndex;
   cursor->mType = INDEXOBJECT;
   cursor->mKey = aKey;
   cursor->mObjectKey = aObjectKey;
-  cursor->mCloneReadInfo.Swap(aCloneReadInfo);
+  cursor->mCloneReadInfo = Move(aCloneReadInfo);
 
   return cursor.forget();
 }
 
 // static
 IDBCursor::Direction
 IDBCursor::ConvertDirection(mozilla::dom::IDBCursorDirection aDirection)
 {
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -77,17 +77,17 @@ public:
   Create(IDBRequest* aRequest,
          IDBTransaction* aTransaction,
          IDBObjectStore* aObjectStore,
          Direction aDirection,
          const Key& aRangeKey,
          const nsACString& aContinueQuery,
          const nsACString& aContinueToQuery,
          const Key& aKey,
-         StructuredCloneReadInfo& aCloneReadInfo);
+         StructuredCloneReadInfo&& aCloneReadInfo);
 
   // For OBJECTSTOREKEY cursors.
   static
   already_AddRefed<IDBCursor>
   Create(IDBRequest* aRequest,
          IDBTransaction* aTransaction,
          IDBObjectStore* aObjectStore,
          Direction aDirection,
@@ -116,17 +116,17 @@ public:
          IDBTransaction* aTransaction,
          IDBIndex* aIndex,
          Direction aDirection,
          const Key& aRangeKey,
          const nsACString& aContinueQuery,
          const nsACString& aContinueToQuery,
          const Key& aKey,
          const Key& aObjectKey,
-         StructuredCloneReadInfo& aCloneReadInfo);
+         StructuredCloneReadInfo&& aCloneReadInfo);
 
   IDBTransaction* Transaction() const
   {
     return mTransaction;
   }
 
   IDBRequest* Request() const
   {
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -31,16 +31,17 @@
 #include "ipc/IndexedDBParent.h"
 
 #include "IndexedDatabaseInlines.h"
 
 USING_INDEXEDDB_NAMESPACE
 using namespace mozilla::dom;
 using namespace mozilla::dom::indexedDB::ipc;
 using mozilla::ErrorResult;
+using mozilla::Move;
 
 namespace {
 
 class IndexHelper : public AsyncConnectionHelper
 {
 public:
   IndexHelper(IDBTransaction* aTransaction,
               IDBRequest* aRequest,
@@ -773,17 +774,17 @@ IDBIndex::OpenCursorFromChildProcess(
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   cloneInfo.mFiles.SwapElements(aBlobs);
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::Create(aRequest, mObjectStore->Transaction(), this, direction,
                       Key(), EmptyCString(), EmptyCString(), aKey, aObjectKey,
-                      cloneInfo);
+                      Move(cloneInfo));
   IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!");
 
   cursor.forget(_retval);
   return NS_OK;
 }
 
@@ -2299,17 +2300,17 @@ OpenCursorHelper::EnsureCursor()
 
   NS_ASSERTION(mSerializedCloneReadInfo.data &&
                mSerializedCloneReadInfo.dataLength,
                "Shouldn't be possible!");
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey,
                       mContinueQuery, mContinueToQuery, mKey, mObjectKey,
-                      mCloneReadInfo);
+                      Move(mCloneReadInfo));
   IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!");
 
   mCursor.swap(cursor);
   return NS_OK;
 }
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -54,16 +54,17 @@
 
 USING_INDEXEDDB_NAMESPACE
 using namespace mozilla::dom;
 using namespace mozilla::dom::indexedDB::ipc;
 using mozilla::dom::quota::FileOutputStream;
 using mozilla::ErrorResult;
 using mozilla::fallible_t;
 using mozilla::LittleEndian;
+using mozilla::Move;
 using mozilla::NativeEndian;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 struct FileHandleData
 {
   nsString type;
   nsString name;
@@ -154,24 +155,25 @@ protected:
 };
 
 class AddHelper : public ObjectStoreHelper
 {
 public:
   AddHelper(IDBTransaction* aTransaction,
             IDBRequest* aRequest,
             IDBObjectStore* aObjectStore,
-            StructuredCloneWriteInfo& aCloneWriteInfo,
+            StructuredCloneWriteInfo&& aCloneWriteInfo,
             const Key& aKey,
             bool aOverwrite,
             nsTArray<IndexUpdateInfo>& aIndexUpdateInfo)
-  : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), mKey(aKey),
+  : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
+    mCloneWriteInfo(Move(aCloneWriteInfo)),
+    mKey(aKey),
     mOverwrite(aOverwrite)
   {
-    mCloneWriteInfo.Swap(aCloneWriteInfo);
     mIndexUpdateInfo.SwapElements(aIndexUpdateInfo);
   }
 
   ~AddHelper()
   {
     IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo);
   }
 
@@ -1936,17 +1938,17 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   if (!request) {
     IDB_WARNING("Failed to generate request!");
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     return nullptr;
   }
 
   nsRefPtr<AddHelper> helper =
-    new AddHelper(mTransaction, request, this, cloneWriteInfo, key,
+    new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key,
                   aOverwrite, updateInfo);
 
   nsresult rv = helper->DispatchToTransactionPool();
   if (NS_FAILED(rv)) {
     IDB_WARNING("Failed to dispatch!");
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     return nullptr;
   }
@@ -2048,18 +2050,18 @@ IDBObjectStore::AddOrPutInternal(
     }
   }
 
   Key key(aKey);
 
   nsTArray<IndexUpdateInfo> updateInfo(aUpdateInfoArray);
 
   nsRefPtr<AddHelper> helper =
-    new AddHelper(mTransaction, request, this, cloneWriteInfo, key, aOverwrite,
-                  updateInfo);
+    new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key,
+                  aOverwrite, updateInfo);
 
   nsresult rv = helper->DispatchToTransactionPool();
   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
 #ifdef IDB_PROFILER_USE_MARKS
   if (aOverwrite) {
     IDB_PROFILER_MARK("IndexedDB Request %llu: "
                       "database(%s).transaction(%s).objectStore(%s).%s(%s)",
@@ -2398,17 +2400,17 @@ IDBObjectStore::OpenCursorFromChildProce
     IDB_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   cloneInfo.mFiles.SwapElements(aBlobs);
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::Create(aRequest, mTransaction, this, direction, Key(),
-                      EmptyCString(), EmptyCString(), aKey, cloneInfo);
+                      EmptyCString(), EmptyCString(), aKey, Move(cloneInfo));
   IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!");
 
   cursor.forget(_retval);
   return NS_OK;
 }
 
@@ -3909,17 +3911,17 @@ OpenCursorHelper::EnsureCursor()
 
   NS_ASSERTION(mSerializedCloneReadInfo.data &&
                mSerializedCloneReadInfo.dataLength,
                "Shouldn't be possible!");
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
                       mRangeKey, mContinueQuery, mContinueToQuery, mKey,
-                      mCloneReadInfo);
+                      Move(mCloneReadInfo));
   IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!");
 
   mCursor.swap(cursor);
   return NS_OK;
 }
 
--- a/dom/indexedDB/IndexedDatabase.h
+++ b/dom/indexedDB/IndexedDatabase.h
@@ -31,24 +31,16 @@
 class nsIDOMBlob;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class FileInfo;
 class IDBDatabase;
 class IDBTransaction;
 
-template <class T>
-void SwapData(T& aData1, T& aData2)
-{
-  T temp = aData2;
-  aData2 = aData1;
-  aData1 = temp;
-}
-
 struct StructuredCloneFile
 {
   bool operator==(const StructuredCloneFile& aOther) const
   {
     return this->mFile == aOther.mFile &&
            this->mFileInfo == aOther.mFileInfo &&
            this->mInputStream == aOther.mInputStream;
   }
@@ -60,22 +52,18 @@ struct StructuredCloneFile
 
 struct SerializedStructuredCloneReadInfo;
 
 struct StructuredCloneReadInfo
 {
   // In IndexedDatabaseInlines.h
   inline StructuredCloneReadInfo();
 
-  void Swap(StructuredCloneReadInfo& aCloneReadInfo)
-  {
-    mCloneBuffer.swap(aCloneReadInfo.mCloneBuffer);
-    mFiles.SwapElements(aCloneReadInfo.mFiles);
-    SwapData(mDatabase, aCloneReadInfo.mDatabase);
-  }
+  inline StructuredCloneReadInfo&
+  operator=(StructuredCloneReadInfo&& aCloneReadInfo);
 
   // In IndexedDatabaseInlines.h
   inline bool
   SetFromSerialized(const SerializedStructuredCloneReadInfo& aOther);
 
   JSAutoStructuredCloneBuffer mCloneBuffer;
   nsTArray<StructuredCloneFile> mFiles;
   IDBDatabase* mDatabase;
@@ -108,24 +96,17 @@ struct SerializedStructuredCloneReadInfo
 };
 
 struct SerializedStructuredCloneWriteInfo;
 
 struct StructuredCloneWriteInfo
 {
   // In IndexedDatabaseInlines.h
   inline StructuredCloneWriteInfo();
-
-  void Swap(StructuredCloneWriteInfo& aCloneWriteInfo)
-  {
-    mCloneBuffer.swap(aCloneWriteInfo.mCloneBuffer);
-    mFiles.SwapElements(aCloneWriteInfo.mFiles);
-    SwapData(mTransaction, aCloneWriteInfo.mTransaction);
-    SwapData(mOffsetToKeyProp, aCloneWriteInfo.mOffsetToKeyProp);
-  }
+  inline StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo);
 
   bool operator==(const StructuredCloneWriteInfo& aOther) const
   {
     return this->mCloneBuffer.nbytes() == aOther.mCloneBuffer.nbytes() &&
            this->mCloneBuffer.data() == aOther.mCloneBuffer.data() &&
            this->mFiles == aOther.mFiles &&
            this->mTransaction == aOther.mTransaction &&
            this->mOffsetToKeyProp == aOther.mOffsetToKeyProp;
--- a/dom/indexedDB/IndexedDatabaseInlines.h
+++ b/dom/indexedDB/IndexedDatabaseInlines.h
@@ -16,16 +16,28 @@ BEGIN_INDEXEDDB_NAMESPACE
 inline
 StructuredCloneWriteInfo::StructuredCloneWriteInfo()
 : mTransaction(nullptr),
   mOffsetToKeyProp(0)
 {
 }
 
 inline
+StructuredCloneWriteInfo::StructuredCloneWriteInfo(
+                                    StructuredCloneWriteInfo&& aCloneWriteInfo)
+: mCloneBuffer(Move(aCloneWriteInfo.mCloneBuffer))
+, mTransaction(aCloneWriteInfo.mTransaction)
+, mOffsetToKeyProp(aCloneWriteInfo.mOffsetToKeyProp)
+{
+  mFiles.SwapElements(aCloneWriteInfo.mFiles);
+  aCloneWriteInfo.mTransaction = nullptr;
+  aCloneWriteInfo.mOffsetToKeyProp = 0;
+}
+
+inline
 bool
 StructuredCloneWriteInfo::SetFromSerialized(
                                const SerializedStructuredCloneWriteInfo& aOther)
 {
   if (!aOther.dataLength) {
     mCloneBuffer.clear();
   }
   else if (!mCloneBuffer.copy(aOther.data, aOther.dataLength)) {
@@ -38,16 +50,29 @@ StructuredCloneWriteInfo::SetFromSeriali
 }
 
 inline
 StructuredCloneReadInfo::StructuredCloneReadInfo()
 : mDatabase(nullptr)
 {
 }
 
+inline StructuredCloneReadInfo&
+StructuredCloneReadInfo::operator=(StructuredCloneReadInfo&& aCloneReadInfo)
+{
+  MOZ_ASSERT(&aCloneReadInfo != this);
+
+  mCloneBuffer = Move(aCloneReadInfo.mCloneBuffer);
+  mFiles.Clear();
+  mFiles.SwapElements(aCloneReadInfo.mFiles);
+  mDatabase = aCloneReadInfo.mDatabase;
+  aCloneReadInfo.mDatabase = nullptr;
+  return *this;
+}
+
 inline
 bool
 StructuredCloneReadInfo::SetFromSerialized(
                                 const SerializedStructuredCloneReadInfo& aOther)
 {
   if (aOther.dataLength &&
       !mCloneBuffer.copy(aOther.data, aOther.dataLength)) {
     return false;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -49,16 +49,17 @@
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsJSEnvironment.h"
 #include "SandboxHal.h"
 #include "nsDebugImpl.h"
 #include "nsHashPropertyBag.h"
 #include "nsLayoutStylesheetCache.h"
 #include "nsIJSRuntimeService.h"
+#include "nsThreadManager.h"
 
 #include "IHistory.h"
 #include "nsNetUtil.h"
 
 #include "base/message_loop.h"
 #include "base/process_util.h"
 #include "base/task.h"
 
@@ -335,16 +336,24 @@ ContentChild::Init(MessageLoop* aIOLoop,
 #endif
 
 #ifdef MOZ_NUWA_PROCESS
     SetTransport(aChannel);
 #endif
 
     NS_ASSERTION(!sSingleton, "only one ContentChild per child");
 
+    // Once we start sending IPC messages, we need the thread manager to be
+    // initialized so we can deal with the responses. Do that here before we
+    // try to construct the crash reporter.
+    nsresult rv = nsThreadManager::get()->Init();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return false;
+    }
+
     Open(aChannel, aParentHandle, aIOLoop);
     sSingleton = this;
 
 #ifdef MOZ_CRASHREPORTER
     SendPCrashReporterConstructor(CrashReporter::CurrentThreadId(),
                                   XRE_GetProcessType());
 #endif
 
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -101,16 +101,17 @@ LOCAL_INCLUDES += [
     '/hal/sandbox',
     '/js/ipc',
     '/layout/base',
     '/netwerk/base/src',
     '/toolkit/xre',
     '/uriloader/exthandler',
     '/widget/xpwidgets',
     '/xpcom/base',
+    '/xpcom/threads',
 ]
 
 DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG['BIN_SUFFIX']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gonk', 'qt'):
     DEFINES['MOZ_ENABLE_FREETYPE'] = True
 
 for var in ('MOZ_PERMISSIONS', 'MOZ_CHILD_PERMISSIONS'):
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -93,17 +93,18 @@ static nsresult CompareDictionaries(JSCo
   JS::Rooted<JSObject*> b(aCx, &bval.toObject());
 
   // Iterate over each property in A, and check if it is in B
 
   JS::AutoIdArray props(aCx, JS_Enumerate(aCx, a));
 
   for (size_t i = 0; i < props.length(); i++) {
     JS::Rooted<JS::Value> bprop(aCx);
-    if (!JS_GetPropertyById(aCx, b, props[i], &bprop)) {
+    JS::Rooted<jsid> id(aCx, props[i]);
+    if (!JS_GetPropertyById(aCx, b, id, &bprop)) {
       LOG(("Error parsing dictionary!\n"));
       return NS_ERROR_UNEXPECTED;
     }
     if (bprop.isUndefined()) {
       // Unknown property found in A. Bail with name
       JS::Rooted<JS::Value> nameval(aCx);
       bool success = JS_IdToValue(aCx, props[i], &nameval);
       NS_ENSURE_TRUE(success, NS_ERROR_UNEXPECTED);
--- a/dom/network/src/TCPSocketParent.cpp
+++ b/dom/network/src/TCPSocketParent.cpp
@@ -212,17 +212,17 @@ TCPSocketParent::SendEvent(const nsAStri
       return NS_ERROR_OUT_OF_MEMORY;
     }
     data = SendableData(str);
 
   } else if (aDataVal.isUndefined() || aDataVal.isNull()) {
     data = mozilla::void_t();
 
   } else if (aDataVal.isObject()) {
-    JSObject* obj = &aDataVal.toObject();
+    JS::Rooted<JSObject *> obj(aCx, &aDataVal.toObject());
     if (JS_IsArrayBufferObject(obj)) {
       uint32_t nbytes = JS_GetArrayBufferByteLength(obj);
       uint8_t* buffer = JS_GetArrayBufferData(obj);
       if (!buffer) {
         FireInteralError(this, __LINE__);
         return NS_ERROR_OUT_OF_MEMORY;
       }
       FallibleTArray<uint8_t> fallibleArr;
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -552,21 +552,23 @@ nsJSObjWrapper::NP_Invalidate(NPObject *
     nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
     sJSObjWrappers.remove(key);
 
     jsnpobj->ClearJSObject();
   }
 }
 
 static bool
-GetProperty(JSContext *cx, JSObject *obj, NPIdentifier id, JS::MutableHandle<JS::Value> rval)
+GetProperty(JSContext *cx, JSObject *objArg, NPIdentifier npid, JS::MutableHandle<JS::Value> rval)
 {
-  NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id),
+  NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
                "id must be either string or int!\n");
-  return ::JS_GetPropertyById(cx, obj, NPIdentifierToJSId(id), rval);
+  JS::Rooted<JSObject *> obj(cx, objArg);
+  JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+  return ::JS_GetPropertyById(cx, obj, id, rval);
 }
 
 // static
 bool
 nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier id)
 {
   NPP npp = NPPStack::Peek();
   JSContext *cx = GetJSContext(npp);
--- a/dom/system/gonk/AutoMounterSetting.cpp
+++ b/dom/system/gonk/AutoMounterSetting.cpp
@@ -237,31 +237,31 @@ AutoMounterSetting::Observe(nsISupports*
 
   mozilla::AutoSafeJSContext cx;
   nsDependentString dataStr(aData);
   JS::Rooted<JS::Value> val(cx);
   if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
       !val.isObject()) {
     return NS_OK;
   }
-  JSObject& obj(val.toObject());
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
   JS::Rooted<JS::Value> key(cx);
-  if (!JS_GetProperty(cx, &obj, "key", &key) ||
+  if (!JS_GetProperty(cx, obj, "key", &key) ||
       !key.isString()) {
     return NS_OK;
   }
 
   JSString *jsKey = JS::ToString(cx, key);
   nsDependentJSString keyStr;
   if (!keyStr.init(cx, jsKey)) {
     return NS_OK;
   }
 
   JS::Rooted<JS::Value> value(cx);
-  if (!JS_GetProperty(cx, &obj, "value", &value)) {
+  if (!JS_GetProperty(cx, obj, "value", &value)) {
     return NS_OK;
   }
 
   // Check for ums.mode changes
   if (keyStr.EqualsLiteral(UMS_MODE)) {
     if (!value.isInt32()) {
       return NS_OK;
     }
--- a/dom/system/gonk/TimeZoneSettingObserver.cpp
+++ b/dom/system/gonk/TimeZoneSettingObserver.cpp
@@ -204,31 +204,31 @@ TimeZoneSettingObserver::Observe(nsISupp
   nsDependentString dataStr(aData);
   JS::Rooted<JS::Value> val(cx);
   if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
       !val.isObject()) {
     return NS_OK;
   }
 
   // Get the key, which should be the JS string "time.timezone".
-  JSObject &obj(val.toObject());
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
   JS::Rooted<JS::Value> key(cx);
-  if (!JS_GetProperty(cx, &obj, "key", &key) ||
+  if (!JS_GetProperty(cx, obj, "key", &key) ||
       !key.isString()) {
     return NS_OK;
   }
   bool match;
   if (!JS_StringEqualsAscii(cx, key.toString(), TIME_TIMEZONE, &match) ||
       !match) {
     return NS_OK;
   }
 
   // Get the value, which should be a JS string like "America/Chicago".
   JS::Rooted<JS::Value> value(cx);
-  if (!JS_GetProperty(cx, &obj, "value", &value) ||
+  if (!JS_GetProperty(cx, obj, "value", &value) ||
       !value.isString()) {
     return NS_OK;
   }
 
   // Set the system timezone.
   return SetTimeZone(value, cx);
 }
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -954,23 +954,24 @@ class MessageEventRunnable MOZ_FINAL : p
   JSAutoStructuredCloneBuffer mBuffer;
   nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
   uint64_t mMessagePortSerial;
   bool mToMessagePort;
 
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
                        TargetAndBusyBehavior aBehavior,
-                       JSAutoStructuredCloneBuffer& aData,
+                       JSAutoStructuredCloneBuffer&& aData,
                        nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                        bool aToMessagePort, uint64_t aMessagePortSerial)
-  : WorkerRunnable(aWorkerPrivate, aBehavior),
-    mMessagePortSerial(aMessagePortSerial), mToMessagePort(aToMessagePort)
+  : WorkerRunnable(aWorkerPrivate, aBehavior)
+  , mBuffer(Move(aData))
+  , mMessagePortSerial(aMessagePortSerial)
+  , mToMessagePort(aToMessagePort)
   {
-    mBuffer.swap(aData);
     mClonedObjects.SwapElements(aClonedObjects);
   }
 
   bool
   DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                    nsDOMEventTargetHelper* aTarget, bool aIsMainThread)
   {
     // Release reference to objects that were AddRef'd for
@@ -1021,17 +1022,17 @@ private:
       if (!aWorkerPrivate->IsAcceptingEvents()) {
         return true;
       }
 
       if (mToMessagePort) {
         return
           aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
                                                             mMessagePortSerial,
-                                                            mBuffer,
+                                                            Move(mBuffer),
                                                             mClonedObjects);
       }
 
       if (aWorkerPrivate->IsSuspended()) {
         aWorkerPrivate->QueueRunnable(this);
         return true;
       }
 
@@ -1674,17 +1675,17 @@ public:
     return true;
   }
 };
 
 class OfflineStatusChangeRunnable : public WorkerRunnable
 {
 public:
   OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
-    : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
       mIsOffline(aIsOffline)
   {
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     aWorkerPrivate->OfflineStatusChangeEventInternal(aCx, mIsOffline);
@@ -2783,17 +2784,17 @@ WorkerPrivateParent<Derived>::PostMessag
   if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(ParentAsWorkerPrivate(),
                              WorkerRunnable::WorkerThreadModifyBusyCount,
-                             buffer, clonedObjects, aToMessagePort,
+                             Move(buffer), clonedObjects, aToMessagePort,
                              aMessagePortSerial);
   if (!runnable->Dispatch(aCx)) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
 }
 
 template <class Derived>
 void
@@ -2809,23 +2810,22 @@ WorkerPrivateParent<Derived>::PostMessag
   PostMessageInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial,
                       aRv);
 }
 
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
                                 JSContext* aCx, uint64_t aMessagePortSerial,
-                                JSAutoStructuredCloneBuffer& aBuffer,
+                                JSAutoStructuredCloneBuffer&& aBuffer,
                                 nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
 {
   AssertIsOnMainThread();
 
-  JSAutoStructuredCloneBuffer buffer;
-  buffer.swap(aBuffer);
+  JSAutoStructuredCloneBuffer buffer(Move(aBuffer));
 
   nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
   clonedObjects.SwapElements(aClonedObjects);
 
   SharedWorker* sharedWorker;
   if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) {
     // SharedWorker has already been unregistered?
     return true;
@@ -4988,17 +4988,17 @@ WorkerPrivate::PostMessageToParentIntern
   if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
     aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
     return;
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(this,
                              WorkerRunnable::ParentThreadUnchangedBusyCount,
-                             buffer, clonedObjects, aToMessagePort,
+                             Move(buffer), clonedObjects, aToMessagePort,
                              aMessagePortSerial);
   if (!runnable->Dispatch(aCx)) {
     aRv = NS_ERROR_FAILURE;
   }
 }
 
 void
 WorkerPrivate::PostMessageToParentMessagePort(
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -372,17 +372,17 @@ public:
                            JS::Handle<JS::Value> aMessage,
                            const Optional<Sequence<JS::Value> >& aTransferable,
                            ErrorResult& aRv);
 
   bool
   DispatchMessageEventToMessagePort(
                                JSContext* aCx,
                                uint64_t aMessagePortSerial,
-                               JSAutoStructuredCloneBuffer& aBuffer,
+                               JSAutoStructuredCloneBuffer&& aBuffer,
                                nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects);
 
   uint64_t
   GetInnerWindowId();
 
   void
   UpdateJSContextOptions(JSContext* aCx, const JS::ContextOptions& aChromeOptions,
                          const JS::ContextOptions& aContentOptions);
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -787,24 +787,25 @@ class SendRunnable MOZ_FINAL : public Wo
   nsString mStringBody;
   JSAutoStructuredCloneBuffer mBody;
   nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   bool mHasUploadListeners;
 
 public:
   SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
-               const nsAString& aStringBody, JSAutoStructuredCloneBuffer& aBody,
-               nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
+               const nsAString& aStringBody, JSAutoStructuredCloneBuffer&& aBody,
+               nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects,
                nsIEventTarget* aSyncLoopTarget, bool aHasUploadListeners)
-  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
-    mStringBody(aStringBody), mSyncLoopTarget(aSyncLoopTarget),
-    mHasUploadListeners(aHasUploadListeners)
+  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
+  , mStringBody(aStringBody)
+  , mBody(Move(aBody))
+  , mSyncLoopTarget(aSyncLoopTarget)
+  , mHasUploadListeners(aHasUploadListeners)
   {
-    mBody.swap(aBody);
     mClonedObjects.SwapElements(aClonedObjects);
   }
 
 private:
   ~SendRunnable()
   { }
 
   virtual nsresult
@@ -1263,18 +1264,17 @@ EventRunnable::WorkerRun(JSContext* aCx,
   }
   else {
     state->mResponseResult = mResponseResult;
 
     if (NS_SUCCEEDED(mResponseResult)) {
       if (mResponseBuffer.data()) {
         MOZ_ASSERT(JSVAL_IS_VOID(mResponse));
 
-        JSAutoStructuredCloneBuffer responseBuffer;
-        mResponseBuffer.swap(responseBuffer);
+        JSAutoStructuredCloneBuffer responseBuffer(Move(mResponseBuffer));
 
         JSStructuredCloneCallbacks* callbacks =
           aWorkerPrivate->IsChromeWorker() ?
           ChromeWorkerStructuredCloneCallbacks(false) :
           WorkerStructuredCloneCallbacks(false);
 
         nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
         clonedObjects.SwapElements(mClonedObjects);
@@ -1761,17 +1761,17 @@ XMLHttpRequest::Unpin()
 
   mRooted = false;
 
   NS_RELEASE_THIS();
 }
 
 void
 XMLHttpRequest::SendInternal(const nsAString& aStringBody,
-                             JSAutoStructuredCloneBuffer& aBody,
+                             JSAutoStructuredCloneBuffer&& aBody,
                              nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                              ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false;
 
   MaybePin(aRv);
@@ -1789,17 +1789,17 @@ XMLHttpRequest::SendInternal(const nsASt
     syncLoopTarget = autoSyncLoop.ref().EventTarget();
   }
 
   mProxy->mOuterChannelId++;
 
   JSContext* cx = mWorkerPrivate->GetJSContext();
 
   nsRefPtr<SendRunnable> runnable =
-    new SendRunnable(mWorkerPrivate, mProxy, aStringBody, aBody,
+    new SendRunnable(mWorkerPrivate, mProxy, aStringBody, Move(aBody),
                      aClonedObjects, syncLoopTarget, hasUploadListeners);
   if (!runnable->Dispatch(cx)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (!isSyncXHR)  {
     autoUnpin.Clear();
@@ -2006,17 +2006,17 @@ XMLHttpRequest::Send(ErrorResult& aRv)
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // Nothing to clone.
   JSAutoStructuredCloneBuffer buffer;
   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
 
-  SendInternal(NullString(), buffer, clonedObjects, aRv);
+  SendInternal(NullString(), Move(buffer), clonedObjects, aRv);
 }
 
 void
 XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
@@ -2028,17 +2028,17 @@ XMLHttpRequest::Send(const nsAString& aB
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // Nothing to clone.
   JSAutoStructuredCloneBuffer buffer;
   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
 
-  SendInternal(aBody, buffer, clonedObjects, aRv);
+  SendInternal(aBody, Move(buffer), clonedObjects, aRv);
 }
 
 void
 XMLHttpRequest::Send(JS::Handle<JSObject*> aBody, ErrorResult& aRv)
 {
   JSContext* cx = mWorkerPrivate->GetJSContext();
 
   MOZ_ASSERT(aBody);
@@ -2078,17 +2078,17 @@ XMLHttpRequest::Send(JS::Handle<JSObject
   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
 
   JSAutoStructuredCloneBuffer buffer;
   if (!buffer.write(cx, valToClone, callbacks, &clonedObjects)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
-  SendInternal(EmptyString(), buffer, clonedObjects, aRv);
+  SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
 }
 
 void
 XMLHttpRequest::Send(const ArrayBuffer& aBody, ErrorResult& aRv)
 {
   JS::Rooted<JSObject*> obj(mWorkerPrivate->GetJSContext(), aBody.Obj());
   return Send(obj, aRv);
 }
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -280,16 +280,16 @@ private:
   bool
   SendInProgress() const
   {
     return mRooted;
   }
 
   void
   SendInternal(const nsAString& aStringBody,
-               JSAutoStructuredCloneBuffer& aBody,
+               JSAutoStructuredCloneBuffer&& aBody,
                nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                ErrorResult& aRv);
 };
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_xmlhttprequest_h__
--- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
+++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
@@ -1047,20 +1047,22 @@ cairo_int_status_t
 				      &size,
 				      &tableContext,
 				      &exists);
 
     if (!exists) {
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
+    if (buffer && *length && (UINT32)offset < size) {
+        size = MIN(size - (UINT32)offset, *length);
+        memcpy(buffer, (const char*)data + offset, size);
+    }
     *length = size;
-    if (buffer) {
-	memcpy(buffer, data, size);
-    }
+
     if (tableContext) {
 	face->dwriteface->ReleaseFontTable(tableContext);
     }
     return (cairo_int_status_t)CAIRO_STATUS_SUCCESS;
 }
 
 // WIN32 Helper Functions
 cairo_font_face_t*
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -95,52 +95,65 @@ public:
     {}
 
     TimeStamp mFrameTime;
     gfx::Point mPoint;
   };
   std::vector<VelocityData> mData;
 };
 
+static gfx::Point GetScrollData(Layer* aLayer) {
+  gfx::Matrix matrix;
+  if (aLayer->GetLocalTransform().Is2D(&matrix)) {
+    return matrix.GetTranslation();
+  }
+
+  gfx::Point origin;
+  return origin;
+}
+
+static LayerVelocityUserData* GetVelocityData(Layer* aLayer) {
+  static char sLayerVelocityUserDataKey;
+  void* key = reinterpret_cast<void*>(&sLayerVelocityUserDataKey);
+  if (!aLayer->HasUserData(key)) {
+    LayerVelocityUserData* newData = new LayerVelocityUserData();
+    aLayer->SetUserData(key, newData);
+  }
+
+  return static_cast<LayerVelocityUserData*>(aLayer->GetUserData(key));
+}
+
 static void DrawVelGraph(const nsIntRect& aClipRect,
                          LayerManagerComposite* aManager,
                          Layer* aLayer) {
-  static char sLayerVelocityUserDataKey;
   Compositor* compositor = aManager->GetCompositor();
   gfx::Rect clipRect(aClipRect.x, aClipRect.y,
                      aClipRect.width, aClipRect.height);
 
   TimeStamp now = TimeStamp::Now();
-
-  void* key = reinterpret_cast<void*>(&sLayerVelocityUserDataKey);
-  if (!aLayer->HasUserData(key)) {
-    aLayer->SetUserData(key, new LayerVelocityUserData());
-  }
-
-  LayerVelocityUserData* velocityData =
-    static_cast<LayerVelocityUserData*>(aLayer->GetUserData(key));
+  LayerVelocityUserData* velocityData = GetVelocityData(aLayer);
 
   if (velocityData->mData.size() >= 1 &&
     now > velocityData->mData[velocityData->mData.size() - 1].mFrameTime +
       TimeDuration::FromMilliseconds(200)) {
     // clear stale data
     velocityData->mData.clear();
   }
 
-  nsIntPoint scrollOffset =
-    aLayer->GetEffectiveVisibleRegion().GetBounds().TopLeft();
+  const gfx::Point layerTransform = GetScrollData(aLayer);
   velocityData->mData.push_back(
-    LayerVelocityUserData::VelocityData(now, scrollOffset.x, scrollOffset.y));
+    LayerVelocityUserData::VelocityData(now,
+      static_cast<int>(layerTransform.x), static_cast<int>(layerTransform.y)));
 
-
+  // TODO: dump to file
   // XXX: Uncomment these lines to enable ScrollGraph logging. This is
   //      useful for HVGA phones or to output the data to accurate
   //      graphing software.
-  //printf_stderr("ScrollGraph (%p): %i, %i\n",
-  //  aLayer, scrollOffset.x, scrollOffset.y);
+  // printf_stderr("ScrollGraph (%p): %f, %f\n",
+  // aLayer, layerTransform.x, layerTransform.y);
 
   // Keep a circular buffer of 100.
   size_t circularBufferSize = 100;
   if (velocityData->mData.size() > circularBufferSize) {
     velocityData->mData.erase(velocityData->mData.begin());
   }
 
   if (velocityData->mData.size() == 1) {
@@ -190,17 +203,16 @@ static void DrawVelGraph(const nsIntRect
                    (p1.y - p2.y) * (p1.y - p2.y));
     graph.push_back(
       gfx::Point(bounds.x + graphRect.width / circularBufferSize * i,
                  graphBounds.y + graphRect.height - vel/yScaleFactor));
   }
 
   compositor->DrawLines(graph, clipRect, gfx::Color(0,1,0,1),
                         opacity, transform);
-
 }
 
 template<class ContainerT> void
 ContainerRender(ContainerT* aContainer,
                 LayerManagerComposite* aManager,
                 const nsIntRect& aClipRect)
 {
   /**
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -122,18 +122,17 @@ gfxPlatformMac::OptimizeImage(gfxImageSu
         isurf = new gfxImageSurface (surfaceSize, format);
         if (!isurf->CopyFrom (aSurface)) {
             // don't even bother doing anything more
             nsRefPtr<gfxASurface> ret = aSurface;
             return ret.forget();
         }
     }
 
-    nsRefPtr<gfxASurface> ret = new gfxQuartzImageSurface(isurf);
-    return ret.forget();
+    return nullptr;
 }
 
 TemporaryRef<ScaledFont>
 gfxPlatformMac::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
 {
     gfxMacFont *font = static_cast<gfxMacFont*>(aFont);
     return font->GetScaledFont(aTarget);
 }
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -286,17 +286,16 @@ nsresult imgFrame::Optimize()
       mOptSurface = mWinSurface;
     }
   }
 #endif
 
 #ifdef XP_MACOSX
   if (mQuartzSurface) {
     mQuartzSurface->Flush();
-    mOptSurface = mQuartzSurface;
   }
 #endif
 
   if (mOptSurface == nullptr)
     mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat);
 
   if (mOptSurface) {
     mImageSurface = nullptr;
new file mode 100644
--- /dev/null
+++ b/intl/unicharutil/util/ICUUtils.cpp
@@ -0,0 +1,256 @@
+/* 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/. */
+
+#ifdef MOZILLA_INTERNAL_API
+#ifdef ENABLE_INTL_API
+
+#include "ICUUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsIToolkitChromeRegistry.h"
+#include "nsStringGlue.h"
+#include "unicode/uloc.h"
+#include "unicode/unum.h"
+
+using namespace mozilla;
+
+static bool gLocaleNumberGroupingEnabled;
+static const char LOCALE_NUMBER_GROUPING_PREF_STR[] = "dom.forms.number.grouping";
+
+static bool
+LocaleNumberGroupingIsEnabled()
+{
+  static bool sInitialized = false;
+
+  if (!sInitialized) {
+    /* check and register ourselves with the pref */
+    Preferences::AddBoolVarCache(&gLocaleNumberGroupingEnabled,
+                                 LOCALE_NUMBER_GROUPING_PREF_STR,
+                                 true);
+    sInitialized = true;
+  }
+
+  return gLocaleNumberGroupingEnabled;
+}
+
+void
+ICUUtils::LanguageTagIterForContent::GetNext(nsACString& aBCP47LangTag)
+{
+  if (mCurrentFallbackIndex < 0) {
+    mCurrentFallbackIndex = 0;
+    // Try the language specified by a 'lang'/'xml:lang' attribute on mContent
+    // or any ancestor, if such an attribute is specified:
+    nsAutoString lang;
+    mContent->GetLang(lang);
+    if (!lang.IsEmpty()) {
+      aBCP47LangTag = NS_ConvertUTF16toUTF8(lang);
+      return;
+    }
+  }
+
+  if (mCurrentFallbackIndex < 1) {
+    mCurrentFallbackIndex = 1;
+    // Else try the language specified by any Content-Language HTTP header or
+    // pragma directive:
+    nsIDocument* doc = mContent->OwnerDoc();
+    nsAutoString lang;
+    doc->GetContentLanguage(lang);
+    if (!lang.IsEmpty()) {
+      aBCP47LangTag = NS_ConvertUTF16toUTF8(lang);
+      return;
+    }
+  }
+
+  if (mCurrentFallbackIndex < 2) {
+    mCurrentFallbackIndex = 2;
+    // Else try the user-agent's locale:
+    nsCOMPtr<nsIToolkitChromeRegistry> cr =
+      mozilla::services::GetToolkitChromeRegistryService();
+    nsAutoCString uaLangTag;
+    if (cr) {
+      cr->GetSelectedLocale(NS_LITERAL_CSTRING("global"), uaLangTag);
+    }
+    if (!uaLangTag.IsEmpty()) {
+      aBCP47LangTag = uaLangTag;
+      return;
+    }
+  }
+
+  // TODO: Probably not worth it, but maybe have a fourth fallback to using
+  // the OS locale?
+
+  aBCP47LangTag.Truncate(); // Signal iterator exhausted
+}
+
+/* static */ bool
+ICUUtils::LocalizeNumber(double aValue,
+                         LanguageTagIterForContent& aLangTags,
+                         nsAString& aLocalizedValue)
+{
+  MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
+
+  static const int32_t kBufferSize = 256;
+
+  UChar buffer[kBufferSize];
+
+  nsAutoCString langTag;
+  aLangTags.GetNext(langTag);
+  while (!langTag.IsEmpty()) {
+    UErrorCode status = U_ZERO_ERROR;
+    AutoCloseUNumberFormat format(unum_open(UNUM_DECIMAL, nullptr, 0,
+                                            langTag.get(), nullptr, &status));
+    unum_setAttribute(format, UNUM_GROUPING_USED,
+                      LocaleNumberGroupingIsEnabled());
+    int32_t length = unum_formatDouble(format, aValue, buffer, kBufferSize,
+                                       nullptr, &status);
+    NS_ASSERTION(length < kBufferSize &&
+                 status != U_BUFFER_OVERFLOW_ERROR &&
+                 status != U_STRING_NOT_TERMINATED_WARNING,
+                 "Need a bigger buffer?!");
+    if (U_SUCCESS(status)) {
+      ICUUtils::AssignUCharArrayToString(buffer, length, aLocalizedValue);
+      return true;
+    }
+    aLangTags.GetNext(langTag);
+  }
+  return false;
+}
+
+/* static */ double
+ICUUtils::ParseNumber(nsAString& aValue,
+                      LanguageTagIterForContent& aLangTags)
+{
+  MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
+
+  if (aValue.IsEmpty()) {
+    return std::numeric_limits<float>::quiet_NaN();
+  }
+
+  uint32_t length = aValue.Length();
+
+  nsAutoCString langTag;
+  aLangTags.GetNext(langTag);
+  while (!langTag.IsEmpty()) {
+    UErrorCode status = U_ZERO_ERROR;
+    AutoCloseUNumberFormat format(unum_open(UNUM_DECIMAL, nullptr, 0,
+                                            langTag.get(), nullptr, &status));
+    int32_t parsePos = 0;
+    static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
+                  "Unexpected character size - the following cast is unsafe");
+    double val = unum_parseDouble(format,
+                                  (const UChar*)PromiseFlatString(aValue).get(),
+                                  length, &parsePos, &status);
+    if (U_SUCCESS(status) && parsePos == (int32_t)length) {
+      return val;
+    }
+    aLangTags.GetNext(langTag);
+  }
+  return std::numeric_limits<float>::quiet_NaN();
+}
+
+/* static */ void
+ICUUtils::AssignUCharArrayToString(UChar* aICUString,
+                                   int32_t aLength,
+                                   nsAString& aMozString)
+{
+  // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
+  // cast here.
+
+  static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
+                "Unexpected character size - the following cast is unsafe");
+
+  aMozString.Assign((const nsAString::char_type*)aICUString, aLength);
+
+  NS_ASSERTION((int32_t)aMozString.Length() == aLength, "Conversion failed");
+}
+
+#if 0
+/* static */ Locale
+ICUUtils::BCP47CodeToLocale(const nsAString& aBCP47Code)
+{
+  MOZ_ASSERT(!aBCP47Code.IsEmpty(), "Don't pass an empty BCP 47 code");
+
+  Locale locale;
+  locale.setToBogus();
+
+  // BCP47 codes are guaranteed to be ASCII, so lossy conversion is okay
+  NS_LossyConvertUTF16toASCII bcp47code(aBCP47Code);
+
+  UErrorCode status = U_ZERO_ERROR;
+  int32_t needed;
+
+  char localeID[256];
+  needed = uloc_forLanguageTag(bcp47code.get(), localeID,
+                               PR_ARRAY_SIZE(localeID) - 1, nullptr,
+                               &status);
+  MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(localeID)) - 1,
+             "Need a bigger buffer");
+  if (needed <= 0 || U_FAILURE(status)) {
+    return locale;
+  }
+
+  char lang[64];
+  needed = uloc_getLanguage(localeID, lang, PR_ARRAY_SIZE(lang) - 1,
+                            &status);
+  MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(lang)) - 1,
+             "Need a bigger buffer");
+  if (needed <= 0 || U_FAILURE(status)) {
+    return locale;
+  }
+
+  char country[64];
+  needed = uloc_getCountry(localeID, country, PR_ARRAY_SIZE(country) - 1,
+                           &status);
+  MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(country)) - 1,
+             "Need a bigger buffer");
+  if (needed > 0 && U_SUCCESS(status)) {
+    locale = Locale(lang, country);
+  }
+
+  if (locale.isBogus()) {
+    // Using the country resulted in a bogus Locale, so try with only the lang
+    locale = Locale(lang);
+  }
+
+  return locale;
+}
+
+/* static */ void
+ICUUtils::ToMozString(UnicodeString& aICUString, nsAString& aMozString)
+{
+  // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
+  // cast here.
+
+  static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
+                "Unexpected character size - the following cast is unsafe");
+
+  const nsAString::char_type* buf =
+    (const nsAString::char_type*)aICUString.getTerminatedBuffer();
+  aMozString.Assign(buf);
+
+  NS_ASSERTION(aMozString.Length() == (uint32_t)aICUString.length(),
+               "Conversion failed");
+}
+
+/* static */ void
+ICUUtils::ToICUString(nsAString& aMozString, UnicodeString& aICUString)
+{
+  // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
+  // cast here.
+
+  static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
+                "Unexpected character size - the following cast is unsafe");
+
+  aICUString.setTo((UChar*)PromiseFlatString(aMozString).get(),
+                   aMozString.Length());
+
+  NS_ASSERTION(aMozString.Length() == (uint32_t)aICUString.length(),
+               "Conversion failed");
+}
+#endif
+
+#endif /* ENABLE_INTL_API */
+#endif /* MOZILLA_INTERNAL_API */
+
new file mode 100644
--- /dev/null
+++ b/intl/unicharutil/util/ICUUtils.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef mozilla_ICUUtils_h__
+#define mozilla_ICUUtils_h__
+
+// We only build the ICU utils if we're building ICU:
+#ifdef ENABLE_INTL_API
+
+// The ICU utils implementation needs internal things like XPCOM strings and
+// nsGkAtom, so we only build when included into internal libs:
+#ifdef MOZILLA_INTERNAL_API
+
+#include "mozilla/Scoped.h"
+#include "nsStringGlue.h"
+#include "unicode/unum.h" // for UNumberFormat
+
+class nsIContent;
+
+namespace {
+  struct ScopedUNumberFormatTraits {
+    typedef UNumberFormat* type;
+    static type empty() { return nullptr; }
+    static void release(type handle) { if (handle) unum_close(handle); }
+  };
+};
+typedef mozilla::Scoped<ScopedUNumberFormatTraits> AutoCloseUNumberFormat;
+
+class ICUUtils
+{
+public:
+
+  /**
+   * This class is used to encapsulate an nsIContent object to allow lazy
+   * iteration over its primary and fallback BCP 47 language tags.
+   */
+  class LanguageTagIterForContent {
+  public:
+    LanguageTagIterForContent(nsIContent* aContent)
+      : mContent(aContent)
+      , mCurrentFallbackIndex(-1)
+    {}
+
+    /**
+     * Used to iterate over the nsIContent object's primary language tag and
+     * its fallbacks tags. The following sources of language tag information
+     * are tried in turn:
+     *
+     * 1) the "lang" of the nsIContent object (which is based on the 'lang'/
+     *    'xml:lang' attribute on itself or the nearest ancestor to have such
+     *    an attribute, if any);
+     * 2) the Content-Language HTTP pragma directive or HTTP header;
+     * 3) the configured language tag of the user-agent.
+     *
+     * Once all fallbacks have been exhausted then this function will set
+     * aBCP47LangTag to the empty string.
+     */
+    void GetNext(nsACString& aBCP47LangTag);
+
+    bool IsAtStart() const {
+      return mCurrentFallbackIndex < 0;
+    }
+
+  private:
+    nsIContent* mContent;
+    int8_t mCurrentFallbackIndex;
+  };
+
+  /**
+   * Attempts to localize aValue and return the result via the aLocalizedValue
+   * outparam. Returns true on success. Returns false on failure, in which
+   * case aLocalizedValue will be untouched.
+   */
+  static bool LocalizeNumber(double aValue,
+                             LanguageTagIterForContent& aLangTags,
+                             nsAString& aLocalizedValue);
+
+  /**
+   * Parses the localized number that is serialized in aValue using aLangTags
+   * and returns the result as a double. Returns NaN on failure.
+   */
+  static double ParseNumber(nsAString& aValue,
+                            LanguageTagIterForContent& aLangTags);
+
+  static void AssignUCharArrayToString(UChar* aICUString,
+                                       int32_t aLength,
+                                       nsAString& aMozString);
+
+#if 0
+  // Currently disabled because using C++ API doesn't play nicely with enabling
+  // system ICU.
+
+  /**
+   * Converts an IETF BCP 47 language code to an ICU Locale.
+   */
+  static Locale BCP47CodeToLocale(const nsAString& aBCP47Code);
+
+  static void ToMozString(UnicodeString& aICUString, nsAString& aMozString);
+  static void ToICUString(nsAString& aMozString, UnicodeString& aICUString);
+#endif
+};
+
+#endif /* ENABLE_INTL_API */
+#endif /* MOZILLA_INTERNAL_API */
+
+#endif /* mozilla_ICUUtils_h__ */
+
--- a/intl/unicharutil/util/Makefile.in
+++ b/intl/unicharutil/util/Makefile.in
@@ -9,13 +9,17 @@
 
 DIST_INSTALL = 1
 SDK_LIBRARY = $(LIBRARY)
 
 USE_STATIC_LIBS = 1
 
 include $(topsrcdir)/config/rules.mk
 
+ifdef ENABLE_INTL_API
+LOCAL_INCLUDES += $(MOZ_ICU_CFLAGS)
+endif
+
 ifdef _MSC_VER
 # Don't include directives about which CRT to use
 OS_COMPILE_CXXFLAGS += -Zl
 OS_COMPILE_CFLAGS += -Zl
 endif
--- a/intl/unicharutil/util/internal/Makefile.in
+++ b/intl/unicharutil/util/internal/Makefile.in
@@ -2,10 +2,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/.
 
 # This makefile builds the version of unicharutils_s static library which uses
 # internal linkage. Components that use frozen (external) linkage should use
 # unicharutil_external_s.
 
+ifdef ENABLE_INTL_API
+LOCAL_INCLUDES += $(MOZ_ICU_CFLAGS)
+endif
+
 DIST_INSTALL = 1
 MOZILLA_INTERNAL_API = 1
--- a/intl/unicharutil/util/moz.build
+++ b/intl/unicharutil/util/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # 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/.
 
 DIRS += ['internal']
 
 EXPORTS += [
+    'ICUUtils.h',
     'nsBidiUtils.h',
     'nsSpecialCasingData.h',
     'nsUnicharUtils.h',
     'nsUnicodeProperties.h',
     'nsUnicodeScriptCodes.h',
 ]
 
 include('objs.mozbuild')
--- a/intl/unicharutil/util/objs.mozbuild
+++ b/intl/unicharutil/util/objs.mozbuild
@@ -1,15 +1,22 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-intl_unicharutil_util_lcppsrcs = [
+intl_unicharutil_util_lcppsrcs = []
+
+if CONFIG['ENABLE_INTL_API']:
+    intl_unicharutil_util_lcppsrcs += [
+        'ICUUtils.cpp',
+    ]
+
+intl_unicharutil_util_lcppsrcs += [
     'nsBidiUtils.cpp',
     'nsSpecialCasingData.cpp',
     'nsUnicharUtils.cpp',
     'nsUnicodeProperties.cpp',
 ]
 
 intl_unicharutil_util_cppsrcs = [
     '%s/intl/unicharutil/util/%s' % (TOPSRCDIR, s) \
--- a/js/public/CallArgs.h
+++ b/js/public/CallArgs.h
@@ -39,17 +39,17 @@
 #include "js/Value.h"
 
 /* Typedef for native functions called by the JS VM. */
 typedef bool
 (* JSNative)(JSContext *cx, unsigned argc, JS::Value *vp);
 
 /* Typedef for native functions that may be called in parallel. */
 typedef bool
-(* JSParallelNative)(js::ForkJoinSlice *slice, unsigned argc, JS::Value *vp);
+(* JSParallelNative)(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp);
 
 /*
  * Typedef for native functions that may be called either in parallel or
  * sequential execution.
  */
 typedef bool
 (* JSThreadSafeNative)(js::ThreadSafeContext *cx, unsigned argc, JS::Value *vp);
 
@@ -58,17 +58,17 @@ typedef bool
  * a JSNative or a JSParallelNative.
  */
 template <JSThreadSafeNative threadSafeNative>
 inline bool
 JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp);
 
 template <JSThreadSafeNative threadSafeNative>
 inline bool
-JSParallelNativeThreadSafeWrapper(js::ForkJoinSlice *slice, unsigned argc, JS::Value *vp);
+JSParallelNativeThreadSafeWrapper(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp);
 
 /*
  * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
  * replacing with the global object as necessary.
  *
  * This method will go away at some point: instead use |args.thisv()|.  If the
  * value is an object, no further work is required.  If that value is |null| or
  * |undefined|, use |JS_GetGlobalForObject| to compute the global object.  If
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -89,16 +89,19 @@ class JS_PUBLIC_API(JSAutoStructuredClon
     uint64_t *data_;
     size_t nbytes_;
     uint32_t version_;
 
   public:
     JSAutoStructuredCloneBuffer()
         : data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {}
 
+    JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer &&other);
+    JSAutoStructuredCloneBuffer &operator=(JSAutoStructuredCloneBuffer &&other);
+
     ~JSAutoStructuredCloneBuffer() { clear(); }
 
     uint64_t *data() const { return data_; }
     size_t nbytes() const { return nbytes_; }
 
     void clear();
 
     // Copy some memory. It will be automatically freed by the destructor.
@@ -118,19 +121,16 @@ class JS_PUBLIC_API(JSAutoStructuredClon
               const JSStructuredCloneCallbacks *optionalCallbacks=nullptr, void *closure=nullptr);
 
     bool write(JSContext *cx, JS::HandleValue v,
                const JSStructuredCloneCallbacks *optionalCallbacks=nullptr, void *closure=nullptr);
 
     bool write(JSContext *cx, JS::HandleValue v, JS::HandleValue transferable,
                const JSStructuredCloneCallbacks *optionalCallbacks=nullptr, void *closure=nullptr);
 
-    // Swap ownership with another JSAutoStructuredCloneBuffer.
-    void swap(JSAutoStructuredCloneBuffer &other);
-
   private:
     // Copy and assignment are not supported.
     JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other);
     JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other);
 };
 
 // The range of tag values the application may use for its own custom object types.
 #define JS_SCTAG_USER_MIN  ((uint32_t) 0xFFFF8000)
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -124,17 +124,17 @@ PrintBacktrace()
     } while (0)
 
 #  define JS_OOM_POSSIBLY_FAIL_REPORT(cx) \
     do \
     { \
         if (++OOM_counter > OOM_maxAllocations) { \
             JS_OOM_EMIT_BACKTRACE();\
             js_ReportOutOfMemory(cx);\
-            return nullptr; \
+            return false; \
         } \
     } while (0)
 
 # else
 #  define JS_OOM_POSSIBLY_FAIL() do {} while(0)
 #  define JS_OOM_POSSIBLY_FAIL_REPORT(cx) do {} while(0)
 # endif /* JS_DEBUG */
 
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -16,17 +16,17 @@
 #include "js/Utility.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
-using mozilla::DoubleIsInt32;
+using mozilla::DoubleEqualsInt32;
 using mozilla::Forward;
 using mozilla::IsNaN;
 using mozilla::Move;
 using mozilla::ArrayLength;
 using JS::DoubleNaNValue;
 using JS::ForOfIterator;
 
 
@@ -779,17 +779,17 @@ HashableValue::setValue(JSContext *cx, H
         // Atomize so that hash() and operator==() are fast and infallible.
         JSString *str = AtomizeString(cx, v.toString(), DoNotInternAtom);
         if (!str)
             return false;
         value = StringValue(str);
     } else if (v.isDouble()) {
         double d = v.toDouble();
         int32_t i;
-        if (DoubleIsInt32(d, &i)) {
+        if (DoubleEqualsInt32(d, &i)) {
             // Normalize int32_t-valued doubles to int32_t for faster hashing and testing.
             value = Int32Value(i);
         } else if (IsNaN(d)) {
             // NaNs with different bits must hash and test identically.
             value = DoubleNaNValue();
         } else {
             value = v;
         }
@@ -892,17 +892,17 @@ MapIteratorObject::kind() const
     int32_t i = getSlot(KindSlot).toInt32();
     JS_ASSERT(i == MapObject::Keys || i == MapObject::Values || i == MapObject::Entries);
     return MapObject::IteratorKind(i);
 }
 
 bool
 GlobalObject::initMapIteratorProto(JSContext *cx, Handle<GlobalObject *> global)
 {
-    JSObject *base = global->getOrCreateIteratorPrototype(cx);
+    JSObject *base = GlobalObject::getOrCreateIteratorPrototype(cx, global);
     if (!base)
         return false;
     Rooted<JSObject*> proto(cx,
         NewObjectWithGivenProto(cx, &MapIteratorObject::class_, base, global));
     if (!proto)
         return false;
     proto->setSlot(MapIteratorObject::RangeSlot, PrivateValue(nullptr));
     if (!JS_DefineFunctions(cx, proto, MapIteratorObject::methods))
@@ -911,17 +911,17 @@ GlobalObject::initMapIteratorProto(JSCon
     return true;
 }
 
 MapIteratorObject *
 MapIteratorObject::create(JSContext *cx, HandleObject mapobj, ValueMap *data,
                           MapObject::IteratorKind kind)
 {
     Rooted<GlobalObject *> global(cx, &mapobj->global());
-    Rooted<JSObject*> proto(cx, global->getOrCreateMapIteratorPrototype(cx));
+    Rooted<JSObject*> proto(cx, GlobalObject::getOrCreateMapIteratorPrototype(cx, global));
     if (!proto)
         return nullptr;
 
     ValueMap::Range *range = cx->new_<ValueMap::Range>(data->all());
     if (!range)
         return nullptr;
 
     JSObject *iterobj = NewObjectWithGivenProto(cx, &class_, proto, global);
@@ -1487,17 +1487,17 @@ SetIteratorObject::kind() const
     int32_t i = getSlot(KindSlot).toInt32();
     JS_ASSERT(i == SetObject::Values || i == SetObject::Entries);
     return SetObject::IteratorKind(i);
 }
 
 bool
 GlobalObject::initSetIteratorProto(JSContext *cx, Handle<GlobalObject*> global)
 {
-    JSObject *base = global->getOrCreateIteratorPrototype(cx);
+    JSObject *base = GlobalObject::getOrCreateIteratorPrototype(cx, global);
     if (!base)
         return false;
     RootedObject proto(cx, NewObjectWithGivenProto(cx, &SetIteratorObject::class_, base, global));
     if (!proto)
         return false;
     proto->setSlot(SetIteratorObject::RangeSlot, PrivateValue(nullptr));
     if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods))
         return false;
@@ -1505,17 +1505,17 @@ GlobalObject::initSetIteratorProto(JSCon
     return true;
 }
 
 SetIteratorObject *
 SetIteratorObject::create(JSContext *cx, HandleObject setobj, ValueSet *data,
                           SetObject::IteratorKind kind)
 {
     Rooted<GlobalObject *> global(cx, &setobj->global());
-    Rooted<JSObject*> proto(cx, global->getOrCreateSetIteratorPrototype(cx));
+    Rooted<JSObject*> proto(cx, GlobalObject::getOrCreateSetIteratorPrototype(cx, global));
     if (!proto)
         return nullptr;
 
     ValueSet::Range *range = cx->new_<ValueSet::Range>(data->all());
     if (!range)
         return nullptr;
 
     JSObject *iterobj = NewObjectWithGivenProto(cx, &class_, proto, global);
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -267,18 +267,34 @@ js::ObjectToSource(JSContext *cx, Handle
 
     return buf.finishString();
 }
 #endif /* JS_HAS_TOSOURCE */
 
 JSString *
 JS_BasicObjectToString(JSContext *cx, HandleObject obj)
 {
+    // Some classes are really common, don't allocate new strings for them.
+    // The ordering below is based on the measurements in bug 966264.
+    if (obj->is<JSObject>())
+        return cx->names().objectObject;
+    if (obj->is<StringObject>())
+        return cx->names().objectString;
+    if (obj->is<ArrayObject>())
+        return cx->names().objectArray;
+    if (obj->is<JSFunction>())
+        return cx->names().objectFunction;
+    if (obj->is<NumberObject>())
+        return cx->names().objectNumber;
+
     const char *className = JSObject::className(cx, obj);
 
+    if (strcmp(className, "Window") == 0)
+        return cx->names().objectWindow;
+
     StringBuffer sb(cx);
     if (!sb.append("[object ") || !sb.appendInflated(className, strlen(className)) ||
         !sb.append("]"))
     {
         return nullptr;
     }
     return sb.finishString();
 }
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1400,17 +1400,21 @@ Neuter(JSContext *cx, unsigned argc, jsv
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 WorkerThreadCount(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setNumber(static_cast<double>(cx->runtime()->workerThreadCount()));
+#ifdef JS_THREADSAFE
+    args.rval().setInt32(cx->runtime()->useHelperThreads() ? WorkerThreadState().threadCount : 0);
+#else
+    args.rval().setInt32(0);
+#endif
     return true;
 }
 
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment')",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
--- a/js/src/builtin/TypeRepresentation.cpp
+++ b/js/src/builtin/TypeRepresentation.cpp
@@ -381,24 +381,27 @@ TypeRepresentation::addToTableOrFree(JSC
 
     // Next, attempt to add the type representation to the table.
     if (!comp->typeReprs.relookupOrAdd(p, this, this)) {
         js_ReportOutOfMemory(cx);
         js_free(this); // do not finalize, not present in the table
         return nullptr;
     }
 
+    RootedObject objectProto(cx, global->getOrCreateObjectPrototype(cx));
+    if (!objectProto)
+        return nullptr;
+
     // Now that the object is in the table, try to make the owner
     // object. If this succeeds, then the owner will remove from the
     // table once it is finalized. Otherwise, if this fails, we must
     // remove ourselves from the table ourselves and report an error.
-    RootedObject ownerObject(cx,
-        NewBuiltinClassInstance(cx,
-                                &class_,
-                                gc::GetGCObjectKind(&class_)));
+    RootedObject ownerObject(cx);
+    ownerObject = NewObjectWithGivenProto(cx, &class_, objectProto,
+                                          cx->global());
     if (!ownerObject) {
         comp->typeReprs.remove(this);
         js_free(this);
         return nullptr;
     }
 
     ownerObject->setPrivate(this);
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5161,17 +5161,17 @@ EmitReturn(ExclusiveContext *cx, Bytecod
     if (Emit1(cx, bce, JSOP_RETURN) < 0)
         return false;
 
     NonLocalExitScope nle(cx, bce);
 
     if (!nle.prepareForNonLocalJump(nullptr))
         return false;
 
-    if (top + JSOP_RETURN_LENGTH != bce->offset()) {
+    if (top + static_cast<ptrdiff_t>(JSOP_RETURN_LENGTH) != bce->offset()) {
         bce->code()[top] = JSOP_SETRVAL;
         if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
             return false;
     }
 
     return true;
 }
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1968,17 +1968,17 @@ Parser<ParseHandler>::functionDef(Handle
         return pn;
 
     RootedObject proto(context);
     if (generatorKind == StarGenerator) {
         // If we are off the main thread, the generator meta-objects have
         // already been created by js::StartOffThreadParseScript, so cx will not
         // be necessary.
         JSContext *cx = context->maybeJSContext();
-        proto = context->global()->getOrCreateStarGeneratorFunctionPrototype(cx);
+        proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
         if (!proto)
             return null();
     }
     RootedFunction fun(context, newFunction(pc, funName, kind, proto));
     if (!fun)
         return null();
 
     // Speculatively parse using the directives of the parent parsing context.
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -124,16 +124,47 @@ js::Nursery::isEmpty() const
 {
     JS_ASSERT(runtime_);
     if (!isEnabled())
         return true;
     JS_ASSERT_IF(runtime_->gcZeal_ != ZealGenerationalGCValue, currentStart_ == start());
     return position() == currentStart_;
 }
 
+JSObject *
+js::Nursery::allocateObject(JSContext *cx, size_t size, size_t numDynamic)
+{
+    /* Attempt to allocate slots contiguously after object, if possible. */
+    if (numDynamic && numDynamic <= MaxNurserySlots) {
+        size_t totalSize = size + sizeof(HeapSlot) * numDynamic;
+        JSObject *obj = static_cast<JSObject *>(allocate(totalSize));
+        if (obj) {
+            obj->setInitialSlots(reinterpret_cast<HeapSlot *>(size_t(obj) + size));
+            return obj;
+        }
+        /* If we failed to allocate as a block, retry with out-of-line slots. */
+    }
+
+    HeapSlot *slots = nullptr;
+    if (numDynamic) {
+        slots = allocateHugeSlots(cx, numDynamic);
+        if (MOZ_UNLIKELY(!slots))
+            return nullptr;
+    }
+
+    JSObject *obj = static_cast<JSObject *>(allocate(size));
+
+    if (obj)
+        obj->setInitialSlots(slots);
+    else
+        freeSlots(cx, slots);
+
+    return obj;
+}
+
 void *
 js::Nursery::allocate(size_t size)
 {
     JS_ASSERT(isEnabled());
     JS_ASSERT(!runtime()->isHeapBusy());
 
     /* Ensure there's enough space to replace the contents with a RelocationOverlay. */
     JS_ASSERT(size >= sizeof(RelocationOverlay));
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -74,20 +74,20 @@ class Nursery
     bool isEmpty() const;
 
     template <typename T>
     MOZ_ALWAYS_INLINE bool isInside(const T *p) const {
         return gc::IsInsideNursery((JS::shadow::Runtime *)runtime_, p);
     }
 
     /*
-     * Allocate and return a pointer to a new GC thing. Returns nullptr if the
-     * Nursery is full.
+     * Allocate and return a pointer to a new GC object with its |slots|
+     * pointer pre-filled. Returns nullptr if the Nursery is full.
      */
-    void *allocate(size_t size);
+    JSObject *allocateObject(JSContext *cx, size_t size, size_t numDynamic);
 
     /* Allocate a slots array for the given object. */
     HeapSlot *allocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots);
 
     /* Allocate an elements vector for the given object. */
     ObjectElements *allocateElements(JSContext *cx, JSObject *obj, uint32_t nelems);
 
     /* Resize an existing slots array. */
@@ -178,17 +178,17 @@ class Nursery
      * The set of externally malloced slots potentially kept live by objects
      * stored in the nursery. Any external slots that do not belong to a
      * tenured thing at the end of a minor GC must be freed.
      */
     typedef HashSet<HeapSlot *, PointerHasher<HeapSlot *, 3>, SystemAllocPolicy> HugeSlotsSet;
     HugeSlotsSet hugeSlots;
 
     /* The maximum number of slots allowed to reside inline in the nursery. */
-    static const size_t MaxNurserySlots = 100;
+    static const size_t MaxNurserySlots = 128;
 
     /* The amount of space in the mapped nursery available to allocations. */
     static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer);
 
     struct NurseryChunkLayout {
         char data[NurseryChunkUsableSize];
         gc::ChunkTrailer trailer;
         uintptr_t start() { return uintptr_t(&data); }
@@ -248,16 +248,19 @@ class Nursery
     /* Allocates and registers external slots with the nursery. */
     HeapSlot *allocateHugeSlots(JSContext *cx, size_t nslots);
 
     /* Allocates a new GC thing from the tenured generation during minor GC. */
     void *allocateFromTenured(JS::Zone *zone, gc::AllocKind thingKind);
 
     struct TenureCountCache;
 
+    /* Common internal allocator function. */
+    void *allocate(size_t size);
+
     /*
      * Move the object at |src| in the Nursery to an already-allocated cell
      * |dst| in Tenured.
      */
     void collectToFixedPoint(gc::MinorCollectionTracer *trc, TenureCountCache &tenureCounts);
     MOZ_ALWAYS_INLINE void traceObject(gc::MinorCollectionTracer *trc, JSObject *src);
     MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, uint32_t nslots);
     MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, HeapSlot *end);
deleted file mode 100644
--- a/js/src/jit-test/tests/auto-regress/bug770954.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// Binary: cache/js-dbg-32-b6aa44d8f11f-linux
-// Flags:
-//
-
-gczeal(4);
-var s = new Set;
-s.add(-0);
-s.add(0);
-assertEq(s.delete(-0), true);
-assertEq(s.has(0), (true  ));
-assertEq(s.has(-0), false);
-var m = new Map;
-m.set(-0, 'x');
-assertEq(m.has(0), false);
-assertEq(m.get(0), undefined);
-assertEq(m.has(-0), true);
-assertEq(m.delete(-0), true);
--- a/js/src/jit-test/tests/collections/Map-gc-4.js
+++ b/js/src/jit-test/tests/collections/Map-gc-4.js
@@ -1,15 +1,15 @@
 // Bug 770954.
 
 gczeal(4);
 var s = new Set;
 s.add(-0);
 s.add(0);
 assertEq(s.delete(-0), true);
-assertEq(s.has(0), (true  ));
+assertEq(s.has(0), (false  ));
 assertEq(s.has(-0), false);
 var m = new Map;
 m.set(-0, 'x');
-assertEq(m.has(0), false);
-assertEq(m.get(0), undefined);
+assertEq(m.has(0), true);
+assertEq(m.get(0), 'x');
 assertEq(m.has(-0), true);
 assertEq(m.delete(-0), true);
--- a/js/src/jit-test/tests/collections/Map-get.js
+++ b/js/src/jit-test/tests/collections/Map-get.js
@@ -8,17 +8,17 @@ function rope() {
     return s;
 }
 
 var keys = [undefined, null, true, false,
             0, 1, 65535, 65536, 2147483647, 2147483648, 4294967295, 4294967296,
             -1, -65536, -2147483648,
             0.5, Math.sqrt(81), Math.PI,
             Number.MAX_VALUE, -Number.MAX_VALUE, Number.MIN_VALUE, -Number.MIN_VALUE,
-            -0, NaN, Infinity, -Infinity,
+            NaN, Infinity, -Infinity,
             "", "\0", "a", "ab", "abcdefg", rope(),
             {}, [], /a*b/, Object.prototype, Object];
 
 for (var i = 0; i < keys.length; i++) {
     // without being set
     var key = keys[i];
     assertEq(m.has(key), false);
     assertEq(m.get(key), undefined);
--- a/js/src/jit-test/tests/collections/key-equality-0.js
+++ b/js/src/jit-test/tests/collections/key-equality-0.js
@@ -1,37 +1,43 @@
-// -0 is a distinct key from +0.
+// -0 is treated as the same key as +0.
 
 var s = new Set;
 s.add(-0);
-assertEq(s.has(0), false);
+assertEq(s.has(0), true);
 assertEq(s.has(-0), true);
 
-assertEq(s.delete(0), false);
-assertEq(s.has(-0), true);
+assertEq(s.delete(0), true);
+assertEq(s.has(-0), false);
+assertEq(s.has(0), false);
 
 s.add(0);
+assertEq(s.has(0), true);
+assertEq(s.has(-0), true);
 assertEq(s.delete(-0), true);
-assertEq(s.has(0), true);
 assertEq(s.has(-0), false);
+assertEq(s.has(0), false);
 
 var m = new Map;
 m.set(-0, 'x');
-assertEq(m.has(0), false);
-assertEq(m.get(0), undefined);
+assertEq(m.has(0), true);
+assertEq(m.get(0), 'x');
 assertEq(m.has(-0), true);
 assertEq(m.get(-0), 'x');
 
-assertEq(m.delete(0), false);
-assertEq(m.has(-0), true);
-assertEq(m.get(-0), 'x');
+assertEq(m.delete(0), true);
+assertEq(m.has(-0), false);
+assertEq(m.get(-0), undefined);
+assertEq(m.has(0), false);
+assertEq(m.get(0), undefined);
 
+m.set(-0, 'x');
 m.set(0, 'y');
 assertEq(m.has(0), true);
 assertEq(m.get(0), 'y');
 assertEq(m.has(-0), true);
-assertEq(m.get(-0), 'x');
+assertEq(m.get(-0), 'y');
 
 assertEq(m.delete(-0), true);
-assertEq(m.has(0), true);
-assertEq(m.get(0), 'y');
+assertEq(m.has(0), false);
+assertEq(m.get(0), undefined);
 assertEq(m.has(-0), false);
 assertEq(m.get(-0), undefined);
--- a/js/src/jit-test/tests/debug/resumption-06.js
+++ b/js/src/jit-test/tests/debug/resumption-06.js
@@ -12,9 +12,9 @@ g.eval("var dbg = new Debugger(debuggeeG
 function* gen() {
     yield '1';
     debugger;  // Force return here. The value is ignored.
     yield '2';
 }
 var iter = gen();
 assertIteratorNext(iter, '1');
 assertEq(iter.next(), '!');
-assertThrowsInstanceOf(iter.next.bind(iter), TypeError);
+assertIteratorDone(iter);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/generators/next-on-finished.js
@@ -0,0 +1,6 @@
+function*g(){ };
+o = g();
+o.next();
+result = o.next();
+assertEq(result.done, true);
+assertEq(o.value, undefined);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/generators/throw-on-finished.js
@@ -0,0 +1,7 @@
+load(libdir + "asserts.js");
+
+function*g(){ };
+o = g();
+o.next();
+function TestException() {};
+assertThrowsInstanceOf(() => o.throw(new TestException()), TestException);
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -5359,74 +5359,75 @@ CheckFunctionsSequential(ModuleCompiler 
 
 #ifdef JS_THREADSAFE
 
 // Currently, only one asm.js parallel compilation is allowed at a time.
 // This RAII class attempts to claim this parallel compilation using atomic ops
 // on rt->workerThreadState->asmJSCompilationInProgress.
 class ParallelCompilationGuard
 {
-    WorkerThreadState *parallelState_;
+    bool parallelState_;
   public:
-    ParallelCompilationGuard() : parallelState_(nullptr) {}
+    ParallelCompilationGuard() : parallelState_(false) {}
     ~ParallelCompilationGuard() {
         if (parallelState_) {
-            JS_ASSERT(parallelState_->asmJSCompilationInProgress == true);
-            parallelState_->asmJSCompilationInProgress = false;
+            JS_ASSERT(WorkerThreadState().asmJSCompilationInProgress == true);
+            WorkerThreadState().asmJSCompilationInProgress = false;
         }
     }
-    bool claim(WorkerThreadState *state) {
+    bool claim() {
         JS_ASSERT(!parallelState_);
-        if (!state->asmJSCompilationInProgress.compareExchange(false, true))
+        if (!WorkerThreadState().asmJSCompilationInProgress.compareExchange(false, true))
             return false;
-        parallelState_ = state;
+        parallelState_ = true;
         return true;
     }
 };
 
 static bool
 ParallelCompilationEnabled(ExclusiveContext *cx)
 {
     // If 'cx' isn't a JSContext, then we are already off the main thread so
     // off-thread compilation must be enabled. However, since there are a fixed
     // number of worker threads and one is already being consumed by this
     // parsing task, ensure that there another free thread to avoid deadlock.
     // (Note: there is at most one thread used for parsing so we don't have to
     // worry about general dining philosophers.)
+    if (WorkerThreadState().threadCount <= 1)
+        return false;
+
     if (!cx->isJSContext())
-        return cx->workerThreadState()->numThreads > 1;
-
+        return true;
     return cx->asJSContext()->runtime()->canUseParallelIonCompilation();
 }
 
 // State of compilation as tracked and updated by the main thread.
 struct ParallelGroupState
 {
-    WorkerThreadState &state;
     js::Vector<AsmJSParallelTask> &tasks;
     int32_t outstandingJobs; // Good work, jobs!
     uint32_t compiledJobs;
 
-    ParallelGroupState(WorkerThreadState &state, js::Vector<AsmJSParallelTask> &tasks)
-      : state(state), tasks(tasks), outstandingJobs(0), compiledJobs(0)
+    ParallelGroupState(js::Vector<AsmJSParallelTask> &tasks)
+      : tasks(tasks), outstandingJobs(0), compiledJobs(0)
     { }
 };
 
 // Block until a worker-assigned LifoAlloc becomes finished.
 static AsmJSParallelTask *
 GetFinishedCompilation(ModuleCompiler &m, ParallelGroupState &group)
 {
-    AutoLockWorkerThreadState lock(*m.cx()->workerThreadState());
-
-    while (!group.state.asmJSWorkerFailed()) {
-        if (!group.state.asmJSFinishedList.empty()) {
+    AutoLockWorkerThreadState lock;
+
+    while (!WorkerThreadState().asmJSWorkerFailed()) {
+        if (!WorkerThreadState().asmJSFinishedList().empty()) {
             group.outstandingJobs--;
-            return group.state.asmJSFinishedList.popCopy();
+            return WorkerThreadState().asmJSFinishedList().popCopy();
         }
-        group.state.wait(WorkerThreadState::CONSUMER);
+        WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
     }
 
     return nullptr;
 }
 
 static bool
 GenerateCodeForFinishedJob(ModuleCompiler &m, ParallelGroupState &group, AsmJSParallelTask **outTask)
 {
@@ -5466,34 +5467,39 @@ GetUnusedTask(ParallelGroupState &group,
         return false;
     *outTask = &group.tasks[i];
     return true;
 }
 
 static bool
 CheckFunctionsParallelImpl(ModuleCompiler &m, ParallelGroupState &group)
 {
-    JS_ASSERT(group.state.asmJSWorklist.empty());
-    JS_ASSERT(group.state.asmJSFinishedList.empty());
-    group.state.resetAsmJSFailureState();
+#ifdef DEBUG
+    {
+        AutoLockWorkerThreadState lock;
+        JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
+        JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
+    }
+#endif
+    WorkerThreadState().resetAsmJSFailureState();
 
     for (unsigned i = 0; PeekToken(m.parser()) == TOK_FUNCTION; i++) {
         // Get exclusive access to an empty LifoAlloc from the thread group's pool.
         AsmJSParallelTask *task = nullptr;
         if (!GetUnusedTask(group, i, &task) && !GenerateCodeForFinishedJob(m, group, &task))
             return false;
 
         // Generate MIR into the LifoAlloc on the main thread.
         MIRGenerator *mir;
         ModuleCompiler::Func *func;
         if (!CheckFunction(m, task->lifo, &mir, &func))
             return false;
 
         // Perform optimizations and LIR generation on a worker thread.
-        task->init(func, mir);
+        task->init(m.cx()->compartment()->runtimeFromAnyThread(), func, mir);
         if (!StartOffThreadAsmJSCompile(m.cx(), task))
             return false;
 
         group.outstandingJobs++;
     }
 
     // Block for all outstanding workers to complete.
     while (group.outstandingJobs > 0) {
@@ -5502,97 +5508,101 @@ CheckFunctionsParallelImpl(ModuleCompile
             return false;
     }
 
     if (!CheckAllFunctionsDefined(m))
         return false;
 
     JS_ASSERT(group.outstandingJobs == 0);
     JS_ASSERT(group.compiledJobs == m.numFunctions());
-    JS_ASSERT(group.state.asmJSWorklist.empty());
-    JS_ASSERT(group.state.asmJSFinishedList.empty());
-    JS_ASSERT(!group.state.asmJSWorkerFailed());
+#ifdef DEBUG
+    {
+        AutoLockWorkerThreadState lock;
+        JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
+        JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
+    }
+#endif
+    JS_ASSERT(!WorkerThreadState().asmJSWorkerFailed());
     return true;
 }
 
 static void
 CancelOutstandingJobs(ModuleCompiler &m, ParallelGroupState &group)
 {
     // This is failure-handling code, so it's not allowed to fail.
     // The problem is that all memory for compilation is stored in LifoAllocs
     // maintained in the scope of CheckFunctionsParallel() -- so in order
     // for that function to safely return, and thereby remove the LifoAllocs,
     // none of that memory can be in use or reachable by workers.
 
     JS_ASSERT(group.outstandingJobs >= 0);
     if (!group.outstandingJobs)
         return;
 
-    AutoLockWorkerThreadState lock(*m.cx()->workerThreadState());
+    AutoLockWorkerThreadState lock;
 
     // From the compiling tasks, eliminate those waiting for worker assignation.
-    group.outstandingJobs -= group.state.asmJSWorklist.length();
-    group.state.asmJSWorklist.clear();
+    group.outstandingJobs -= WorkerThreadState().asmJSWorklist().length();
+    WorkerThreadState().asmJSWorklist().clear();
 
     // From the compiling tasks, eliminate those waiting for codegen.
-    group.outstandingJobs -= group.state.asmJSFinishedList.length();
-    group.state.asmJSFinishedList.clear();
+    group.outstandingJobs -= WorkerThreadState().asmJSFinishedList().length();
+    WorkerThreadState().asmJSFinishedList().clear();
 
     // Eliminate tasks that failed without adding to the finished list.
-    group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
+    group.outstandingJobs -= WorkerThreadState().harvestFailedAsmJSJobs();
 
     // Any remaining tasks are therefore undergoing active compilation.
     JS_ASSERT(group.outstandingJobs >= 0);
     while (group.outstandingJobs > 0) {
-        group.state.wait(WorkerThreadState::CONSUMER);
-
-        group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
-        group.outstandingJobs -= group.state.asmJSFinishedList.length();
-        group.state.asmJSFinishedList.clear();
+        WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
+
+        group.outstandingJobs -= WorkerThreadState().harvestFailedAsmJSJobs();
+        group.outstandingJobs -= WorkerThreadState().asmJSFinishedList().length();
+        WorkerThreadState().asmJSFinishedList().clear();
     }
 
     JS_ASSERT(group.outstandingJobs == 0);
-    JS_ASSERT(group.state.asmJSWorklist.empty());
-    JS_ASSERT(group.state.asmJSFinishedList.empty());
+    JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
+    JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
 }
 
 static const size_t LIFO_ALLOC_PARALLEL_CHUNK_SIZE = 1 << 12;
 
 static bool
 CheckFunctionsParallel(ModuleCompiler &m)
 {
     // If parallel compilation isn't enabled (not enough cores, disabled by
     // pref, etc) or another thread is currently compiling asm.js in parallel,
     // fall back to sequential compilation. (We could lift the latter
     // constraint by hoisting asmJS* state out of WorkerThreadState so multiple
     // concurrent asm.js parallel compilations don't race.)
     ParallelCompilationGuard g;
-    if (!ParallelCompilationEnabled(m.cx()) || !g.claim(m.cx()->workerThreadState()))
+    if (!ParallelCompilationEnabled(m.cx()) || !g.claim())
         return CheckFunctionsSequential(m);
 
     // Saturate all worker threads plus the main thread.
-    WorkerThreadState &state = *m.cx()->workerThreadState();
-    size_t numParallelJobs = state.numThreads + 1;
+    size_t numParallelJobs = WorkerThreadState().threadCount + 1;
 
     // Allocate scoped AsmJSParallelTask objects. Each contains a unique
     // LifoAlloc that provides all necessary memory for compilation.
     js::Vector<AsmJSParallelTask, 0> tasks(m.cx());
     if (!tasks.initCapacity(numParallelJobs))
         return false;
 
     for (size_t i = 0; i < numParallelJobs; i++)
         tasks.infallibleAppend(LIFO_ALLOC_PARALLEL_CHUNK_SIZE);
 
     // With compilation memory in-scope, dispatch worker threads.
-    ParallelGroupState group(state, tasks);
+    ParallelGroupState group(tasks);
     if (!CheckFunctionsParallelImpl(m, group)) {
         CancelOutstandingJobs(m, group);
 
         // If failure was triggered by a worker thread, report error.
-        if (void *maybeFunc = state.maybeAsmJSFailedFunction()) {
+        if (void *maybeFunc = WorkerThreadState().maybeAsmJSFailedFunction()) {
             ModuleCompiler::Func *func = reinterpret_cast<ModuleCompiler::Func *>(maybeFunc);
             return m.failOffset(func->srcOffset(), "allocation failure during compilation");
         }
 
         // Otherwise, the error occurred on the main thread and was already reported.
         return false;
     }
     return true;
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -6234,26 +6234,27 @@ static bool
 TryAttachPrimitiveGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
                               ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val,
                               HandleValue res, bool *attached)
 {
     JS_ASSERT(!*attached);
 
     JSValueType primitiveType;
     RootedObject proto(cx);
+    Rooted<GlobalObject*> global(cx, &script->global());
     if (val.isString()) {
         primitiveType = JSVAL_TYPE_STRING;
-        proto = script->global().getOrCreateStringPrototype(cx);
+        proto = GlobalObject::getOrCreateStringPrototype(cx, global);
     } else if (val.isNumber()) {
         primitiveType = JSVAL_TYPE_DOUBLE;
-        proto = script->global().getOrCreateNumberPrototype(cx);
+        proto = GlobalObject::getOrCreateNumberPrototype(cx, global);
     } else {
         JS_ASSERT(val.isBoolean());
         primitiveType = JSVAL_TYPE_BOOLEAN;
-        proto = script->global().getOrCreateBooleanPrototype(cx);
+        proto = GlobalObject::getOrCreateBooleanPrototype(cx, global);
     }
     if (!proto)
         return false;
 
     // Instantiate this property, for use during Ion compilation.
     RootedId id(cx, NameToId(name));
     if (IsIonEnabled(cx))
         types::EnsureTrackPropertyTypes(cx, proto, id);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -153,17 +153,17 @@ CodeGenerator::CodeGenerator(MIRGenerato
 
 CodeGenerator::~CodeGenerator()
 {
     JS_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteLinks() == 0);
     js_delete(unassociatedScriptCounts_);
 }
 
 typedef bool (*StringToNumberFn)(ThreadSafeContext *, JSString *, double *);
-typedef bool (*StringToNumberParFn)(ForkJoinSlice *, JSString *, double *);
+typedef bool (*StringToNumberParFn)(ForkJoinContext *, JSString *, double *);
 static const VMFunctionsModal StringToNumberInfo = VMFunctionsModal(
     FunctionInfo<StringToNumberFn>(StringToNumber),
     FunctionInfo<StringToNumberParFn>(StringToNumberPar));
 
 bool
 CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
 {
     ValueOperand operand = ToValue(lir, LValueToInt32::Input);
@@ -723,17 +723,17 @@ CodeGenerator::emitIntToString(Register 
     masm.branch32(Assembler::AboveOrEqual, input, Imm32(StaticStrings::INT_STATIC_LIMIT), ool);
 
     // Fast path for small integers.
     masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings().intStaticTable), output);
     masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
 }
 
 typedef JSFlatString *(*IntToStringFn)(ThreadSafeContext *, int);
-typedef JSFlatString *(*IntToStringParFn)(ForkJoinSlice *, int);
+typedef JSFlatString *(*IntToStringParFn)(ForkJoinContext *, int);
 static const VMFunctionsModal IntToStringInfo = VMFunctionsModal(
     FunctionInfo<IntToStringFn>(Int32ToString<CanGC>),
     FunctionInfo<IntToStringParFn>(IntToStringPar));
 
 bool
 CodeGenerator::visitIntToString(LIntToString *lir)
 {
     Register input = ToRegister(lir->input());
@@ -746,17 +746,17 @@ CodeGenerator::visitIntToString(LIntToSt
 
     emitIntToString(input, output, ool->entry());
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 typedef JSString *(*DoubleToStringFn)(ThreadSafeContext *, double);
-typedef JSString *(*DoubleToStringParFn)(ForkJoinSlice *, double);
+typedef JSString *(*DoubleToStringParFn)(ForkJoinContext *, double);
 static const VMFunctionsModal DoubleToStringInfo = VMFunctionsModal(
     FunctionInfo<DoubleToStringFn>(NumberToString<CanGC>),
     FunctionInfo<DoubleToStringParFn>(DoubleToStringPar));
 
 bool
 CodeGenerator::visitDoubleToString(LDoubleToString *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
@@ -772,17 +772,17 @@ CodeGenerator::visitDoubleToString(LDoub
     masm.convertDoubleToInt32(input, temp, ool->entry(), true);
     emitIntToString(temp, output, ool->entry());
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 typedef JSString *(*PrimitiveToStringFn)(JSContext *, HandleValue);
-typedef JSString *(*PrimitiveToStringParFn)(ForkJoinSlice *, HandleValue);
+typedef JSString *(*PrimitiveToStringParFn)(ForkJoinContext *, HandleValue);
 static const VMFunctionsModal PrimitiveToStringInfo = VMFunctionsModal(
     FunctionInfo<PrimitiveToStringFn>(ToStringSlow),
     FunctionInfo<PrimitiveToStringParFn>(PrimitiveToStringPar));
 
 bool
 CodeGenerator::visitPrimitiveToString(LPrimitiveToString *lir)
 {
     ValueOperand input = ToValue(lir, LPrimitiveToString::Input);
@@ -1004,25 +1004,25 @@ CodeGenerator::emitLambdaInit(const Regi
     masm.storePtr(scopeChain, Address(output, JSFunction::offsetOfEnvironment()));
     masm.storePtr(ImmGCPtr(info.fun->displayAtom()), Address(output, JSFunction::offsetOfAtom()));
 }
 
 bool
 CodeGenerator::visitLambdaPar(LLambdaPar *lir)
 {
     Register resultReg = ToRegister(lir->output());
-    Register sliceReg = ToRegister(lir->forkJoinSlice());
+    Register cxReg = ToRegister(lir->forkJoinContext());
     Register scopeChainReg = ToRegister(lir->scopeChain());
     Register tempReg1 = ToRegister(lir->getTemp0());
     Register tempReg2 = ToRegister(lir->getTemp1());
     const LambdaFunctionInfo &info = lir->mir()->info();
 
     JS_ASSERT(scopeChainReg != resultReg);
 
-    emitAllocateGCThingPar(lir, resultReg, sliceReg, tempReg1, tempReg2, info.fun);
+    emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, info.fun);
     emitLambdaInit(resultReg, scopeChainReg, info);
     return true;
 }
 
 bool
 CodeGenerator::visitLabel(LLabel *lir)
 {
     return true;
@@ -1629,34 +1629,34 @@ bool
 CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment *lir)
 {
     Address environment(ToRegister(lir->function()), JSFunction::offsetOfEnvironment());
     masm.loadPtr(environment, ToRegister(lir->output()));
     return true;
 }
 
 bool
-CodeGenerator::visitForkJoinSlice(LForkJoinSlice *lir)
+CodeGenerator::visitForkJoinContext(LForkJoinContext *lir)
 {
     const Register tempReg = ToRegister(lir->getTempReg());
 
     masm.setupUnalignedABICall(0, tempReg);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ForkJoinSlicePar));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ForkJoinContextPar));
     JS_ASSERT(ToRegister(lir->output()) == ReturnReg);
     return true;
 }
 
 bool
 CodeGenerator::visitGuardThreadExclusive(LGuardThreadExclusive *lir)
 {
     JS_ASSERT(gen->info().executionMode() == ParallelExecution);
 
     const Register tempReg = ToRegister(lir->getTempReg());
     masm.setupUnalignedABICall(2, tempReg);
-    masm.passABIArg(ToRegister(lir->forkJoinSlice()));
+    masm.passABIArg(ToRegister(lir->forkJoinContext()));
     masm.passABIArg(ToRegister(lir->object()));
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParallelWriteGuard));
 
     OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutIllegalWrite, lir);
     if (!bail)
         return false;
 
     // branch to the OOL failure code if false is returned
@@ -1883,17 +1883,17 @@ CodeGenerator::visitCallNative(LCallNati
 
     DebugOnly<uint32_t> initialStack = masm.framePushed();
 
     masm.checkStackAlignment();
 
     // Sequential native functions have the signature:
     //  bool (*)(JSContext *, unsigned, Value *vp)
     // and parallel native functions have the signature:
-    //  ParallelResult (*)(ForkJoinSlice *, unsigned, Value *vp)
+    //  ParallelResult (*)(ForkJoinContext *, unsigned, Value *vp)
     // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
     // are the function arguments.
 
     // Allocate space for the outparam, moving the StackPointer to what will be &vp[1].
     masm.adjustStack(unusedStack);
 
     // Push a Value containing the callee object: natives are allowed to access their callee before
     // setitng the return value. The StackPointer is moved to &vp[0].
@@ -2835,20 +2835,20 @@ CodeGenerator::visitCheckOverRecursedPar
 {
     // See above: unlike visitCheckOverRecursed(), this code runs in
     // parallel mode and hence uses the ionStackLimit from the current
     // thread state.  Also, we must check the interrupt flags because
     // on interrupt or abort, only the stack limit for the main thread
     // is reset, not the worker threads.  See comment in vm/ForkJoin.h
     // for more details.
 
-    Register sliceReg = ToRegister(lir->forkJoinSlice());
+    Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg = ToRegister(lir->getTempReg());
 
-    masm.loadPtr(Address(sliceReg, offsetof(ForkJoinSlice, perThreadData)), tempReg);
+    masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg);
     masm.loadPtr(Address(tempReg, offsetof(PerThreadData, ionStackLimit)), tempReg);
 
     // Conditional forward (unlikely) branch to failure.
     CheckOverRecursedFailurePar *ool = new(alloc()) CheckOverRecursedFailurePar(lir);
     if (!addOutOfLineCode(ool))
         return false;
     masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry());
     masm.checkInterruptFlagsPar(tempReg, ool->entry());
@@ -2868,17 +2868,17 @@ CodeGenerator::visitCheckOverRecursedFai
     // ReturnReg into it below and we don't want to clobber that
     // during PopRegsInMask():
     LCheckOverRecursedPar *lir = ool->lir();
     Register tempReg = ToRegister(lir->getTempReg());
     RegisterSet saveSet(lir->safepoint()->liveRegs());
     saveSet.takeUnchecked(tempReg);
 
     masm.PushRegsInMask(saveSet);
-    masm.movePtr(ToRegister(lir->forkJoinSlice()), CallTempReg0);
+    masm.movePtr(ToRegister(lir->forkJoinContext()), CallTempReg0);
     masm.setupUnalignedABICall(1, CallTempReg1);
     masm.passABIArg(CallTempReg0);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CheckOverRecursedPar));
     masm.movePtr(ReturnReg, tempReg);
     masm.PopRegsInMask(saveSet);
     masm.branchIfFalseBool(tempReg, bail->entry());
     masm.jump(ool->rejoin());
 
@@ -2898,17 +2898,17 @@ class OutOfLineCheckInterruptPar : publi
     bool accept(CodeGenerator *codegen) {
         return codegen->visitOutOfLineCheckInterruptPar(this);
     }
 };
 
 bool
 CodeGenerator::visitCheckInterruptPar(LCheckInterruptPar *lir)
 {
-    // First check for slice->shared->interrupt_.
+    // First check for cx->shared->interrupt_.
     OutOfLineCheckInterruptPar *ool = new(alloc()) OutOfLineCheckInterruptPar(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
     // We must check two flags:
     // - runtime->interrupt
     // - runtime->parallelAbort
     // See vm/ForkJoin.h for discussion on why we use this design.
@@ -2930,17 +2930,17 @@ CodeGenerator::visitOutOfLineCheckInterr
     // ReturnReg into it below and we don't want to clobber that
     // during PopRegsInMask():
     LCheckInterruptPar *lir = ool->lir;
     Register tempReg = ToRegister(lir->getTempReg());
     RegisterSet saveSet(lir->safepoint()->liveRegs());
     saveSet.takeUnchecked(tempReg);
 
     masm.PushRegsInMask(saveSet);
-    masm.movePtr(ToRegister(ool->lir->forkJoinSlice()), CallTempReg0);
+    masm.movePtr(ToRegister(ool->lir->forkJoinContext()), CallTempReg0);
     masm.setupUnalignedABICall(1, CallTempReg1);
     masm.passABIArg(CallTempReg0);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CheckInterruptPar));
     masm.movePtr(ReturnReg, tempReg);
     masm.PopRegsInMask(saveSet);
     masm.branchIfFalseBool(tempReg, bail->entry());
     masm.jump(ool->rejoin());
 
@@ -3609,22 +3609,22 @@ CodeGenerator::visitNewCallObject(LNewCa
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitNewCallObjectPar(LNewCallObjectPar *lir)
 {
     Register resultReg = ToRegister(lir->output());
-    Register sliceReg = ToRegister(lir->forkJoinSlice());
+    Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg1 = ToRegister(lir->getTemp0());
     Register tempReg2 = ToRegister(lir->getTemp1());
     JSObject *templateObj = lir->mir()->templateObj();
 
-    emitAllocateGCThingPar(lir, resultReg, sliceReg, tempReg1, tempReg2, templateObj);
+    emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, templateObj);
 
     // NB: !lir->slots()->isRegister() implies that there is no slots
     // array at all, and the memory is already zeroed when copying
     // from the template object
 
     if (lir->slots()->isRegister()) {
         Register slotsReg = ToRegister(lir->slots());
         JS_ASSERT(slotsReg != resultReg);
@@ -3632,37 +3632,37 @@ CodeGenerator::visitNewCallObjectPar(LNe
     }
 
     return true;
 }
 
 bool
 CodeGenerator::visitNewDenseArrayPar(LNewDenseArrayPar *lir)
 {
-    Register sliceReg = ToRegister(lir->forkJoinSlice());
+    Register cxReg = ToRegister(lir->forkJoinContext());
     Register lengthReg = ToRegister(lir->length());
     Register tempReg0 = ToRegister(lir->getTemp0());
     Register tempReg1 = ToRegister(lir->getTemp1());
     Register tempReg2 = ToRegister(lir->getTemp2());
     JSObject *templateObj = lir->mir()->templateObject();
 
     // Allocate the array into tempReg2.  Don't use resultReg because it
-    // may alias sliceReg etc.
-    emitAllocateGCThingPar(lir, tempReg2, sliceReg, tempReg0, tempReg1, templateObj);
+    // may alias cxReg etc.
+    emitAllocateGCThingPar(lir, tempReg2, cxReg, tempReg0, tempReg1, templateObj);
 
     // Invoke a C helper to allocate the elements.  For convenience,
     // this helper also returns the array back to us, or nullptr, which
     // obviates the need to preserve the register across the call.  In
     // reality, we should probably just have the C helper also
     // *allocate* the array, but that would require that it initialize
     // the various fields of the object, and I didn't want to
     // duplicate the code in initGCThing() that already does such an
     // admirable job.
     masm.setupUnalignedABICall(3, tempReg0);
-    masm.passABIArg(sliceReg);
+    masm.passABIArg(cxReg);
     masm.passABIArg(tempReg2);
     masm.passABIArg(lengthReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ExtendArrayPar));
 
     Register resultReg = ToRegister(lir->output());
     JS_ASSERT(resultReg == ReturnReg);
     OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, lir);
     if (!bail)
@@ -3700,70 +3700,70 @@ CodeGenerator::visitNewStringObject(LNew
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitNewPar(LNewPar *lir)
 {
     Register objReg = ToRegister(lir->output());
-    Register sliceReg = ToRegister(lir->forkJoinSlice());
+    Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg1 = ToRegister(lir->getTemp0());
     Register tempReg2 = ToRegister(lir->getTemp1());
     JSObject *templateObject = lir->mir()->templateObject();
-    emitAllocateGCThingPar(lir, objReg, sliceReg, tempReg1, tempReg2, templateObject);
+    emitAllocateGCThingPar(lir, objReg, cxReg, tempReg1, tempReg2, templateObject);
     return true;
 }
 
 class OutOfLineNewGCThingPar : public OutOfLineCodeBase<CodeGenerator>
 {
 public:
     LInstruction *lir;
     gc::AllocKind allocKind;
     Register objReg;
-    Register sliceReg;
+    Register cxReg;
 
     OutOfLineNewGCThingPar(LInstruction *lir, gc::AllocKind allocKind, Register objReg,
-                           Register sliceReg)
-      : lir(lir), allocKind(allocKind), objReg(objReg), sliceReg(sliceReg)
+                           Register cxReg)
+      : lir(lir), allocKind(allocKind), objReg(objReg), cxReg(cxReg)
     {}
 
     bool accept(CodeGenerator *codegen) {
         return codegen->visitOutOfLineNewGCThingPar(this);
     }
 };
 
 bool
 CodeGenerator::emitAllocateGCThingPar(LInstruction *lir, const Register &objReg,
-                                      const Register &sliceReg, const Register &tempReg1,
+                                      const Register &cxReg, const Register &tempReg1,
                                       const Register &tempReg2, JSObject *templateObj)
 {
     gc::AllocKind allocKind = templateObj->tenuredGetAllocKind();
-    OutOfLineNewGCThingPar *ool = new(alloc()) OutOfLineNewGCThingPar(lir, allocKind, objReg, sliceReg);
+    OutOfLineNewGCThingPar *ool = new(alloc()) OutOfLineNewGCThingPar(lir, allocKind, objReg, cxReg);
     if (!ool || !addOutOfLineCode(ool))
         return false;
 
-    masm.newGCThingPar(objReg, sliceReg, tempReg1, tempReg2, templateObj, ool->entry());
+    masm.newGCThingPar(objReg, cxReg, tempReg1, tempReg2, templateObj, ool->entry());
     masm.bind(ool->rejoin());
     masm.initGCThing(objReg, templateObj);
     return true;
 }
 
 bool
 CodeGenerator::visitOutOfLineNewGCThingPar(OutOfLineNewGCThingPar *ool)
 {
     // As a fallback for allocation in par. exec. mode, we invoke the
     // C helper NewGCThingPar(), which calls into the GC code.  If it
     // returns nullptr, we bail.  If returns non-nullptr, we rejoin the
     // original instruction.
     Register out = ool->objReg;
 
     saveVolatile(out);
     masm.setupUnalignedABICall(2, out);
-    masm.passABIArg(ool->sliceReg);
+    masm.passABIArg(ool->cxReg);
     masm.move32(Imm32(ool->allocKind), out);
     masm.passABIArg(out);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewGCThingPar));
     masm.storeCallResult(out);
     restoreVolatile(out);
 
     OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, ool->lir);
     if (!bail)
@@ -3910,32 +3910,31 @@ CodeGenerator::visitCreateThisWithProto(
     if (callee->isConstant())
         pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
     else
         pushArg(ToRegister(callee));
 
     return callVM(CreateThisWithProtoInfo, lir);
 }
 
-typedef JSObject *(*NewGCThingFn)(JSContext *cx, gc::AllocKind allocKind, size_t thingSize,
-                                  gc::InitialHeap initialHeap);
-static const VMFunction NewGCThingInfo =
-    FunctionInfo<NewGCThingFn>(js::jit::NewGCThing);
+typedef JSObject *(*NewGCObjectFn)(JSContext *cx, gc::AllocKind allocKind,
+                                   gc::InitialHeap initialHeap);
+static const VMFunction NewGCObjectInfo =
+    FunctionInfo<NewGCObjectFn>(js::jit::NewGCObject);
 
 bool
 CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
 {
     JSObject *templateObject = lir->mir()->templateObject();
     gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
-    int thingSize = (int)gc::Arena::thingSize(allocKind);
     gc::InitialHeap initialHeap = lir->mir()->initialHeap();
     Register objReg = ToRegister(lir->output());
 
-    OutOfLineCode *ool = oolCallVM(NewGCThingInfo, lir,
-                                   (ArgList(), Imm32(allocKind), Imm32(thingSize), Imm32(initialHeap)),
+    OutOfLineCode *ool = oolCallVM(NewGCObjectInfo, lir,
+                                   (ArgList(), Imm32(allocKind), Imm32(initialHeap)),
                                    StoreRegisterTo(objReg));
     if (!ool)
         return false;
 
     // Allocate. If the FreeList is empty, call to VM, which may GC.
     masm.newGCThing(objReg, templateObject, ool->entry(), lir->mir()->initialHeap());
 
     // Initialize based on the templateObject.
@@ -4357,17 +4356,17 @@ CodeGenerator::visitModD(LModD *ins)
     if (gen->compilingAsmJS())
         masm.callWithABI(AsmJSImm_ModD, MoveOp::DOUBLE);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MoveOp::DOUBLE);
     return true;
 }
 
 typedef bool (*BinaryFn)(JSContext *, MutableHandleValue, MutableHandleValue, Value *);
-typedef bool (*BinaryParFn)(ForkJoinSlice *, HandleValue, HandleValue, Value *);
+typedef bool (*BinaryParFn)(ForkJoinContext *, HandleValue, HandleValue, Value *);
 
 static const VMFunction AddInfo = FunctionInfo<BinaryFn>(js::AddValues);
 static const VMFunction SubInfo = FunctionInfo<BinaryFn>(js::SubValues);
 static const VMFunction MulInfo = FunctionInfo<BinaryFn>(js::MulValues);
 static const VMFunction DivInfo = FunctionInfo<BinaryFn>(js::DivValues);
 static const VMFunction ModInfo = FunctionInfo<BinaryFn>(js::ModValues);
 static const VMFunctionsModal UrshInfo = VMFunctionsModal(
     FunctionInfo<BinaryFn>(js::UrshValues),
@@ -4399,17 +4398,17 @@ CodeGenerator::visitBinaryV(LBinaryV *li
         return callVM(UrshInfo, lir);
 
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected binary op");
     }
 }
 
 typedef bool (*StringCompareFn)(JSContext *, HandleString, HandleString, bool *);
-typedef bool (*StringCompareParFn)(ForkJoinSlice *, HandleString, HandleString, bool *);
+typedef bool (*StringCompareParFn)(ForkJoinContext *, HandleString, HandleString, bool *);
 static const VMFunctionsModal StringsEqualInfo = VMFunctionsModal(
     FunctionInfo<StringCompareFn>(jit::StringsEqual<true>),
     FunctionInfo<StringCompareParFn>(jit::StringsEqualPar));
 static const VMFunctionsModal StringsNotEqualInfo = VMFunctionsModal(
     FunctionInfo<StringCompareFn>(jit::StringsEqual<false>),
     FunctionInfo<StringCompareParFn>(jit::StringsUnequalPar));
 
 bool
@@ -4471,17 +4470,17 @@ CodeGenerator::visitCompareS(LCompareS *
     Register right = ToRegister(lir->right());
     Register output = ToRegister(lir->output());
     Register temp = ToRegister(lir->temp());
 
     return emitCompareS(lir, op, left, right, output, temp);
 }
 
 typedef bool (*CompareFn)(JSContext *, MutableHandleValue, MutableHandleValue, bool *);
-typedef bool (*CompareParFn)(ForkJoinSlice *, MutableHandleValue, MutableHandleValue, bool *);
+typedef bool (*CompareParFn)(ForkJoinContext *, MutableHandleValue, MutableHandleValue, bool *);
 static const VMFunctionsModal EqInfo = VMFunctionsModal(
     FunctionInfo<CompareFn>(jit::LooselyEqual<true>),
     FunctionInfo<CompareParFn>(jit::LooselyEqualPar));
 static const VMFunctionsModal NeInfo = VMFunctionsModal(
     FunctionInfo<CompareFn>(jit::LooselyEqual<false>),
     FunctionInfo<CompareParFn>(jit::LooselyUnequalPar));
 static const VMFunctionsModal StrictEqInfo = VMFunctionsModal(
     FunctionInfo<CompareFn>(jit::StrictlyEqual<true>),
@@ -4755,17 +4754,17 @@ CodeGenerator::visitEmulatesUndefinedAnd
 
     Register objreg = ToRegister(lir->input());
 
     testObjectEmulatesUndefined(objreg, equal, unequal, ToRegister(lir->temp()), ool);
     return true;
 }
 
 typedef JSString *(*ConcatStringsFn)(ThreadSafeContext *, HandleString, HandleString);
-typedef JSString *(*ConcatStringsParFn)(ForkJoinSlice *, HandleString, HandleString);
+typedef JSString *(*ConcatStringsParFn)(ForkJoinContext *, HandleString, HandleString);
 static const VMFunctionsModal ConcatStringsInfo = VMFunctionsModal(
     FunctionInfo<ConcatStringsFn>(ConcatStrings<CanGC>),
     FunctionInfo<ConcatStringsParFn>(ConcatStringsPar));
 
 bool
 CodeGenerator::emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output)
 {
     OutOfLineCode *ool = oolCallVM(ConcatStringsInfo, lir, (ArgList(), lhs, rhs),
@@ -4800,24 +4799,24 @@ CodeGenerator::visitConcat(LConcat *lir)
     JS_ASSERT(output == CallTempReg5);
 
     return emitConcat(lir, lhs, rhs, output);
 }
 
 bool
 CodeGenerator::visitConcatPar(LConcatPar *lir)
 {
-    DebugOnly<Register> slice = ToRegister(lir->forkJoinSlice());
+    DebugOnly<Register> cx = ToRegister(lir->forkJoinContext());
     Register lhs = ToRegister(lir->lhs());
     Register rhs = ToRegister(lir->rhs());
     Register output = ToRegister(lir->output());
 
     JS_ASSERT(lhs == CallTempReg0);
     JS_ASSERT(rhs == CallTempReg1);
-    JS_ASSERT((Register)slice == CallTempReg4);
+    JS_ASSERT((Register)cx == CallTempReg4);
     JS_ASSERT(ToRegister(lir->temp1()) == CallTempReg0);
     JS_ASSERT(ToRegister(lir->temp2()) == CallTempReg1);
     JS_ASSERT(ToRegister(lir->temp3()) == CallTempReg2);
     JS_ASSERT(ToRegister(lir->temp4()) == CallTempReg3);
     JS_ASSERT(output == CallTempReg5);
 
     return emitConcat(lir, lhs, rhs, output);
 }
@@ -4854,20 +4853,20 @@ JitCompartment::generateStringConcatStub
 
     Register lhs = CallTempReg0;
     Register rhs = CallTempReg1;
     Register temp1 = CallTempReg2;
     Register temp2 = CallTempReg3;
     Register temp3 = CallTempReg4;
     Register output = CallTempReg5;
 
-    // In parallel execution, we pass in the ForkJoinSlice in CallTempReg4, as
+    // In parallel execution, we pass in the ForkJoinContext in CallTempReg4, as
     // by the time we need to use the temp3 we no longer have need of the
-    // slice.
-    Register forkJoinSlice = CallTempReg4;
+    // cx.
+    Register forkJoinContext = CallTempReg4;
 
     Label failure, failurePopTemps;
 
     // If lhs is empty, return rhs.
     Label leftEmpty;
     masm.loadStringLength(lhs, temp1);
     masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty);
 
@@ -4889,17 +4888,17 @@ JitCompartment::generateStringConcatStub
     // Allocate a new rope.
     switch (mode) {
       case SequentialExecution:
         masm.newGCString(output, &failure);
         break;
       case ParallelExecution:
         masm.push(temp1);
         masm.push(temp2);
-        masm.newGCStringPar(output, forkJoinSlice, temp1, temp2, &failurePopTemps);
+        masm.newGCStringPar(output, forkJoinContext, temp1, temp2, &failurePopTemps);
         masm.pop(temp2);
         masm.pop(temp1);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
     // Store lengthAndFlags.
@@ -4934,17 +4933,17 @@ JitCompartment::generateStringConcatStub
     // Allocate a JSShortString.
     switch (mode) {
       case SequentialExecution:
         masm.newGCShortString(output, &failure);
         break;
       case ParallelExecution:
         masm.push(temp1);
         masm.push(temp2);
-        masm.newGCShortStringPar(output, forkJoinSlice, temp1, temp2, &failurePopTemps);
+        masm.newGCShortStringPar(output, forkJoinContext, temp1, temp2, &failurePopTemps);
         masm.pop(temp2);
         masm.pop(temp1);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
     // Set lengthAndFlags.
@@ -4953,18 +4952,18 @@ JitCompartment::generateStringConcatStub
     masm.storePtr(temp2, Address(output, JSString::offsetOfLengthAndFlags()));
 
     // Set chars pointer, keep in temp2 for copy loop below.
     masm.computeEffectiveAddress(Address(output, JSShortString::offsetOfInlineStorage()), temp2);
     masm.storePtr(temp2, Address(output, JSShortString::offsetOfChars()));
 
     {
         // We use temp3 in this block, which in parallel execution also holds
-        // a live ForkJoinSlice pointer. If we are compiling for parallel
-        // execution, be sure to save and restore the ForkJoinSlice.
+        // a live ForkJoinContext pointer. If we are compiling for parallel
+        // execution, be sure to save and restore the ForkJoinContext.
         if (mode == ParallelExecution)
             masm.push(temp3);
 
         // Copy lhs chars. Temp1 still holds the lhs length. Note that this
         // advances temp2 to point to the next char. Note that this also
         // repurposes the lhs register.
         masm.loadPtr(Address(lhs, JSString::offsetOfChars()), lhs);
         CopyStringChars(masm, temp2, lhs, temp1, temp3);
@@ -5367,17 +5366,17 @@ CodeGenerator::visitStoreElementHoleV(LS
         masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 typedef bool (*SetObjectElementFn)(JSContext *, HandleObject, HandleValue, HandleValue,
                                    bool strict);
-typedef bool (*SetElementParFn)(ForkJoinSlice *, HandleObject, HandleValue, HandleValue, bool);
+typedef bool (*SetElementParFn)(ForkJoinContext *, HandleObject, HandleValue, HandleValue, bool);
 static const VMFunctionsModal SetObjectElementInfo = VMFunctionsModal(
     FunctionInfo<SetObjectElementFn>(SetObjectElement),
     FunctionInfo<SetElementParFn>(SetElementPar));
 
 bool
 CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
 {
     Register object, elements;
@@ -5974,17 +5973,17 @@ CodeGenerator::visitRunOncePrologue(LRun
 {
     pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
     return callVM(RunOnceScriptPrologueInfo, lir);
 }
 
 
 typedef JSObject *(*InitRestParameterFn)(JSContext *, uint32_t, Value *, HandleObject,
                                          HandleObject);
-typedef JSObject *(*InitRestParameterParFn)(ForkJoinSlice *, uint32_t, Value *,
+typedef JSObject *(*InitRestParameterParFn)(ForkJoinContext *, uint32_t, Value *,
                                             HandleObject, HandleObject);
 static const VMFunctionsModal InitRestParameterInfo = VMFunctionsModal(
     FunctionInfo<InitRestParameterFn>(InitRestParameter),
     FunctionInfo<InitRestParameterParFn>(InitRestParameterPar));
 
 bool
 CodeGenerator::emitRest(LInstruction *lir, Register array, Register numActuals,
                         Register temp0, Register temp1, unsigned numFormals,
@@ -6037,24 +6036,24 @@ CodeGenerator::visitRest(LRest *lir)
 
     return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject);
 }
 
 bool
 CodeGenerator::visitRestPar(LRestPar *lir)
 {
     Register numActuals = ToRegister(lir->numActuals());
-    Register slice = ToRegister(lir->forkJoinSlice());
+    Register cx = ToRegister(lir->forkJoinContext());
     Register temp0 = ToRegister(lir->getTemp(0));
     Register temp1 = ToRegister(lir->getTemp(1));
     Register temp2 = ToRegister(lir->getTemp(2));
     unsigned numFormals = lir->mir()->numFormals();
     JSObject *templateObject = lir->mir()->templateObject();
 
-    if (!emitAllocateGCThingPar(lir, temp2, slice, temp0, temp1, templateObject))
+    if (!emitAllocateGCThingPar(lir, temp2, cx, temp0, temp1, templateObject))
         return false;
 
     return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject);
 }
 
 bool
 CodeGenerator::generateAsmJS()
 {
@@ -6682,17 +6681,17 @@ CodeGenerator::visitGetPropertyIC(OutOfL
         return false;
     StoreValueTo(ic->output()).generate(this);
     restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
 
     masm.jump(ool->rejoin());
     return true;
 }
 
-typedef bool (*GetPropertyParICFn)(ForkJoinSlice *, size_t, HandleObject, MutableHandleValue);
+typedef bool (*GetPropertyParICFn)(ForkJoinContext *, size_t, HandleObject, MutableHandleValue);
 const VMFunction GetPropertyParIC::UpdateInfo =
     FunctionInfo<GetPropertyParICFn>(GetPropertyParIC::update);
 
 bool
 CodeGenerator::visitGetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyParIC> &ic)
 {
     LInstruction *lir = ool->lir();
     saveLive(lir);
@@ -6822,17 +6821,17 @@ CodeGenerator::visitSetElementIC(OutOfLi
     if (!callVM(SetElementIC::UpdateInfo, lir))
         return false;
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
     return true;
 }
 
-typedef bool (*SetElementParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue, HandleValue);
+typedef bool (*SetElementParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue, HandleValue);
 const VMFunction SetElementParIC::UpdateInfo =
     FunctionInfo<SetElementParICFn>(SetElementParIC::update);
 
 bool
 CodeGenerator::visitSetElementParIC(OutOfLineUpdateCache *ool, DataPtr<SetElementParIC> &ic)
 {
     LInstruction *lir = ool->lir();
     saveLive(lir);
@@ -6844,17 +6843,17 @@ CodeGenerator::visitSetElementParIC(OutO
     if (!callVM(SetElementParIC::UpdateInfo, lir))
         return false;
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
     return true;
 }
 
-typedef bool (*GetElementParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue,
+typedef bool (*GetElementParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue,
                                   MutableHandleValue);
 const VMFunction GetElementParIC::UpdateInfo =
     FunctionInfo<GetElementParICFn>(GetElementParIC::update);
 
 bool
 CodeGenerator::visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic)
 {
     LInstruction *lir = ool->lir();
@@ -6900,17 +6899,17 @@ CodeGenerator::visitBindNameIC(OutOfLine
     restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered());
 
     masm.jump(ool->rejoin());
     return true;
 }
 
 typedef bool (*SetPropertyFn)(JSContext *, HandleObject,
                               HandlePropertyName, const HandleValue, bool, jsbytecode *);
-typedef bool (*SetPropertyParFn)(ForkJoinSlice *, HandleObject,
+typedef bool (*SetPropertyParFn)(ForkJoinContext *, HandleObject,
                                  HandlePropertyName, const HandleValue, bool, jsbytecode *);
 static const VMFunctionsModal SetPropertyInfo = VMFunctionsModal(
     FunctionInfo<SetPropertyFn>(SetProperty),
     FunctionInfo<SetPropertyParFn>(SetPropertyPar));
 
 bool
 CodeGenerator::visitCallSetProperty(LCallSetProperty *ins)
 {
@@ -7007,17 +7006,17 @@ CodeGenerator::visitSetPropertyIC(OutOfL
     if (!callVM(SetPropertyIC::UpdateInfo, lir))
         return false;
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
     return true;
 }
 
-typedef bool (*SetPropertyParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue);
+typedef bool (*SetPropertyParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue);
 const VMFunction SetPropertyParIC::UpdateInfo =
     FunctionInfo<SetPropertyParICFn>(SetPropertyParIC::update);
 
 bool
 CodeGenerator::visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic)
 {
     LInstruction *lir = ool->lir();
     saveLive(lir);
@@ -7039,30 +7038,30 @@ static const VMFunction ThrowInfoCodeGen
 bool
 CodeGenerator::visitThrow(LThrow *lir)
 {
     pushArg(ToValue(lir, LThrow::Value));
     return callVM(ThrowInfoCodeGen, lir);
 }
 
 typedef bool (*BitNotFn)(JSContext *, HandleValue, int *p);
-typedef bool (*BitNotParFn)(ForkJoinSlice *, HandleValue, int32_t *);
+typedef bool (*BitNotParFn)(ForkJoinContext *, HandleValue, int32_t *);
 static const VMFunctionsModal BitNotInfo = VMFunctionsModal(
     FunctionInfo<BitNotFn>(BitNot),
     FunctionInfo<BitNotParFn>(BitNotPar));
 
 bool
 CodeGenerator::visitBitNotV(LBitNotV *lir)
 {
     pushArg(ToValue(lir, LBitNotV::Input));
     return callVM(BitNotInfo, lir);
 }
 
 typedef bool (*BitopFn)(JSContext *, HandleValue, HandleValue, int *p);
-typedef bool (*BitopParFn)(ForkJoinSlice *, HandleValue, HandleValue, int32_t *);
+typedef bool (*BitopParFn)(ForkJoinContext *, HandleValue, HandleValue, int32_t *);
 static const VMFunctionsModal BitAndInfo = VMFunctionsModal(
     FunctionInfo<BitopFn>(BitAnd),
     FunctionInfo<BitopParFn>(BitAndPar));
 static const VMFunctionsModal BitOrInfo = VMFunctionsModal(
     FunctionInfo<BitopFn>(BitOr),
     FunctionInfo<BitopParFn>(BitOrPar));
 static const VMFunctionsModal BitXorInfo = VMFunctionsModal(
     FunctionInfo<BitopFn>(BitXor),
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -207,17 +207,17 @@ class CodeGenerator : public CodeGenerat
     bool visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir);
     bool emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output);
     bool visitConcat(LConcat *lir);
     bool visitConcatPar(LConcatPar *lir);
     bool visitCharCodeAt(LCharCodeAt *lir);
     bool visitFromCharCode(LFromCharCode *lir);
     bool visitStringSplit(LStringSplit *lir);
     bool visitFunctionEnvironment(LFunctionEnvironment *lir);
-    bool visitForkJoinSlice(LForkJoinSlice *lir);
+    bool visitForkJoinContext(LForkJoinContext *lir);
     bool visitGuardThreadExclusive(LGuardThreadExclusive *lir);
     bool visitCallGetProperty(LCallGetProperty *lir);
     bool visitCallGetElement(LCallGetElement *lir);
     bool visitCallSetElement(LCallSetElement *lir);
     bool visitCallInitElementArray(LCallInitElementArray *lir);
     bool visitThrow(LThrow *lir);
     bool visitTypeOfV(LTypeOfV *lir);
     bool visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool);
@@ -360,17 +360,17 @@ class CodeGenerator : public CodeGenerat
                              bool needsTypeBarrier);
     bool addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp,
                             FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
                             bool strict, bool guardHoles);
     bool checkForAbortPar(LInstruction *lir);
 
     bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
 
-    bool emitAllocateGCThingPar(LInstruction *lir, const Register &objReg, const Register &sliceReg,
+    bool emitAllocateGCThingPar(LInstruction *lir, const Register &objReg, const Register &cxReg,
                                 const Register &tempReg1, const Register &tempReg2,
                                 JSObject *templateObj);
 
     bool emitCallToUncompiledScriptPar(LInstruction *lir, Register calleeReg);
 
     void emitLambdaInit(const Register &resultReg, const Register &scopeChainReg,
                         const LambdaFunctionInfo &info);
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -508,25 +508,30 @@ jit::FinishOffThreadBuilder(IonBuilder *
     // destroy the builder and all other data accumulated during compilation,
     // except any final codegen (which includes an assembler and needs to be
     // explicitly destroyed).
     js_delete(builder->backgroundCodegen());
     js_delete(builder->alloc().lifoAlloc());
 }
 
 static inline void
-FinishAllOffThreadCompilations(JitCompartment *ion)
+FinishAllOffThreadCompilations(JSCompartment *comp)
 {
-    OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
-
-    for (size_t i = 0; i < compilations.length(); i++) {
-        IonBuilder *builder = compilations[i];
-        FinishOffThreadBuilder(builder);
+#ifdef JS_THREADSAFE
+    AutoLockWorkerThreadState lock;
+    GlobalWorkerThreadState::IonBuilderVector &finished = WorkerThreadState().ionFinishedList();
+
+    for (size_t i = 0; i < finished.length(); i++) {
+        IonBuilder *builder = finished[i];
+        if (builder->compartment == CompileCompartment::get(comp)) {
+            FinishOffThreadBuilder(builder);
+            WorkerThreadState().remove(finished, &i);
+        }
     }
-    compilations.clear();
+#endif
 }
 
 /* static */ void
 JitRuntime::Mark(JSTracer *trc)
 {
     JS_ASSERT(!trc->runtime->isHeapMinorCollecting());
     Zone *zone = trc->runtime->atomsCompartment()->zone();
     for (gc::CellIterUnderGC i(zone, gc::FINALIZE_JITCODE); !i.done(); i.next()) {
@@ -538,17 +543,17 @@ JitRuntime::Mark(JSTracer *trc)
 void
 JitCompartment::mark(JSTracer *trc, JSCompartment *compartment)
 {
     // Cancel any active or pending off thread compilations. Note that the
     // MIR graph does not hold any nursery pointers, so there's no need to
     // do this for minor GCs.
     JS_ASSERT(!trc->runtime->isHeapMinorCollecting());
     CancelOffThreadIonCompile(compartment, nullptr);
-    FinishAllOffThreadCompilations(this);
+    FinishAllOffThreadCompilations(compartment);
 
     // Free temporary OSR buffer.
     rt->freeOsrTempData();
 }
 
 void
 JitCompartment::sweep(FreeOp *fop)
 {
@@ -587,17 +592,17 @@ JitRuntime::getVMWrapper(const VMFunctio
 
     return p->value();
 }
 
 template <AllowGC allowGC>
 JitCode *
 JitCode::New(JSContext *cx, uint8_t *code, uint32_t bufferSize, JSC::ExecutablePool *pool)
 {
-    JitCode *codeObj = gc::NewGCThing<JitCode, allowGC>(cx, gc::FINALIZE_JITCODE, sizeof(JitCode), gc::DefaultHeap);
+    JitCode *codeObj = js::NewJitCode<allowGC>(cx);
     if (!codeObj) {
         pool->release();
         return nullptr;
     }
 
     new (codeObj) JitCode(code, bufferSize, pool);
     return codeObj;
 }
@@ -1507,79 +1512,97 @@ CompileBackEnd(MIRGenerator *mir, MacroA
     return GenerateCode(mir, lir, maybeMasm);
 }
 
 void
 AttachFinishedCompilations(JSContext *cx)
 {
 #ifdef JS_THREADSAFE
     JitCompartment *ion = cx->compartment()->jitCompartment();
-    if (!ion || !cx->runtime()->workerThreadState)
+    if (!ion)
         return;
 
     types::AutoEnterAnalysis enterTypes(cx);
-    AutoLockWorkerThreadState lock(*cx->runtime()->workerThreadState);
-
-    OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
-
-    // Incorporate any off thread compilations which have finished, failed or
-    // have been cancelled.
-    while (!compilations.empty()) {
-        IonBuilder *builder = compilations.popCopy();
+    AutoLockWorkerThreadState lock;
+
+    GlobalWorkerThreadState::IonBuilderVector &finished = WorkerThreadState().ionFinishedList();
+
+    // Incorporate any off thread compilations for the compartment which have
+    // finished, failed or have been cancelled.
+    while (true) {
+        IonBuilder *builder = nullptr;
+
+        // Find a finished builder for the compartment.
+        for (size_t i = 0; i < finished.length(); i++) {
+            IonBuilder *testBuilder = finished[i];
+            if (testBuilder->compartment == CompileCompartment::get(cx->compartment())) {
+                builder = testBuilder;
+                WorkerThreadState().remove(finished, &i);
+                break;
+            }
+        }
+        if (!builder)
+            break;
 
         if (CodeGenerator *codegen = builder->backgroundCodegen()) {
             RootedScript script(cx, builder->script());
             IonContext ictx(cx, &builder->alloc());
 
             // Root the assembler until the builder is finished below. As it
             // was constructed off thread, the assembler has not been rooted
             // previously, though any GC activity would discard the builder.
             codegen->masm.constructRoot(cx);
 
             bool success;
             {
                 // Release the worker thread lock and root the compiler for GC.
                 AutoTempAllocatorRooter root(cx, &builder->alloc());
-                AutoUnlockWorkerThreadState unlock(cx->runtime());
+                AutoUnlockWorkerThreadState unlock;
                 AutoFlushCache afc("AttachFinishedCompilations", cx->runtime()->jitRuntime());
                 success = codegen->link(cx, builder->constraints());
             }
 
             if (!success) {
                 // Silently ignore OOM during code generation, we're at an
                 // operation callback and can't propagate failures.
                 cx->clearPendingException();
             }
         } else {
             if (builder->abortReason() == AbortReason_Disable)
                 SetIonScript(builder->script(), builder->info().executionMode(), ION_DISABLED_SCRIPT);
         }
 
         FinishOffThreadBuilder(builder);
     }
-
-    compilations.clear();
 #endif
 }
 
 static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
 
 static inline bool
 OffThreadCompilationAvailable(JSContext *cx)
 {
+#ifdef JS_THREADSAFE
     // Even if off thread compilation is enabled, compilation must still occur
     // on the main thread in some cases. Do not compile off thread during an
     // incremental GC, as this may trip incremental read barriers.
     //
+    // Require cpuCount > 1 so that Ion compilation jobs and main-thread
+    // execution are not competing for the same resources.
+    //
     // Skip off thread compilation if PC count profiling is enabled, as
     // CodeGenerator::maybeCreateScriptCounts will not attach script profiles
     // when running off thread.
     return cx->runtime()->canUseParallelIonCompilation()
+        && WorkerThreadState().cpuCount > 1
         && cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL
         && !cx->runtime()->profilingScripts;
+#else
+    return false;
+#endif
 }
 
 static void
 TrackAllProperties(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->hasSingletonType());
 
     for (Shape::Range<NoGC> range(obj->lastProperty()); !range.empty(); range.popFront())
@@ -1827,17 +1850,22 @@ CheckScriptSize(JSContext *cx, JSScript*
         }
 
         return Method_Compiled;
     }
 
     if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE ||
         numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
     {
-        if (cx->runtime()->canUseParallelIonCompilation()) {
+#ifdef JS_THREADSAFE
+        size_t cpuCount = WorkerThreadState().cpuCount;
+#else
+        size_t cpuCount = 1;
+#endif
+        if (cx->runtime()->canUseParallelIonCompilation() && cpuCount > 1) {
             // Even if off thread compilation is enabled, there are cases where
             // compilation must still occur on the main thread. Don't compile
             // in these cases (except when profiling scripts, as compilations
             // occurring with profiling should reflect those without), but do
             // not forbid compilation so that the script may be compiled later.
             if (!OffThreadCompilationAvailable(cx) && !cx->runtime()->profilingScripts) {
                 IonSpew(IonSpew_Abort,
                         "Script too large for main thread, skipping (%u bytes) (%u locals/args)",
@@ -2484,17 +2512,17 @@ InvalidateActivation(FreeOp *fop, uint8_
 }
 
 void
 jit::StopAllOffThreadCompilations(JSCompartment *comp)
 {
     if (!comp->jitCompartment())
         return;
     CancelOffThreadIonCompile(comp, nullptr);
-    FinishAllOffThreadCompilations(comp->jitCompartment());
+    FinishAllOffThreadCompilations(comp);
 }
 
 void
 jit::InvalidateAll(FreeOp *fop, Zone *zone)
 {
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         StopAllOffThreadCompilations(comp);
 
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1836,76 +1836,76 @@ GetPropertyParIC::attachTypedArrayLength
     GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output());
 
     JS_ASSERT(!hasTypedArrayLengthStub_);
     hasTypedArrayLengthStub_ = true;
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array length");
 }
 
 bool
-GetPropertyParIC::update(ForkJoinSlice *slice, size_t cacheIndex,
+GetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex,
                          HandleObject obj, MutableHandleValue vp)
 {
-    AutoFlushCache afc("GetPropertyParCache", slice->runtime()->jitRuntime());
-
-    IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript();
+    AutoFlushCache afc("GetPropertyParCache", cx->runtime()->jitRuntime());
+
+    IonScript *ion = GetTopIonJSScript(cx)->parallelIonScript();
     GetPropertyParIC &cache = ion->getCache(cacheIndex).toGetPropertyPar();
 
     // Grab the property early, as the pure path is fast anyways and doesn't
     // need a lock. If we can't do it purely, bail out of parallel execution.
-    if (!GetPropertyPure(slice, obj, NameToId(cache.name()), vp.address()))
+    if (!GetPropertyPure(cx, obj, NameToId(cache.name()), vp.address()))
         return false;
 
     // Avoid unnecessary locking if cannot attach stubs.
     if (!cache.canAttachStub())
         return true;
 
     {
         // Lock the context before mutating the cache. Ideally we'd like to do
         // finer-grained locking, with one lock per cache. However, generating
         // new jitcode uses a global ExecutableAllocator tied to the runtime.
-        LockedJSContext cx(slice);
+        LockedJSContext ncx(cx);
 
         if (cache.canAttachStub()) {
             bool alreadyStubbed;
-            if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed))
-                return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+            if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
+                return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
             if (alreadyStubbed)
                 return true;
 
             // See note about the stub limit in GetPropertyCache.
             bool attachedStub = false;
 
             {
-                RootedShape shape(cx);
-                RootedObject holder(cx);
-                RootedPropertyName name(cx, cache.name());
+                RootedShape shape(ncx);
+                RootedObject holder(ncx);
+                RootedPropertyName name(ncx, cache.name());
 
                 GetPropertyIC::NativeGetPropCacheability canCache =
-                    CanAttachNativeGetProp(cx, cache, obj, name, &holder, &shape);
+                    CanAttachNativeGetProp(ncx, cache, obj, name, &holder, &shape);
 
                 if (canCache == GetPropertyIC::CanAttachReadSlot) {
-                    if (!cache.attachReadSlot(cx, ion, obj, holder, shape))
-                        return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+                    if (!cache.attachReadSlot(ncx, ion, obj, holder, shape))
+                        return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                     attachedStub = true;
                 }
 
                 if (!attachedStub && canCache == GetPropertyIC::CanAttachArrayLength) {
-                    if (!cache.attachArrayLength(cx, ion, obj))
-                        return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+                    if (!cache.attachArrayLength(ncx, ion, obj))
+                        return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                     attachedStub = true;
                 }
             }
 
             if (!attachedStub && !cache.hasTypedArrayLengthStub() &&
-                obj->is<TypedArrayObject>() && slice->names().length == cache.name() &&
+                obj->is<TypedArrayObject>() && cx->names().length == cache.name() &&
                 (cache.output().type() == MIRType_Value || cache.output().type() == MIRType_Int32))
             {
-                if (!cache.attachTypedArrayLength(cx, ion, obj))
-                    return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+                if (!cache.attachTypedArrayLength(ncx, ion, obj))
+                    return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
         }
     }
 
     return true;
 }
 
@@ -2832,93 +2832,93 @@ SetPropertyIC::update(JSContext *cx, siz
 void
 SetPropertyIC::reset()
 {
     RepatchIonCache::reset();
     hasGenericProxyStub_ = false;
 }
 
 bool
-SetPropertyParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+SetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
                          HandleValue value)
 {
-    JS_ASSERT(slice->isThreadLocal(obj));
-
-    AutoFlushCache afc("SetPropertyParCache", slice->runtime()->jitRuntime());
-
-    IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript();
+    JS_ASSERT(cx->isThreadLocal(obj));
+
+    AutoFlushCache afc("SetPropertyParCache", cx->runtime()->jitRuntime());
+
+    IonScript *ion = GetTopIonJSScript(cx)->parallelIonScript();
     SetPropertyParIC &cache = ion->getCache(cacheIndex).toSetPropertyPar();
 
-    RootedValue v(slice, value);
-    RootedId id(slice, AtomToId(cache.name()));
+    RootedValue v(cx, value);
+    RootedId id(cx, AtomToId(cache.name()));
 
     // Avoid unnecessary locking if cannot attach stubs.
     if (!cache.canAttachStub()) {
-        return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0,
+        return baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, 0,
                                                              &v, cache.strict());
     }
 
     SetPropertyIC::NativeSetPropCacheability canCache = SetPropertyIC::CanAttachNone;
     bool attachedStub = false;
 
     {
-        LockedJSContext cx(slice);
+        LockedJSContext ncx(cx);
 
         if (cache.canAttachStub()) {
             bool alreadyStubbed;
-            if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed))
-                return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+            if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
+                return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
             if (alreadyStubbed) {
-                return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0,
+                return baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, 0,
                                                                      &v, cache.strict());
             }
 
             // If the object has a lazy type, we need to de-lazify it, but
             // this is not safe in parallel.
             if (obj->hasLazyType())
                 return false;
 
             {
-                RootedShape shape(slice);
-                RootedObject holder(slice);
+                RootedShape shape(cx);
+                RootedObject holder(cx);
                 bool checkTypeset;
                 canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(),
                                                   &holder, &shape, &checkTypeset);
 
                 if (canCache == SetPropertyIC::CanAttachSetSlot) {
-                    if (!cache.attachSetSlot(cx, ion, obj, shape, checkTypeset))
-                        return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+                    if (!cache.attachSetSlot(ncx, ion, obj, shape, checkTypeset))
+                        return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                     attachedStub = true;
                 }
             }
         }
     }
 
     uint32_t oldSlots = obj->numDynamicSlots();
-    RootedShape oldShape(slice, obj->lastProperty());
-
-    if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, cache.strict()))
+    RootedShape oldShape(cx, obj->lastProperty());
+
+    if (!baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, 0, &v, cache.strict()))
         return false;
 
     bool checkTypeset;
     if (!attachedStub && canCache == SetPropertyIC::MaybeCanAttachAddSlot &&
         IsPropertyAddInlineable(obj, id, cache.value(), oldSlots, oldShape, cache.needsTypeBarrier(),
                                 &checkTypeset))
     {
-        LockedJSContext cx(slice);
-        if (cache.canAttachStub() && !cache.attachAddSlot(cx, ion, obj, oldShape, checkTypeset))
-            return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+        LockedJSContext ncx(cx);
+        if (cache.canAttachStub() && !cache.attachAddSlot(ncx, ion, obj, oldShape, checkTypeset))
+            return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
     }
 
     return true;
 }
 
 bool
 SetPropertyParIC::attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape,
-                                  bool checkTypeset)
+                                bool checkTypeset)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(),
                     checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel setting");
 }
 
@@ -3852,51 +3852,51 @@ SetElementParIC::attachTypedArrayElement
     {
         return false;
     }
 
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
 }
 
 bool
-SetElementParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+SetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
                         HandleValue idval, HandleValue value)
 {
-    IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript();
+    IonScript *ion = GetTopIonJSScript(cx)->parallelIonScript();
     SetElementParIC &cache = ion->getCache(cacheIndex).toSetElementPar();
 
     // Avoid unnecessary locking if cannot attach stubs.
     if (!cache.canAttachStub())
-        return SetElementPar(slice, obj, idval, value, cache.strict());
+        return SetElementPar(cx, obj, idval, value, cache.strict());
 
     {
-        LockedJSContext cx(slice);
+        LockedJSContext ncx(cx);
 
         if (cache.canAttachStub()) {
             bool alreadyStubbed;
-            if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed))
-                return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+            if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
+                return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
             if (alreadyStubbed)
-                return SetElementPar(slice, obj, idval, value, cache.strict());
+                return SetElementPar(cx, obj, idval, value, cache.strict());
 
             bool attachedStub = false;
             if (IsDenseElementSetInlineable(obj, idval)) {
-                if (!cache.attachDenseElement(cx, ion, obj, idval))
-                    return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+                if (!cache.attachDenseElement(ncx, ion, obj, idval))
+                    return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
             if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
                 TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
-                if (!cache.attachTypedArrayElement(cx, ion, tarr))
-                    return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+                if (!cache.attachTypedArrayElement(ncx, ion, tarr))
+                    return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
             }
         }
     }
 
-    return SetElementPar(slice, obj, idval, value, cache.strict());
+    return SetElementPar(cx, obj, idval, value, cache.strict());
 }
 
 bool
 GetElementParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj,
                                 const Value &idval, PropertyName *name, JSObject *holder,
                                 Shape *shape)
 {
     MacroAssembler masm(cx, ion);
@@ -3932,77 +3932,77 @@ GetElementParIC::attachTypedArrayElement
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output(),
                                  allowDoubleResult());
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
 }
 
 bool
-GetElementParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+GetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
                         HandleValue idval, MutableHandleValue vp)
 {
-    AutoFlushCache afc("GetElementParCache", slice->runtime()->jitRuntime());
-
-    IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript();
+    AutoFlushCache afc("GetElementParCache", cx->runtime()->jitRuntime());
+
+    IonScript *ion = GetTopIonJSScript(cx)->parallelIonScript();
     GetElementParIC &cache = ion->getCache(cacheIndex).toGetElementPar();
 
     // Try to get the element early, as the pure path doesn't need a lock. If
     // we can't do it purely, bail out of parallel execution.
-    if (!GetObjectElementOperationPure(slice, obj, idval, vp.address()))
+    if (!GetObjectElementOperationPure(cx, obj, idval, vp.address()))
         return false;
 
     // Avoid unnecessary locking if cannot attach stubs.
     if (!cache.canAttachStub())
         return true;
 
     {
-        LockedJSContext cx(slice);
+        LockedJSContext ncx(cx);
 
         if (cache.canAttachStub()) {
             bool alreadyStubbed;
-            if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed))
-                return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+            if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
+                return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
             if (alreadyStubbed)
                 return true;
 
             jsid id;
             if (!ValueToIdPure(idval, &id))
                 return false;
 
             bool attachedStub = false;
             if (cache.monitoredResult() &&
                 GetElementIC::canAttachGetProp(obj, idval, id))
             {
-                RootedShape shape(cx);
-                RootedObject holder(cx);
-                RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
+                RootedShape shape(ncx);
+                RootedObject holder(ncx);
+                RootedPropertyName name(ncx, JSID_TO_ATOM(id)->asPropertyName());
 
                 GetPropertyIC::NativeGetPropCacheability canCache =
-                    CanAttachNativeGetProp(cx, cache, obj, name, &holder, &shape);
+                    CanAttachNativeGetProp(ncx, cache, obj, name, &holder, &shape);
 
                 if (canCache == GetPropertyIC::CanAttachReadSlot)
                 {
-                    if (!cache.attachReadSlot(cx, ion, obj, idval, name, holder, shape))
-                        return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+                    if (!cache.attachReadSlot(ncx, ion, obj, idval, name, holder, shape))
+                        return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                     attachedStub = true;
                 }
             }
             if (!attachedStub &&
                 GetElementIC::canAttachDenseElement(obj, idval))
             {
-                if (!cache.attachDenseElement(cx, ion, obj, idval))
-                    return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+                if (!cache.attachDenseElement(ncx, ion, obj, idval))
+                    return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
             if (!attachedStub &&
                 GetElementIC::canAttachTypedArrayElement(obj, idval, cache.output()))
             {
-                if (!cache.attachTypedArrayElement(cx, ion, &obj->as<TypedArrayObject>(), idval))
-                    return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
+                if (!cache.attachTypedArrayElement(ncx, ion, &obj->as<TypedArrayObject>(), idval))
+                    return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
         }
     }
 
     return true;
 }
 
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -1069,17 +1069,17 @@ class GetPropertyParIC : public Parallel
     bool allowGetters() const { return false; }
     bool allowArrayLength(Context, HandleObject) const { return true; }
 
     bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, JSObject *holder,
                         Shape *shape);
     bool attachArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj);
     bool attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj);
 
-    static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+    static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
                        MutableHandleValue vp);
 };
 
 class GetElementParIC : public ParallelIonCache
 {
   protected:
     Register object_;
     ConstantOrRegister index_;
@@ -1130,17 +1130,17 @@ class GetElementParIC : public ParallelI
     bool allowArrayLength(Context, HandleObject) const { return false; }
 
     bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval,
                         PropertyName *name, JSObject *holder, Shape *shape);
     bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval);
     bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr,
                                  const Value &idval);
 
-    static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, HandleValue idval,
+    static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
                        MutableHandleValue vp);
 
 };
 
 class SetPropertyParIC : public ParallelIonCache
 {
   protected:
     Register object_;
@@ -1184,17 +1184,17 @@ class SetPropertyParIC : public Parallel
         return needsTypeBarrier_;
     }
 
     bool attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape,
                        bool checkTypeset);
     bool attachAddSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *oldShape,
                        bool checkTypeset);
 
-    static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+    static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
                        HandleValue value);
 };
 
 class SetElementParIC : public ParallelIonCache
 {
   protected:
     Register object_;
     Register tempToUnboxIndex_;
@@ -1251,17 +1251,17 @@ class SetElementParIC : public ParallelI
     }
     bool guardHoles() const {
         return guardHoles_;
     }
 
     bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval);
     bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr);
 
-    static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+    static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
                        HandleValue idval, HandleValue value);
 };
 
 #undef CACHE_HEADER
 
 // Implement cache casts now that the compiler can see the inheritance.
 #define CACHE_CASTS(ickind)                                             \
     ickind##IC &IonCache::to##ickind()                                  \
--- a/js/src/jit/IonFrames-inl.h
+++ b/js/src/jit/IonFrames-inl.h
@@ -83,19 +83,19 @@ GetTopBaselineFrame(JSContext *cx)
 
 inline JSScript *
 GetTopIonJSScript(JSContext *cx, void **returnAddrOut = nullptr)
 {
     return GetTopIonJSScript(cx->mainThread().ionTop, returnAddrOut, SequentialExecution);
 }
 
 inline JSScript *
-GetTopIonJSScript(ForkJoinSlice *slice, void **returnAddrOut = nullptr)
+GetTopIonJSScript(ForkJoinContext *cx, void **returnAddrOut = nullptr)
 {
-    return GetTopIonJSScript(slice->perThreadData->ionTop, returnAddrOut, ParallelExecution);
+    return GetTopIonJSScript(cx->perThreadData->ionTop, returnAddrOut, ParallelExecution);
 }
 
 } // namespace jit
 } // namespace js
 
 #endif // JS_ION
 
 #endif /* jit_IonFrames_inl_h */
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -683,25 +683,25 @@ HandleException(ResumeFromException *rfe
     }
 
     rfe->stackPointer = iter.fp();
 }
 
 void
 HandleParallelFailure(ResumeFromException *rfe)
 {
-    ForkJoinSlice *slice = ForkJoinSlice::current();
-    IonFrameIterator iter(slice->perThreadData->ionTop, ParallelExecution);
+    ForkJoinContext *cx = ForkJoinContext::current();
+    IonFrameIterator iter(cx->perThreadData->ionTop, ParallelExecution);
 
     parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry");
 
     while (!iter.isEntry()) {
         if (iter.isScripted()) {
-            slice->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM,
-                                              iter.script(), iter.script(), nullptr);
+            cx->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM,
+                                           iter.script(), iter.script(), nullptr);
             break;
         }
         ++iter;
     }
 
     while (!iter.isEntry()) {
         if (iter.isScripted())
             PropagateAbortPar(iter.script(), iter.script());
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -703,33 +703,33 @@ MacroAssembler::newGCString(const Regist
 
 void
 MacroAssembler::newGCShortString(const Register &result, Label *fail)
 {
     newGCThing(result, js::gc::FINALIZE_SHORT_STRING, fail);
 }
 
 void
-MacroAssembler::newGCThingPar(const Register &result, const Register &slice,
+MacroAssembler::newGCThingPar(const Register &result, const Register &cx,
                               const Register &tempReg1, const Register &tempReg2,
                               gc::AllocKind allocKind, Label *fail)
 {
     // Similar to ::newGCThing(), except that it allocates from a custom
-    // Allocator in the ForkJoinSlice*, rather than being hardcoded to the
+    // Allocator in the ForkJoinContext*, rather than being hardcoded to the
     // compartment allocator.  This requires two temporary registers.
     //
     // Subtle: I wanted to reuse `result` for one of the temporaries, but the
-    // register allocator was assigning it to the same register as `slice`.
+    // register allocator was assigning it to the same register as `cx`.
     // Then we overwrite that register which messed up the OOL code.
 
     uint32_t thingSize = (uint32_t)gc::Arena::thingSize(allocKind);
 
     // Load the allocator:
-    // tempReg1 = (Allocator*) forkJoinSlice->allocator()
-    loadPtr(Address(slice, ThreadSafeContext::offsetOfAllocator()),
+    // tempReg1 = (Allocator*) forkJoinCx->allocator()
+    loadPtr(Address(cx, ThreadSafeContext::offsetOfAllocator()),
             tempReg1);
 
     // Get a pointer to the relevant free list:
     // tempReg1 = (FreeSpan*) &tempReg1->arenas.freeLists[(allocKind)]
     uint32_t offset = (offsetof(Allocator, arenas) +
                        js::gc::ArenaLists::getFreeListOffset(allocKind));
     addPtr(Imm32(offset), tempReg1);
 
@@ -751,40 +751,40 @@ MacroAssembler::newGCThingPar(const Regi
     addPtr(Imm32(thingSize), tempReg2);
 
     // Update `first`
     // tempReg1->first = tempReg2;
     storePtr(tempReg2, Address(tempReg1, offsetof(gc::FreeSpan, first)));
 }
 
 void
-MacroAssembler::newGCThingPar(const Register &result, const Register &slice,
+MacroAssembler::newGCThingPar(const Register &result, const Register &cx,
                               const Register &tempReg1, const Register &tempReg2,
                               JSObject *templateObject, Label *fail)
 {
     gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
     JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
 
-    newGCThingPar(result, slice, tempReg1, tempReg2, allocKind, fail);
+    newGCThingPar(result, cx, tempReg1, tempReg2, allocKind, fail);
 }
 
 void
-MacroAssembler::newGCStringPar(const Register &result, const Register &slice,
+MacroAssembler::newGCStringPar(const Register &result, const Register &cx,
                                const Register &tempReg1, const Register &tempReg2,
                                Label *fail)
 {
-    newGCThingPar(result, slice, tempReg1, tempReg2, js::gc::FINALIZE_STRING, fail);
+    newGCThingPar(result, cx, tempReg1, tempReg2, js::gc::FINALIZE_STRING, fail);
 }
 
 void
-MacroAssembler::newGCShortStringPar(const Register &result, const Register &slice,
+MacroAssembler::newGCShortStringPar(const Register &result, const Register &cx,
                                     const Register &tempReg1, const Register &tempReg2,
                                     Label *fail)
 {
-    newGCThingPar(result, slice, tempReg1, tempReg2, js::gc::FINALIZE_SHORT_STRING, fail);
+    newGCThingPar(result, cx, tempReg1, tempReg2, js::gc::FINALIZE_SHORT_STRING, fail);
 }
 
 void
 MacroAssembler::initGCThing(const Register &obj, JSObject *templateObject)
 {
     // Fast initialization of an empty object returned by NewGCThing().
 
     AutoThreadSafeAccess ts0(templateObject);
@@ -1089,80 +1089,80 @@ void
 MacroAssembler::loadBaselineFramePtr(Register framePtr, Register dest)
 {
     if (framePtr != dest)
         movePtr(framePtr, dest);
     subPtr(Imm32(BaselineFrame::Size()), dest);
 }
 
 void
-MacroAssembler::loadForkJoinSlice(Register slice, Register scratch)
+MacroAssembler::loadForkJoinContext(Register cx, Register scratch)
 {
-    // Load the current ForkJoinSlice *. If we need a parallel exit frame,
+    // Load the current ForkJoinContext *. If we need a parallel exit frame,
     // chances are we are about to do something very slow anyways, so just
-    // call ForkJoinSlicePar again instead of using the cached version.
+    // call ForkJoinContextPar again instead of using the cached version.
     setupUnalignedABICall(0, scratch);
-    callWithABI(JS_FUNC_TO_DATA_PTR(void *, ForkJoinSlicePar));
-    if (ReturnReg != slice)
-        movePtr(ReturnReg, slice);
+    callWithABI(JS_FUNC_TO_DATA_PTR(void *, ForkJoinContextPar));
+    if (ReturnReg != cx)
+        movePtr(ReturnReg, cx);
 }
 
 void
 MacroAssembler::loadContext(Register cxReg, Register scratch, ExecutionMode executionMode)
 {
     switch (executionMode) {
       case SequentialExecution:
         // The scratch register is not used for sequential execution.
         loadJSContext(cxReg);
         break;
       case ParallelExecution:
-        loadForkJoinSlice(cxReg, scratch);
+        loadForkJoinContext(cxReg, scratch);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 }
 
 void
-MacroAssembler::enterParallelExitFrameAndLoadSlice(const VMFunction *f, Register slice,
-                                                   Register scratch)
+MacroAssembler::enterParallelExitFrameAndLoadContext(const VMFunction *f, Register cx,
+                                                     Register scratch)
 {
-    loadForkJoinSlice(slice, scratch);
-    // Load the PerThreadData from from the slice.
-    loadPtr(Address(slice, offsetof(ForkJoinSlice, perThreadData)), scratch);
+    loadForkJoinContext(cx, scratch);
+    // Load the PerThreadData from from the cx.
+    loadPtr(Address(cx, offsetof(ForkJoinContext, perThreadData)), scratch);
     linkParallelExitFrame(scratch);
     // Push the ioncode.
     exitCodePatch_ = PushWithPatch(ImmWord(-1));
     // Push the VMFunction pointer, to mark arguments.
     Push(ImmPtr(f));
 }
 
 void
-MacroAssembler::enterFakeParallelExitFrame(Register slice, Register scratch,
+MacroAssembler::enterFakeParallelExitFrame(Register cx, Register scratch,
                                            JitCode *codeVal)
 {
-    // Load the PerThreadData from from the slice.
-    loadPtr(Address(slice, offsetof(ForkJoinSlice, perThreadData)), scratch);
+    // Load the PerThreadData from from the cx.
+    loadPtr(Address(cx, offsetof(ForkJoinContext, perThreadData)), scratch);
     linkParallelExitFrame(scratch);
     Push(ImmPtr(codeVal));
     Push(ImmPtr(nullptr));
 }
 
 void
 MacroAssembler::enterExitFrameAndLoadContext(const VMFunction *f, Register cxReg, Register scratch,
                                              ExecutionMode executionMode)
 {
     switch (executionMode) {
       case SequentialExecution:
         // The scratch register is not used for sequential execution.
         enterExitFrame(f);
         loadJSContext(cxReg);
         break;
       case ParallelExecution:
-        enterParallelExitFrameAndLoadSlice(f, cxReg, scratch);
+        enterParallelExitFrameAndLoadContext(f, cxReg, scratch);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 }
 
 void
 MacroAssembler::enterFakeExitFrame(Register cxReg, Register scratch,
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -771,26 +771,26 @@ class MacroAssembler : public MacroAssem
     // Inline allocation.
     void newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail,
                     gc::InitialHeap initialHeap = gc::DefaultHeap);
     void newGCThing(const Register &result, JSObject *templateObject, Label *fail,
                     gc::InitialHeap initialHeap);
     void newGCString(const Register &result, Label *fail);
     void newGCShortString(const Register &result, Label *fail);
 
-    void newGCThingPar(const Register &result, const Register &slice,
+    void newGCThingPar(const Register &result, const Register &cx,
                        const Register &tempReg1, const Register &tempReg2,
                        gc::AllocKind allocKind, Label *fail);
-    void newGCThingPar(const Register &result, const Register &slice,
+    void newGCThingPar(const Register &result, const Register &cx,
                        const Register &tempReg1, const Register &tempReg2,
                        JSObject *templateObject, Label *fail);
-    void newGCStringPar(const Register &result, const Register &slice,
+    void newGCStringPar(const Register &result, const Register &cx,
                         const Register &tempReg1, const Register &tempReg2,
                         Label *fail);
-    void newGCShortStringPar(const Register &result, const Register &slice,
+    void newGCShortStringPar(const Register &result, const Register &cx,
                              const Register &tempReg1, const Register &tempReg2,
                              Label *fail);
     void initGCThing(const Register &obj, JSObject *templateObject);
 
     // Compares two strings for equality based on the JSOP.
     // This checks for identical pointers, atoms and length and fails for everything else.
     void compareStrings(JSOp op, Register left, Register right, Register result,
                         Register temp, Label *fail);
@@ -814,26 +814,26 @@ class MacroAssembler : public MacroAssem
         Push(ImmPtr(f));
     }
     void enterFakeExitFrame(JitCode *codeVal = nullptr) {
         linkExitFrame();
         Push(ImmPtr(codeVal));
         Push(ImmPtr(nullptr));
     }
 
-    void loadForkJoinSlice(Register slice, Register scratch);
+    void loadForkJoinContext(Register cx, Register scratch);
     void loadContext(Register cxReg, Register scratch, ExecutionMode executionMode);
 
-    void enterParallelExitFrameAndLoadSlice(const VMFunction *f, Register slice,
-                                            Register scratch);
+    void enterParallelExitFrameAndLoadContext(const VMFunction *f, Register cx,
+                                              Register scratch);
 
     void enterExitFrameAndLoadContext(const VMFunction *f, Register cxReg, Register scratch,
                                       ExecutionMode executionMode);
 
-    void enterFakeParallelExitFrame(Register slice, Register scratch,
+    void enterFakeParallelExitFrame(Register cx, Register scratch,
                                     JitCode *codeVal = nullptr);
 
     void enterFakeExitFrame(Register cxReg, Register scratch,
                             ExecutionMode executionMode,
                             JitCode *codeVal = nullptr);
 
     void leaveExitFrame() {
         freeStack(IonExitFooterFrame::Size());
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -84,18 +84,18 @@ enum MIRType
     MIRType_Object,
     MIRType_Magic,
     MIRType_Value,
     MIRType_None,          // Invalid, used as a placeholder.
     MIRType_Slots,         // A slots vector
     MIRType_Elements,      // An elements vector
     MIRType_Pointer,       // An opaque pointer that receives no special treatment
     MIRType_Shape,         // A Shape pointer.
-    MIRType_ForkJoinSlice, // js::ForkJoinSlice*
-    MIRType_Last = MIRType_ForkJoinSlice,
+    MIRType_ForkJoinContext, // js::ForkJoinContext*
+    MIRType_Last = MIRType_ForkJoinContext,
     MIRType_Float32x4 = MIRType_Float32 | (2 << VECTOR_SCALE_SHIFT),
     MIRType_Int32x4   = MIRType_Int32   | (2 << VECTOR_SCALE_SHIFT),
     MIRType_Doublex2  = MIRType_Double  | (1 << VECTOR_SCALE_SHIFT)
 };
 
 static inline MIRType
 ElementType(MIRType type)
 {
@@ -194,18 +194,18 @@ StringFromMIRType(MIRType type)
     case MIRType_None:
       return "None";
     case MIRType_Slots:
       return "Slots";
     case MIRType_Elements:
       return "Elements";
     case MIRType_Pointer:
       return "Pointer";
-    case MIRType_ForkJoinSlice:
-      return "ForkJoinSlice";
+    case MIRType_ForkJoinContext:
+      return "ForkJoinContext";
     default:
       MOZ_ASSUME_UNREACHABLE("Unknown MIRType.");
   }
 }
 
 static inline bool
 IsNumberType(MIRType type)
 {
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -54,18 +54,16 @@ struct EnterJitData
 };
 
 typedef void (*EnterJitCode)(void *code, unsigned argc, Value *argv, StackFrame *fp,
                              CalleeToken calleeToken, JSObject *scopeChain,
                              size_t numStackValues, Value *vp);
 
 class IonBuilder;
 
-typedef Vector<IonBuilder*, 0, SystemAllocPolicy> OffThreadCompilationVector;
-
 // ICStubSpace is an abstraction for allocation policy and storage for stub data.
 // There are two kinds of stubs: optimized stubs and fallback stubs (the latter
 // also includes stubs that can make non-tail calls that can GC).
 //
 // Optimized stubs are allocated per-compartment and are always purged when
 // JIT-code is discarded. Fallback stubs are allocated per BaselineScript and
 // are only destroyed when the BaselineScript is destroyed.
 class ICStubSpace
@@ -327,22 +325,16 @@ class JitRuntime
 
 class JitCompartment
 {
     friend class JitActivation;
 
     // Ion state for the compartment's runtime.
     JitRuntime *rt;
 
-    // Any scripts for which off thread compilation has successfully finished,
-    // failed, or been cancelled. All off thread compilations which are started
-    // will eventually appear in this list asynchronously. Protected by the
-    // runtime's analysis lock.
-    OffThreadCompilationVector finishedOffThreadCompilations_;
-
     // Map ICStub keys to ICStub shared code objects.
     typedef WeakValueCache<uint32_t, ReadBarriered<JitCode> > ICStubCodeMap;
     ICStubCodeMap *stubCodes_;
 
     // Keep track of offset into various baseline stubs' code at return
     // point from called script.
     void *baselineCallReturnAddr_;
     void *baselineGetPropReturnAddr_;
@@ -356,20 +348,16 @@ class JitCompartment
     // pointers. This has to be a weak pointer to avoid keeping the whole
     // compartment alive.
     ReadBarriered<JitCode> stringConcatStub_;
     ReadBarriered<JitCode> parallelStringConcatStub_;
 
     JitCode *generateStringConcatStub(JSContext *cx, ExecutionMode mode);
 
   public:
-    OffThreadCompilationVector &finishedOffThreadCompilations() {
-        return finishedOffThreadCompilations_;
-    }
-
     JitCode *getStubCode(uint32_t key) {
         ICStubCodeMap::AddPtr p = stubCodes_->lookupForAdd(key);
         if (p)
             return p->value();
         return nullptr;
     }
     bool putStubCode(uint32_t key, Handle<JitCode *> stubCode) {
         // Make sure to do a lookupForAdd(key) and then insert into that slot, because
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -356,27 +356,27 @@ class LNewObject : public LInstructionHe
     }
 };
 
 class LNewPar : public LInstructionHelper<1, 1, 2>
 {
   public:
     LIR_HEADER(NewPar);
 
-    LNewPar(const LAllocation &slice, const LDefinition &temp1, const LDefinition &temp2) {
-        setOperand(0, slice);
+    LNewPar(const LAllocation &cx, const LDefinition &temp1, const LDefinition &temp2) {
+        setOperand(0, cx);
         setTemp(0, temp1);
         setTemp(1, temp2);
     }
 
     MNewPar *mir() const {
         return mir_->toNewPar();
     }
 
-    const LAllocation *forkJoinSlice() {
+    const LAllocation *forkJoinContext() {
         return getOperand(0);
     }
 
     const LDefinition *getTemp0() {
         return getTemp(0);
     }
 
     const LDefinition *getTemp1() {
@@ -384,31 +384,31 @@ class LNewPar : public LInstructionHelpe
     }
 };
 
 class LNewDenseArrayPar : public LCallInstructionHelper<1, 2, 3>
 {
   public:
     LIR_HEADER(NewDenseArrayPar);
 
-    LNewDenseArrayPar(const LAllocation &slice, const LAllocation &length,
+    LNewDenseArrayPar(const LAllocation &cx, const LAllocation &length,
                       const LDefinition &temp1, const LDefinition &temp2, const LDefinition &temp3)
     {
-        setOperand(0, slice);
+        setOperand(0, cx);
         setOperand(1, length);
         setTemp(0, temp1);
         setTemp(1, temp2);
         setTemp(2, temp3);
     }
 
     MNewDenseArrayPar *mir() const {
         return mir_->toNewDenseArrayPar();
     }
 
-    const LAllocation *forkJoinSlice() {
+    const LAllocation *forkJoinContext() {
         return getOperand(0);
     }
 
     const LAllocation *length() {
         return getOperand(1);
     }
 
     const LDefinition *getTemp0() {
@@ -463,44 +463,44 @@ class LNewCallObject : public LInstructi
     }
     MNewCallObject *mir() const {
         return mir_->toNewCallObject();
     }
 };
 
 class LNewCallObjectPar : public LInstructionHelper<1, 2, 2>
 {
-    LNewCallObjectPar(const LAllocation &slice, const LAllocation &slots,
+    LNewCallObjectPar(const LAllocation &cx, const LAllocation &slots,
                       const LDefinition &temp1, const LDefinition &temp2)
     {
-        setOperand(0, slice);
+        setOperand(0, cx);
         setOperand(1, slots);
         setTemp(0, temp1);
         setTemp(1, temp2);
     }
 
 public:
     LIR_HEADER(NewCallObjectPar);
 
     static LNewCallObjectPar *NewWithSlots(TempAllocator &alloc,
-                                           const LAllocation &slice, const LAllocation &slots,
+                                           const LAllocation &cx, const LAllocation &slots,
                                            const LDefinition &temp1, const LDefinition &temp2)
     {
-        return new(alloc) LNewCallObjectPar(slice, slots, temp1, temp2);
+        return new(alloc) LNewCallObjectPar(cx, slots, temp1, temp2);
     }
 
     static LNewCallObjectPar *NewSansSlots(TempAllocator &alloc,
-                                           const LAllocation &slice,
+                                           const LAllocation &cx,
                                            const LDefinition &temp1, const LDefinition &temp2)
     {
         LAllocation slots = LConstantIndex::Bogus();
-        return new(alloc) LNewCallObjectPar(slice, slots, temp1, temp2);
-    }
-
-    const LAllocation *forkJoinSlice() {
+        return new(alloc) LNewCallObjectPar(cx, slots, temp1, temp2);
+    }
+
+    const LAllocation *forkJoinContext() {
         return getOperand(0);
     }
 
     const LAllocation *slots() {
         return getOperand(1);
     }
 
     const bool hasDynamicSlots() {
@@ -695,22 +695,22 @@ class LCheckOverRecursed : public LInstr
     { }
 };
 
 class LCheckOverRecursedPar : public LInstructionHelper<0, 1, 1>
 {
   public:
     LIR_HEADER(CheckOverRecursedPar);
 
-    LCheckOverRecursedPar(const LAllocation &slice, const LDefinition &tempReg) {
-        setOperand(0, slice);
+    LCheckOverRecursedPar(const LAllocation &cx, const LDefinition &tempReg) {
+        setOperand(0, cx);
         setTemp(0, tempReg);
     }
 
-    const LAllocation *forkJoinSlice() {
+    const LAllocation *forkJoinContext() {
         return getOperand(0);
     }
 
     const LDefinition *getTempReg() {
         return getTemp(0);
     }
 };
 
@@ -737,22 +737,22 @@ class LInterruptCheckImplicit : public L
     }
 };
 
 class LCheckInterruptPar : public LInstructionHelper<0, 1, 1>
 {
   public:
     LIR_HEADER(CheckInterruptPar);
 
-    LCheckInterruptPar(const LAllocation &slice, const LDefinition &tempReg) {
-        setOperand(0, slice);
+    LCheckInterruptPar(const LAllocation &cx, const LDefinition &tempReg) {
+        setOperand(0, cx);
         setTemp(0, tempReg);
     }
 
-    const LAllocation *forkJoinSlice() {
+    const LAllocation *forkJoinContext() {
         return getOperand(0);
     }
 
     const LDefinition *getTempReg() {
         return getTemp(0);
     }
 };
 
@@ -2809,30 +2809,30 @@ class LConcat : public LInstructionHelpe
     }
 };
 
 class LConcatPar : public LInstructionHelper<1, 3, 4>
 {
   public:
     LIR_HEADER(ConcatPar)
 
-    LConcatPar(const LAllocation &slice, const LAllocation &lhs, const LAllocation &rhs,
+    LConcatPar(const LAllocation &cx, const LAllocation &lhs, const LAllocation &rhs,
                const LDefinition &temp1, const LDefinition &temp2, const LDefinition &temp3,
                const LDefinition &temp4)
     {
-        setOperand(0, slice);
+        setOperand(0, cx);
         setOperand(1, lhs);
         setOperand(2, rhs);
         setTemp(0, temp1);
         setTemp(1, temp2);
         setTemp(2, temp3);
         setTemp(3, temp4);
     }
 
-    const LAllocation *forkJoinSlice() {
+    const LAllocation *forkJoinContext() {
         return this->getOperand(0);
     }
     const LAllocation *lhs() {
         return this->getOperand(1);
     }
     const LAllocation *rhs() {
         return this->getOperand(2);
     }
@@ -3412,25 +3412,25 @@ class LLambda : public LInstructionHelpe
     }
 };
 
 class LLambdaPar : public LInstructionHelper<1, 2, 2>
 {
   public:
     LIR_HEADER(LambdaPar);
 
-    LLambdaPar(const LAllocation &slice, const LAllocation &scopeChain,
+    LLambdaPar(const LAllocation &cx, const LAllocation &scopeChain,
                const LDefinition &temp1, const LDefinition &temp2)
     {
-        setOperand(0, slice);
+        setOperand(0, cx);
         setOperand(1, scopeChain);
         setTemp(0, temp1);
         setTemp(1, temp2);
     }
-    const LAllocation *forkJoinSlice() {
+    const LAllocation *forkJoinContext() {
         return getOperand(0);
     }
     const LAllocation *scopeChain() {
         return getOperand(1);
     }
     const MLambdaPar *mir() const {
         return mir_->toLambdaPar();
     }
@@ -4744,22 +4744,22 @@ class LFunctionEnvironment : public LIns
     LFunctionEnvironment(const LAllocation &function) {
         setOperand(0, function);
     }
     const LAllocation *function() {
         return getOperand(0);
     }
 };
 
-class LForkJoinSlice : public LCallInstructionHelper<1, 0, 1>
-{
-  public:
-    LIR_HEADER(ForkJoinSlice);
-
-    LForkJoinSlice(const LDefinition &temp1) {
+class LForkJoinContext : public LCallInstructionHelper<1, 0, 1>
+{
+  public:
+    LIR_HEADER(ForkJoinContext);
+
+    LForkJoinContext(const LDefinition &temp1) {
         setTemp(0, temp1);
     }
 
     const LDefinition *getTempReg() {
         return getTemp(0);
     }
 };
 
@@ -5208,48 +5208,48 @@ class LRest : public LCallInstructionHel
     }
 };
 
 class LRestPar : public LCallInstructionHelper<1, 2, 3>
 {
   public:
     LIR_HEADER(RestPar);
 
-    LRestPar(const LAllocation &slice, const LAllocation &numActuals,
+    LRestPar(const LAllocation &cx, const LAllocation &numActuals,
              const LDefinition &temp1, const LDefinition &temp2, const LDefinition &temp3)
     {
-        setOperand(0, slice);
+        setOperand(0, cx);
         setOperand(1, numActuals);
         setTemp(0, temp1);
         setTemp(1, temp2);
         setTemp(2, temp3);
     }
-    const LAllocation *forkJoinSlice() {
+    const LAllocation *forkJoinContext() {
         return getOperand(0);
     }
     const LAllocation *numActuals() {
         return getOperand(1);
     }
     MRestPar *mir() const {
         return mir_->toRestPar();
     }
 };
 
 class LGuardThreadExclusive : public LCallInstructionHelper<0, 2, 1>
 {
   public:
     LIR_HEADER(GuardThreadExclusive);
 
-    LGuardThreadExclusive(const LAllocation &slice, const LAllocation &object, const LDefinition &temp1) {
-        setOperand(0, slice);
+    LGuardThreadExclusive(const LAllocation &cx, const LAllocation &object, const LDefinition &temp1) {
+        setOperand(0, cx);
         setOperand(1, object);
         setTemp(0, temp1);
     }
 
-    const LAllocation *forkJoinSlice() {
+    const LAllocation *forkJoinContext() {
         return getOperand(0);
     }
 
     const LAllocation *object() {
         return getOperand(1);
     }
 
     const LDefinition *getTempReg() {
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -541,17 +541,17 @@ class LDefinition
           case MIRType_Value:
             return LDefinition::BOX;
 #endif
           case MIRType_Slots:
           case MIRType_Elements:
             return LDefinition::SLOTS;
           case MIRType_Pointer:
             return LDefinition::GENERAL;
-          case MIRType_ForkJoinSlice:
+          case MIRType_ForkJoinContext:
             return LDefinition::GENERAL;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected type");
         }
     }
 };
 
 // Forward declarations of LIR types.
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -207,17 +207,17 @@
     _(ClampIToUint8)                \
     _(ClampDToUint8)                \
     _(ClampVToUint8)                \
     _(LoadFixedSlotV)               \
     _(LoadFixedSlotT)               \
     _(StoreFixedSlotV)              \
     _(StoreFixedSlotT)              \
     _(FunctionEnvironment)          \
-    _(ForkJoinSlice)                \
+    _(ForkJoinContext)              \
     _(GetPropertyCacheV)            \
     _(GetPropertyCacheT)            \
     _(GetPropertyPolymorphicV)      \
     _(GetPropertyPolymorphicT)      \
     _(GetElementCacheV)             \
     _(GetElementCacheT)             \
     _(BindNameCache)                \
     _(CallGetProperty)              \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -128,17 +128,17 @@ LIRGenerator::visitCheckOverRecursed(MCh
 
     return true;
 }
 
 bool
 LIRGenerator::visitCheckOverRecursedPar(MCheckOverRecursedPar *ins)
 {
     LCheckOverRecursedPar *lir =
-        new(alloc()) LCheckOverRecursedPar(useRegister(ins->forkJoinSlice()), temp());
+        new(alloc()) LCheckOverRecursedPar(useRegister(ins->forkJoinContext()), temp());
     if (!add(lir, ins))
         return false;
     if (!assignSafepoint(lir, ins))
         return false;
     return true;
 }
 
 bool
@@ -219,17 +219,17 @@ LIRGenerator::visitNewDerivedTypedObject
                                             useRegisterAtStart(ins->owner()),
                                             useRegisterAtStart(ins->offset()));
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitNewCallObjectPar(MNewCallObjectPar *ins)
 {
-    const LAllocation &parThreadContext = useRegister(ins->forkJoinSlice());
+    const LAllocation &parThreadContext = useRegister(ins->forkJoinContext());
     const LDefinition &temp1 = temp();
     const LDefinition &temp2 = temp();
 
     LNewCallObjectPar *lir;
     if (ins->slots()->type() == MIRType_Slots) {
         const LAllocation &slots = useRegister(ins->slots());
         lir = LNewCallObjectPar::NewWithSlots(alloc(), parThreadContext, slots, temp1, temp2);
     } else {
@@ -1545,25 +1545,25 @@ LIRGenerator::visitConcat(MConcat *ins)
     if (!defineFixed(lir, ins, LAllocation(AnyRegister(CallTempReg5))))
         return false;
     return assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitConcatPar(MConcatPar *ins)
 {
-    MDefinition *slice = ins->forkJoinSlice();
+    MDefinition *cx = ins->forkJoinContext();
     MDefinition *lhs = ins->lhs();
     MDefinition *rhs = ins->rhs();
 
     JS_ASSERT(lhs->type() == MIRType_String);
     JS_ASSERT(rhs->type() == MIRType_String);
     JS_ASSERT(ins->type() == MIRType_String);
 
-    LConcatPar *lir = new(alloc()) LConcatPar(useFixed(slice, CallTempReg4),
+    LConcatPar *lir = new(alloc()) LConcatPar(useFixed(cx, CallTempReg4),
                                               useFixedAtStart(lhs, CallTempReg0),
                                               useFixedAtStart(rhs, CallTempReg1),
                                               tempFixed(CallTempReg0),
                                               tempFixed(CallTempReg1),
                                               tempFixed(CallTempReg2),
                                               tempFixed(CallTempReg3));
     if (!defineFixed(lir, ins, LAllocation(AnyRegister(CallTempReg5))))
         return false;
@@ -2021,17 +2021,17 @@ LIRGenerator::visitLambda(MLambda *ins)
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitLambdaPar(MLambdaPar *ins)
 {
     JS_ASSERT(!ins->info().singletonType);
     JS_ASSERT(!ins->info().useNewTypeForClone);
-    LLambdaPar *lir = new(alloc()) LLambdaPar(useRegister(ins->forkJoinSlice()),
+    LLambdaPar *lir = new(alloc()) LLambdaPar(useRegister(ins->forkJoinContext()),
                                               useRegister(ins->scopeChain()),
                                               temp(), temp());
     return define(lir, ins);
 }
 
 bool
 LIRGenerator::visitImplicitThis(MImplicitThis *ins)
 {
@@ -2096,31 +2096,31 @@ LIRGenerator::visitLoadSlot(MLoadSlot *i
 
 bool
 LIRGenerator::visitFunctionEnvironment(MFunctionEnvironment *ins)
 {
     return define(new(alloc()) LFunctionEnvironment(useRegisterAtStart(ins->function())), ins);
 }
 
 bool
-LIRGenerator::visitForkJoinSlice(MForkJoinSlice *ins)
+LIRGenerator::visitForkJoinContext(MForkJoinContext *ins)
 {
-    LForkJoinSlice *lir = new(alloc()) LForkJoinSlice(tempFixed(CallTempReg0));
+    LForkJoinContext *lir = new(alloc()) LForkJoinContext(tempFixed(CallTempReg0));
     return defineReturn(lir, ins);
 }
 
 bool
 LIRGenerator::visitGuardThreadExclusive(MGuardThreadExclusive *ins)
 {
     // FIXME (Bug 956281) -- For now, we always generate the most
     // general form of write guard check. we could employ TI feedback
     // to optimize this if we know that the object being tested is a
     // typed object or know that it is definitely NOT a typed object.
     LGuardThreadExclusive *lir =
-        new(alloc()) LGuardThreadExclusive(useFixed(ins->forkJoinSlice(), CallTempReg0),
+        new(alloc()) LGuardThreadExclusive(useFixed(ins->forkJoinContext(), CallTempReg0),
                                            useFixed(ins->object(), CallTempReg1),
                                            tempFixed(CallTempReg2));
     lir->setMir(ins);
     return add(lir, ins);
 }
 
 bool
 LIRGenerator::visitInterruptCheck(MInterruptCheck *ins)
@@ -2138,36 +2138,36 @@ LIRGenerator::visitInterruptCheck(MInter
     LInterruptCheck *lir = new(alloc()) LInterruptCheck();
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitCheckInterruptPar(MCheckInterruptPar *ins)
 {
     LCheckInterruptPar *lir =
-        new(alloc()) LCheckInterruptPar(useRegister(ins->forkJoinSlice()), temp());
+        new(alloc()) LCheckInterruptPar(useRegister(ins->forkJoinContext()), temp());
     if (!add(lir, ins))
         return false;
     if (!assignSafepoint(lir, ins))
         return false;
     return true;
 }
 
 bool
 LIRGenerator::visitNewPar(MNewPar *ins)
 {
-    LNewPar *lir = new(alloc()) LNewPar(useRegister(ins->forkJoinSlice()), temp(), temp());
+    LNewPar *lir = new(alloc()) LNewPar(useRegister(ins->forkJoinContext()), temp(), temp());
     return define(lir, ins);
 }
 
 bool
 LIRGenerator::visitNewDenseArrayPar(MNewDenseArrayPar *ins)
 {
     LNewDenseArrayPar *lir =
-        new(alloc()) LNewDenseArrayPar(useFixed(ins->forkJoinSlice(), CallTempReg0),
+        new(alloc()) LNewDenseArrayPar(useFixed(ins->forkJoinContext(), CallTempReg0),
                                        useFixed(ins->length(), CallTempReg1),
                                        tempFixed(CallTempReg2),
                                        tempFixed(CallTempReg3),
                                        tempFixed(CallTempReg4));
     return defineReturn(lir, ins);
 }
 
 bool
@@ -3218,17 +3218,17 @@ LIRGenerator::visitRest(MRest *ins)
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitRestPar(MRestPar *ins)
 {
     JS_ASSERT(ins->numActuals()->type() == MIRType_Int32);
 
-    LRestPar *lir = new(alloc()) LRestPar(useFixed(ins->forkJoinSlice(), CallTempReg0),
+    LRestPar *lir = new(alloc()) LRestPar(useFixed(ins->forkJoinContext(), CallTempReg0),
                                           useFixed(ins->numActuals(), CallTempReg1),
                                           tempFixed(CallTempReg2),
                                           tempFixed(CallTempReg3),
                                           tempFixed(CallTempReg4));
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -156,17 +156,17 @@ class LIRGenerator : public LIRGenerator
     bool visitImplicitThis(MImplicitThis *ins);
     bool visitSlots(MSlots *ins);
     bool visitElements(MElements *ins);
     bool visitConstantElements(MConstantElements *ins);
     bool visitConvertElementsToDoubles(MConvertElementsToDoubles *ins);
     bool visitMaybeToDoubleElement(MMaybeToDoubleElement *ins);
     bool visitLoadSlot(MLoadSlot *ins);
     bool visitFunctionEnvironment(MFunctionEnvironment *ins);
-    bool visitForkJoinSlice(MForkJoinSlice *ins);
+    bool visitForkJoinContext(MForkJoinContext *ins);
     bool visitGuardThreadExclusive(MGuardThreadExclusive *ins);
     bool visitInterruptCheck(MInterruptCheck *ins);
     bool visitCheckInterruptPar(MCheckInterruptPar *ins);
     bool visitStoreSlot(MStoreSlot *ins);
     bool visitTypeBarrier(MTypeBarrier *ins);
     bool visitMonitorTypes(MMonitorTypes *ins);
     bool visitPostWriteBarrier(MPostWriteBarrier *ins);
     bool visitArrayLength(MArrayLength *ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1412,17 +1412,17 @@ IonBuilder::inlineNewDenseArrayForParall
 
     JSObject *templateObject = inspector->getTemplateObjectForNative(pc, intrinsic_NewDenseArray);
     if (!templateObject || templateObject->type() != typeObject)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MNewDenseArrayPar *newObject = MNewDenseArrayPar::New(alloc(),
-                                                          graph().forkJoinSlice(),
+                                                          graph().forkJoinContext(),
                                                           callInfo.getArg(0),
                                                           templateObject);
     current->add(newObject);
     current->push(newObject);
 
     return InliningStatus_Inlined;
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1512,31 +1512,31 @@ class MNewObject : public MNullaryInstru
     }
 };
 
 // Could be allocating either a new array or a new object.
 class MNewPar : public MUnaryInstruction
 {
     CompilerRootObject templateObject_;
 
-    MNewPar(MDefinition *slice, JSObject *templateObject)
-      : MUnaryInstruction(slice),
+    MNewPar(MDefinition *cx, JSObject *templateObject)
+      : MUnaryInstruction(cx),
         templateObject_(templateObject)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewPar);
 
-    static MNewPar *New(TempAllocator &alloc, MDefinition *slice, JSObject *templateObject) {
-        return new(alloc) MNewPar(slice, templateObject);
-    }
-
-    MDefinition *forkJoinSlice() const {
+    static MNewPar *New(TempAllocator &alloc, MDefinition *cx, JSObject *templateObject) {
+        return new(alloc) MNewPar(cx, templateObject);
+    }
+
+    MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 
     JSObject *templateObject() const {
         return templateObject_;
     }
 };
 
@@ -4256,35 +4256,35 @@ class MConcat
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 class MConcatPar
   : public MTernaryInstruction
 {
-    MConcatPar(MDefinition *slice, MDefinition *left, MDefinition *right)
-      : MTernaryInstruction(slice, left, right)
+    MConcatPar(MDefinition *cx, MDefinition *left, MDefinition *right)
+      : MTernaryInstruction(cx, left, right)
     {
         // Type analysis has already run, before replacing with the parallel
         // variant.
         JS_ASSERT(left->type() == MIRType_String && right->type() == MIRType_String);
 
         setMovable();
         setResultType(MIRType_String);
     }
 
   public:
     INSTRUCTION_HEADER(ConcatPar)
 
-    static MConcatPar *New(TempAllocator &alloc, MDefinition *slice, MConcat *concat) {
-        return new(alloc) MConcatPar(slice, concat->lhs(), concat->rhs());
-    }
-
-    MDefinition *forkJoinSlice() const {
+    static MConcatPar *New(TempAllocator &alloc, MDefinition *cx, MConcat *concat) {
+        return new(alloc) MConcatPar(cx, concat->lhs(), concat->rhs());
+    }
+
+    MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
     MDefinition *lhs() const {
         return getOperand(1);
     }
     MDefinition *rhs() const {
         return getOperand(2);
     }
@@ -4712,55 +4712,55 @@ class MCheckOverRecursed : public MNulla
         return new(alloc) MCheckOverRecursed();
     }
 };
 
 // Check the current frame for over-recursion past the global stack limit.
 // Uses the per-thread recursion limit.
 class MCheckOverRecursedPar : public MUnaryInstruction
 {
-    MCheckOverRecursedPar(MDefinition *slice)
-      : MUnaryInstruction(slice)
+    MCheckOverRecursedPar(MDefinition *cx)
+      : MUnaryInstruction(cx)
     {
         setResultType(MIRType_None);
         setGuard();
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(CheckOverRecursedPar);
 
-    static MCheckOverRecursedPar *New(TempAllocator &alloc, MDefinition *slice) {
-        return new(alloc) MCheckOverRecursedPar(slice);
-    }
-
-    MDefinition *forkJoinSlice() const {
+    static MCheckOverRecursedPar *New(TempAllocator &alloc, MDefinition *cx) {
+        return new(alloc) MCheckOverRecursedPar(cx);
+    }
+
+    MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 };
 
 // Check for an interrupt (or rendezvous) in parallel mode.
 class MCheckInterruptPar : public MUnaryInstruction
 {
-    MCheckInterruptPar(MDefinition *slice)
-      : MUnaryInstruction(slice)
+    MCheckInterruptPar(MDefinition *cx)
+      : MUnaryInstruction(cx)
     {
         setResultType(MIRType_None);
         setGuard();
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(CheckInterruptPar);
 
-    static MCheckInterruptPar *New(TempAllocator &alloc, MDefinition *slice) {
-        return new(alloc) MCheckInterruptPar(slice);
-    }
-
-    MDefinition *forkJoinSlice() const {
+    static MCheckInterruptPar *New(TempAllocator &alloc, MDefinition *cx) {
+        return new(alloc) MCheckInterruptPar(cx);
+    }
+
+    MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 };
 
 // Check whether we need to fire the interrupt handler.
 class MInterruptCheck : public MNullaryInstruction
 {
     MInterruptCheck() {
@@ -5088,35 +5088,35 @@ class MLambda
 };
 
 class MLambdaPar
   : public MBinaryInstruction,
     public SingleObjectPolicy
 {
     LambdaFunctionInfo info_;
 
-    MLambdaPar(MDefinition *slice, MDefinition *scopeChain, JSFunction *fun,
+    MLambdaPar(MDefinition *cx, MDefinition *scopeChain, JSFunction *fun,
                types::TemporaryTypeSet *resultTypes, const LambdaFunctionInfo &info)
-      : MBinaryInstruction(slice, scopeChain), info_(info)
+      : MBinaryInstruction(cx, scopeChain), info_(info)
     {
         JS_ASSERT(!info_.singletonType);
         JS_ASSERT(!info_.useNewTypeForClone);
         setResultType(MIRType_Object);
         setResultTypeSet(resultTypes);
     }
 
   public:
     INSTRUCTION_HEADER(LambdaPar);
 
-    static MLambdaPar *New(TempAllocator &alloc, MDefinition *slice, MLambda *lambda) {
-        return new(alloc) MLambdaPar(slice, lambda->scopeChain(), lambda->info().fun,
+    static MLambdaPar *New(TempAllocator &alloc, MDefinition *cx, MLambda *lambda) {
+        return new(alloc) MLambdaPar(cx, lambda->scopeChain(), lambda->info().fun,
                                      lambda->resultTypeSet(), lambda->info());
     }
 
-    MDefinition *forkJoinSlice() const {
+    MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 
     MDefinition *scopeChain() const {
         return getOperand(1);
     }
 
     const LambdaFunctionInfo &info() const {
@@ -7407,32 +7407,32 @@ class MFunctionEnvironment
     }
 
     // A function's environment is fixed.
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
-// Loads the current js::ForkJoinSlice*.
+// Loads the current js::ForkJoinContext*.
 // Only applicable in ParallelExecution.
-class MForkJoinSlice
+class MForkJoinContext
   : public MNullaryInstruction
 {
-    MForkJoinSlice()
+    MForkJoinContext()
         : MNullaryInstruction()
     {
-        setResultType(MIRType_ForkJoinSlice);
-    }
-
-  public:
-    INSTRUCTION_HEADER(ForkJoinSlice);
-
-    static MForkJoinSlice *New(TempAllocator &alloc) {
-        return new(alloc) MForkJoinSlice();
+        setResultType(MIRType_ForkJoinContext);
+    }
+
+  public:
+    INSTRUCTION_HEADER(ForkJoinContext);
+
+    static MForkJoinContext *New(TempAllocator &alloc) {
+        return new(alloc) MForkJoinContext();
     }
 
     AliasSet getAliasSet() const {
         // Indicate that this instruction reads nothing, stores nothing.
         // (For all intents and purposes)
         return AliasSet::None();
     }
 
@@ -8642,34 +8642,34 @@ class MRest
     }
 };
 
 class MRestPar
   : public MBinaryInstruction,
     public MRestCommon,
     public IntPolicy<1>
 {
-    MRestPar(MDefinition *slice, MDefinition *numActuals, unsigned numFormals,
+    MRestPar(MDefinition *cx, MDefinition *numActuals, unsigned numFormals,
              JSObject *templateObject, types::TemporaryTypeSet *resultTypes)
-      : MBinaryInstruction(slice, numActuals),
+      : MBinaryInstruction(cx, numActuals),
         MRestCommon(numFormals, templateObject)
     {
         setResultType(MIRType_Object);
         setResultTypeSet(resultTypes);
     }
 
   public:
     INSTRUCTION_HEADER(RestPar);
 
-    static MRestPar *New(TempAllocator &alloc, MDefinition *slice, MRest *rest) {
-        return new(alloc) MRestPar(slice, rest->numActuals(), rest->numFormals(),
+    static MRestPar *New(TempAllocator &alloc, MDefinition *cx, MRest *rest) {
+        return new(alloc) MRestPar(cx, rest->numActuals(), rest->numFormals(),
                                    rest->templateObject(), rest->resultTypeSet());
     }
 
-    MDefinition *forkJoinSlice() const {
+    MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
     MDefinition *numActuals() const {
         return getOperand(1);
     }
 
     TypePolicy *typePolicy() {
         return this;
@@ -8677,36 +8677,36 @@ class MRestPar
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
     bool possiblyCalls() const {
         return true;
     }
 };
 
-// Guard on an object being safe for writes by current parallel slice.
+// Guard on an object being safe for writes by current parallel cx.
 // Must be either thread-local or else a handle into the destination array.
 class MGuardThreadExclusive
   : public MBinaryInstruction,
     public ObjectPolicy<1>
 {
-    MGuardThreadExclusive(MDefinition *slice, MDefinition *obj)
-      : MBinaryInstruction(slice, obj)
+    MGuardThreadExclusive(MDefinition *cx, MDefinition *obj)
+      : MBinaryInstruction(cx, obj)
     {
         setResultType(MIRType_None);
         setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(GuardThreadExclusive);
 
-    static MGuardThreadExclusive *New(TempAllocator &alloc, MDefinition *slice, MDefinition *obj) {
-        return new(alloc) MGuardThreadExclusive(slice, obj);
-    }
-    MDefinition *forkJoinSlice() const {
+    static MGuardThreadExclusive *New(TempAllocator &alloc, MDefinition *cx, MDefinition *obj) {
+        return new(alloc) MGuardThreadExclusive(cx, obj);
+    }
+    MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
     MDefinition *object() const {
         return getOperand(1);
     }
     BailoutKind bailoutKind() const {
         return Bailout_Normal;
     }
@@ -8938,31 +8938,31 @@ class MNewCallObject : public MUnaryInst
         return AliasSet::None();
     }
 };
 
 class MNewCallObjectPar : public MBinaryInstruction
 {
     CompilerRootObject templateObj_;
 
-    MNewCallObjectPar(MDefinition *slice, JSObject *templateObj, MDefinition *slots)
-        : MBinaryInstruction(slice, slots),
+    MNewCallObjectPar(MDefinition *cx, JSObject *templateObj, MDefinition *slots)
+        : MBinaryInstruction(cx, slots),
           templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewCallObjectPar);
 
-    static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *slice, MNewCallObject *callObj) {
-        return new(alloc) MNewCallObjectPar(slice, callObj->templateObject(), callObj->slots());
-    }
-
-    MDefinition *forkJoinSlice() const {
+    static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *cx, MNewCallObject *callObj) {
+        return new(alloc) MNewCallObjectPar(cx, callObj->templateObject(), callObj->slots());
+    }
+
+    MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 
     MDefinition *slots() const {
         return getOperand(1);
     }
 
     JSObject *templateObj() const {
@@ -9077,33 +9077,33 @@ class MEnclosingScope : public MLoadFixe
 
 // Creates a dense array of the given length.
 //
 // Note: the template object should be an *empty* dense array!
 class MNewDenseArrayPar : public MBinaryInstruction
 {
     CompilerRootObject templateObject_;
 
-    MNewDenseArrayPar(MDefinition *slice, MDefinition *length, JSObject *templateObject)
-      : MBinaryInstruction(slice, length),
+    MNewDenseArrayPar(MDefinition *cx, MDefinition *length, JSObject *templateObject)
+      : MBinaryInstruction(cx, length),
         templateObject_(templateObject)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewDenseArrayPar);
 
-    static MNewDenseArrayPar *New(TempAllocator &alloc, MDefinition *slice, MDefinition *length,
+    static MNewDenseArrayPar *New(TempAllocator &alloc, MDefinition *cx, MDefinition *length,
                                   JSObject *templateObject)
     {
-        return new(alloc) MNewDenseArrayPar(slice, length, templateObject);
-    }
-
-    MDefinition *forkJoinSlice() const {
+        return new(alloc) MNewDenseArrayPar(cx, length, templateObject);
+    }
+
+    MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 
     MDefinition *length() const {
         return getOperand(1);
     }
 
     JSObject *templateObject() const {
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -125,44 +125,44 @@ MIRGraph::removeBlock(MBasicBlock *block
 void
 MIRGraph::unmarkBlocks()
 {
     for (MBasicBlockIterator i(blocks_.begin()); i != blocks_.end(); i++)
         i->unmark();
 }
 
 MDefinition *
-MIRGraph::forkJoinSlice()
+MIRGraph::forkJoinContext()
 {
-    // Search the entry block to find a par slice instruction.  If we do not
-    // find one, add one after the Start instruction.
+    // Search the entry block to find a ForkJoinContext instruction. If we do
+    // not find one, add one after the Start instruction.
     //
     // Note: the original design used a field in MIRGraph to cache the
-    // forkJoinSlice rather than searching for it again.  However, this could
-    // become out of date due to DCE.  Given that we do not generally have to
-    // search very far to find the par slice instruction if it exists, and
-    // that we don't look for it that often, I opted to simply eliminate the
-    // cache and search anew each time, so that it is that much easier to keep
-    // the IR coherent. - nmatsakis
+    // forkJoinContext rather than searching for it again.  However, this
+    // could become out of date due to DCE.  Given that we do not generally
+    // have to search very far to find the ForkJoinContext instruction if it
+    // exists, and that we don't look for it that often, I opted to simply
+    // eliminate the cache and search anew each time, so that it is that much
+    // easier to keep the IR coherent. - nmatsakis
 
     MBasicBlock *entry = entryBlock();
     JS_ASSERT(entry->info().executionMode() == ParallelExecution);
 
     MInstruction *start = nullptr;
     for (MInstructionIterator ins(entry->begin()); ins != entry->end(); ins++) {
-        if (ins->isForkJoinSlice())
+        if (ins->isForkJoinContext())
             return *ins;
         else if (ins->isStart())
             start = *ins;
     }
     JS_ASSERT(start);
 
-    MForkJoinSlice *slice = MForkJoinSlice::New(alloc());
-    entry->insertAfter(start, slice);
-    return slice;
+    MForkJoinContext *cx = MForkJoinContext::New(alloc());
+    entry->insertAfter(start, cx);
+    return cx;
 }
 
 MBasicBlock *
 MBasicBlock::New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info,
                  MBasicBlock *pred, jsbytecode *entryPc, Kind kind)
 {
     JS_ASSERT(entryPc != nullptr);
 
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -664,20 +664,20 @@ class MIRGraph
     bool hasTryBlock() const {
         return hasTryBlock_;
     }
     void setHasTryBlock() {
         hasTryBlock_ = true;
     }
 
     // The per-thread context. So as not to modify the calling convention for
-    // parallel code, we obtain the current slice from thread-local storage.
-    // This helper method will lazilly insert an MForkJoinSlice instruction in
-    // the entry block and return the definition.
-    MDefinition *forkJoinSlice();
+    // parallel code, we obtain the current ForkJoinContext from thread-local
+    // storage.  This helper method will lazilly insert an MForkJoinContext
+    // instruction in the entry block and return the definition.
+    MDefinition *forkJoinContext();
 
     void dump(FILE *fp);
     void dump();
 };
 
 class MDefinitionIterator
 {
 
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -206,17 +206,17 @@ namespace jit {
     _(CheckOverRecursedPar)                                                 \
     _(NewCallObjectPar)                                                     \
     _(NewPar)                                                               \
     _(NewDenseArrayPar)                                                     \
     _(NewDerivedTypedObject)                                                \
     _(AbortPar)                                                             \
     _(LambdaPar)                                                            \
     _(RestPar)                                                              \
-    _(ForkJoinSlice)                                                        \
+    _(ForkJoinContext)                                                      \
     _(GuardThreadExclusive)                                                 \
     _(CheckInterruptPar)                                                    \
     _(RecompileCheck)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
  MIR_OPCODE_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
--- a/js/src/jit/ParallelFunctions.cpp
+++ b/js/src/jit/ParallelFunctions.cpp
@@ -17,35 +17,34 @@ using namespace js;
 using namespace jit;
 
 using parallel::Spew;
 using parallel::SpewOps;
 using parallel::SpewBailouts;
 using parallel::SpewBailoutIR;
 
 // Load the current thread context.
-ForkJoinSlice *
-jit::ForkJoinSlicePar()
+ForkJoinContext *
+jit::ForkJoinContextPar()
 {
-    return ForkJoinSlice::current();
+    return ForkJoinContext::current();
 }
 
 // NewGCThingPar() is called in place of NewGCThing() when executing
 // parallel code.  It uses the ArenaLists for the current thread and
 // allocates from there.
 JSObject *
-jit::NewGCThingPar(ForkJoinSlice *slice, gc::AllocKind allocKind)
+jit::NewGCThingPar(ForkJoinContext *cx, gc::AllocKind allocKind)
 {
-    JS_ASSERT(ForkJoinSlice::current() == slice);
-    uint32_t thingSize = (uint32_t)gc::Arena::thingSize(allocKind);
-    return gc::NewGCThing<JSObject, NoGC>(slice, allocKind, thingSize, gc::DefaultHeap);
+    JS_ASSERT(ForkJoinContext::current() == cx);
+    return js::NewGCObject<NoGC>(cx, allocKind, 0, gc::TenuredHeap);
 }
 
 bool
-jit::ParallelWriteGuard(ForkJoinSlice *slice, JSObject *object)
+jit::ParallelWriteGuard(ForkJoinContext *cx, JSObject *object)
 {
     // Implements the most general form of the write guard, which is
     // suitable for writes to any object O. There are two cases to
     // consider and test for:
     //
     // 1. Writes to thread-local memory are safe. Thread-local memory
     //    is defined as memory allocated by the current thread.
     //    The definition of the PJS API guarantees that such memory
@@ -76,52 +75,52 @@ jit::ParallelWriteGuard(ForkJoinSlice *s
     //    harm done in letting them be read (but not written).
     //
     //    In order to be able to distinguish escaped out pointers from
     //    prior iterations and the proper out pointers from the
     //    current iteration, we always track a *target memory region*
     //    (which is a span of bytes within the output buffer) and not
     //    just the output buffer itself.
 
-    JS_ASSERT(ForkJoinSlice::current() == slice);
+    JS_ASSERT(ForkJoinContext::current() == cx);
 
     if (IsTypedDatum(*object)) {
         TypedDatum &datum = AsTypedDatum(*object);
 
         // Note: check target region based on `datum`, not the owner.
         // This is because `datum` may point to some subregion of the
         // owner and we only care if that *subregion* is within the
         // target region, not the entire owner.
-        if (IsInTargetRegion(slice, &datum))
+        if (IsInTargetRegion(cx, &datum))
             return true;
 
         // Also check whether owner is thread-local.
         TypedDatum *owner = datum.owner();
-        return owner && slice->isThreadLocal(owner);
+        return owner && cx->isThreadLocal(owner);
     }
 
     // For other kinds of writable objects, must be thread-local.
-    return slice->isThreadLocal(object);
+    return cx->isThreadLocal(object);
 }
 
 // Check that |object| (which must be a typed datum) maps
 // to memory in the target region.
 //
 // For efficiency, we assume that all handles which the user has
 // access to are either entirely within the target region or entirely
 // without, but not straddling the target region nor encompassing
 // it. This invariant is maintained by the PJS APIs, where the target
 // region and handles are always elements of the same output array.
 bool
-jit::IsInTargetRegion(ForkJoinSlice *slice, TypedDatum *datum)
+jit::IsInTargetRegion(ForkJoinContext *cx, TypedDatum *datum)
 {
     JS_ASSERT(IsTypedDatum(*datum)); // in case JIT supplies something bogus
     uint8_t *typedMem = datum->typedMem();
-    return (typedMem >= slice->targetRegionStart &&
-            typedMem <  slice->targetRegionEnd);
+    return (typedMem >= cx->targetRegionStart &&
+            typedMem <  cx->targetRegionEnd);
 }
 
 #ifdef DEBUG
 static void
 printTrace(const char *prefix, struct IonLIRTraceData *cached)
 {
     fprintf(stderr, "%s / Block %3u / LIR %3u / Mode %u / LIR %s\n",
             prefix,
@@ -155,167 +154,167 @@ jit::TraceLIR(IonLIRTraceData *current)
         else
             traceMode = Bailouts;
     }
 
     IonLIRTraceData *cached;
     if (current->execModeInt == 0)
         cached = &seqTraceData;
     else
-        cached = &ForkJoinSlice::current()->traceData;
+        cached = &ForkJoinContext::current()->traceData;
 
     if (current->blockIndex == 0xDEADBEEF) {
         if (current->execModeInt == 0)
             printTrace("BAILOUT", cached);
         else
             SpewBailoutIR(cached);
     }
 
     memcpy(cached, current, sizeof(IonLIRTraceData));
 
     if (traceMode == All)
         printTrace("Exec", cached);
 #endif
 }
 
 bool
-jit::CheckOverRecursedPar(ForkJoinSlice *slice)
+jit::CheckOverRecursedPar(ForkJoinContext *cx)
 {
-    JS_ASSERT(ForkJoinSlice::current() == slice);
+    JS_ASSERT(ForkJoinContext::current() == cx);
     int stackDummy_;
 
     // When an interrupt is triggered, the main thread stack limit is
     // overwritten with a sentinel value that brings us here.
     // Therefore, we must check whether this is really a stack overrun
     // and, if not, check whether an interrupt is needed.
     //
     // When not on the main thread, we don't overwrite the stack
     // limit, but we do still call into this routine if the interrupt
     // flag is set, so we still need to double check.
 
 #ifdef JS_ARM_SIMULATOR
     if (Simulator::Current()->overRecursed()) {
-        slice->bailoutRecord->setCause(ParallelBailoutOverRecursed);
+        cx->bailoutRecord->setCause(ParallelBailoutOverRecursed);
         return false;
     }
 #endif
 
     uintptr_t realStackLimit;
-    if (slice->isMainThread())
-        realStackLimit = GetNativeStackLimit(slice);
+    if (cx->isMainThread())
+        realStackLimit = GetNativeStackLimit(cx);
     else
-        realStackLimit = slice->perThreadData->ionStackLimit;
+        realStackLimit = cx->perThreadData->ionStackLimit;
 
     if (!JS_CHECK_STACK_SIZE(realStackLimit, &stackDummy_)) {
-        slice->bailoutRecord->setCause(ParallelBailoutOverRecursed);
+        cx->bailoutRecord->setCause(ParallelBailoutOverRecursed);
         return false;
     }
 
-    return CheckInterruptPar(slice);
+    return CheckInterruptPar(cx);
 }
 
 bool
-jit::CheckInterruptPar(ForkJoinSlice *slice)
+jit::CheckInterruptPar(ForkJoinContext *cx)
 {
-    JS_ASSERT(ForkJoinSlice::current() == slice);
-    bool result = slice->check();
+    JS_ASSERT(ForkJoinContext::current() == cx);
+    bool result = cx->check();
     if (!result) {
         // Do not set the cause here.  Either it was set by this
         // thread already by some code that then triggered an abort,
         // or else we are just picking up an abort from some other
         // thread.  Either way we have nothing useful to contribute so
         // we might as well leave our bailout case unset.
         return false;
     }
     return true;
 }
 
 JSObject *
-jit::ExtendArrayPar(ForkJoinSlice *slice, JSObject *array, uint32_t length)
+jit::ExtendArrayPar(ForkJoinContext *cx, JSObject *array, uint32_t length)
 {
     JSObject::EnsureDenseResult res =
-        array->ensureDenseElementsPreservePackedFlag(slice, 0, length);
+        array->ensureDenseElementsPreservePackedFlag(cx, 0, length);
     if (res != JSObject::ED_OK)
         return nullptr;
     return array;
 }
 
 bool
-jit::SetPropertyPar(ForkJoinSlice *slice, HandleObject obj, HandlePropertyName name,
+jit::SetPropertyPar(ForkJoinContext *cx, HandleObject obj, HandlePropertyName name,
                     HandleValue value, bool strict, jsbytecode *pc)
 {
-    JS_ASSERT(slice->isThreadLocal(obj));
+    JS_ASSERT(cx->isThreadLocal(obj));
 
     if (*pc == JSOP_SETALIASEDVAR) {
         // See comment in jit::SetProperty.
         Shape *shape = obj->nativeLookupPure(name);
         JS_ASSERT(shape && shape->hasSlot());
         return obj->nativeSetSlotIfHasType(shape, value);
     }
 
     // Fail early on hooks.
     if (obj->getOps()->setProperty)
         return TP_RETRY_SEQUENTIALLY;
 
-    RootedValue v(slice, value);
-    RootedId id(slice, NameToId(name));
-    return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, strict);
+    RootedValue v(cx, value);
+    RootedId id(cx, NameToId(name));
+    return baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, 0, &v, strict);
 }
 
 bool
-jit::SetElementPar(ForkJoinSlice *slice, HandleObject obj, HandleValue index, HandleValue value,
+jit::SetElementPar(ForkJoinContext *cx, HandleObject obj, HandleValue index, HandleValue value,
                    bool strict)
 {
-    RootedId id(slice);
+    RootedId id(cx);
     if (!ValueToIdPure(index, id.address()))
         return false;
 
     // SetObjectElementOperation, the sequential version, has several checks
     // for certain deoptimizing behaviors, such as marking having written to
     // holes and non-indexed element accesses. We don't do that here, as we
     // can't modify any TI state anyways. If we need to add a new type, we
     // would bail out.
-    RootedValue v(slice, value);
-    return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, strict);
+    RootedValue v(cx, value);
+    return baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, 0, &v, strict);
 }
 
 JSString *
-jit::ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right)
+jit::ConcatStringsPar(ForkJoinContext *cx, HandleString left, HandleString right)
 {
-    return ConcatStrings<NoGC>(slice, left, right);
+    return ConcatStrings<NoGC>(cx, left, right);
 }
 
 JSFlatString *
-jit::IntToStringPar(ForkJoinSlice *slice, int i)
+jit::IntToStringPar(ForkJoinContext *cx, int i)
 {
-    return Int32ToString<NoGC>(slice, i);
+    return Int32ToString<NoGC>(cx, i);
 }
 
 JSString *
-jit::DoubleToStringPar(ForkJoinSlice *slice, double d)
+jit::DoubleToStringPar(ForkJoinContext *cx, double d)
 {
-    return NumberToString<NoGC>(slice, d);
+    return NumberToString<NoGC>(cx, d);
 }
 
 JSString *
-jit::PrimitiveToStringPar(ForkJoinSlice *slice, HandleValue input)
+jit::PrimitiveToStringPar(ForkJoinContext *cx, HandleValue input)
 {
     // All other cases are handled in assembly.
     JS_ASSERT(input.isDouble() || input.isInt32());
 
     if (input.isInt32())
-        return Int32ToString<NoGC>(slice, input.toInt32());
+        return Int32ToString<NoGC>(cx, input.toInt32());
 
-    return NumberToString<NoGC>(slice, input.toDouble());
+    return NumberToString<NoGC>(cx, input.toDouble());
 }
 
 bool
-jit::StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out)
+jit::StringToNumberPar(ForkJoinContext *cx, JSString *str, double *out)
 {
-    return StringToNumber(slice, str, out);
+    return StringToNumber(cx, str, out);
 }
 
 #define PAR_RELATIONAL_OP(OP, EXPECTED)                                         \
 do {                                                                            \
     /* Optimize for two int-tagged operands (typical loop control). */          \
     if (lhs.isInt32() && rhs.isInt32()) {                                       \
         *res = (lhs.toInt32() OP rhs.toInt32()) == EXPECTED;                    \
     } else if (lhs.isNumber() && rhs.isNumber()) {                              \
@@ -330,68 +329,68 @@ do {                                    
         double r = rhs.toNumber();                                              \
         *res = (l OP r) == EXPECTED;                                            \
     } else if (lhs.isNumber() && rhs.isBoolean()) {                             \
         double l = lhs.toNumber();                                              \
         bool r = rhs.toBoolean();                                               \
         *res = (l OP r) == EXPECTED;                                            \
     } else {                                                                    \
         int32_t vsZero;                                                         \
-        if (!CompareMaybeStringsPar(slice, lhs, rhs, &vsZero))                  \
+        if (!CompareMaybeStringsPar(cx, lhs, rhs, &vsZero))                  \
             return false;                                                       \
         *res = (vsZero OP 0) == EXPECTED;                                       \
     }                                                                           \
     return true;                                                                \
 } while(0)
 
 static bool
-CompareStringsPar(ForkJoinSlice *slice, JSString *left, JSString *right, int32_t *res)
+CompareStringsPar(ForkJoinContext *cx, JSString *left, JSString *right, int32_t *res)
 {
     ScopedThreadSafeStringInspector leftInspector(left);
     ScopedThreadSafeStringInspector rightInspector(right);
-    if (!leftInspector.ensureChars(slice) || !rightInspector.ensureChars(slice))
+    if (!leftInspector.ensureChars(cx) || !rightInspector.ensureChars(cx))
         return false;
 
     *res = CompareChars(leftInspector.chars(), left->length(),
                         rightInspector.chars(), right->length());
     return true;
 }
 
 static bool
-CompareMaybeStringsPar(ForkJoinSlice *slice, HandleValue v1, HandleValue v2, int32_t *res)
+CompareMaybeStringsPar(ForkJoinContext *cx, HandleValue v1, HandleValue v2, int32_t *res)
 {
     if (!v1.isString())
         return false;
     if (!v2.isString())
         return false;
-    return CompareStringsPar(slice, v1.toString(), v2.toString(), res);
+    return CompareStringsPar(cx, v1.toString(), v2.toString(), res);
 }
 
 template<bool Equal>
 bool
-LooselyEqualImplPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
+LooselyEqualImplPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     PAR_RELATIONAL_OP(==, Equal);
 }
 
 bool
-js::jit::LooselyEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
+js::jit::LooselyEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
-    return LooselyEqualImplPar<true>(slice, lhs, rhs, res);
+    return LooselyEqualImplPar<true>(cx, lhs, rhs, res);
 }
 
 bool
-js::jit::LooselyUnequalPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
+js::jit::LooselyUnequalPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
-    return LooselyEqualImplPar<false>(slice, lhs, rhs, res);
+    return LooselyEqualImplPar<false>(cx, lhs, rhs, res);
 }
 
 template<bool Equal>
 bool
-StrictlyEqualImplPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
+StrictlyEqualImplPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     if (lhs.isNumber()) {
         if (rhs.isNumber()) {
             *res = (lhs.toNumber() == rhs.toNumber()) == Equal;
             return true;
         }
     } else if (lhs.isBoolean()) {
         if (rhs.isBoolean()) {
@@ -410,150 +409,150 @@ StrictlyEqualImplPar(ForkJoinSlice *slic
         }
     } else if (lhs.isObject()) {
         if (rhs.isObject()) {
             *res = (lhs.toObjectOrNull() == rhs.toObjectOrNull()) == Equal;
             return true;
         }
     } else if (lhs.isString()) {
         if (rhs.isString())
-            return LooselyEqualImplPar<Equal>(slice, lhs, rhs, res);
+            return LooselyEqualImplPar<Equal>(cx, lhs, rhs, res);
     }
 
     *res = false;
     return true;
 }
 
 bool
-js::jit::StrictlyEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
+js::jit::StrictlyEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
-    return StrictlyEqualImplPar<true>(slice, lhs, rhs, res);
+    return StrictlyEqualImplPar<true>(cx, lhs, rhs, res);
 }
 
 bool
-js::jit::StrictlyUnequalPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
+js::jit::StrictlyUnequalPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
-    return StrictlyEqualImplPar<false>(slice, lhs, rhs, res);
+    return StrictlyEqualImplPar<false>(cx, lhs, rhs, res);
 }
 
 bool
-js::jit::LessThanPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
+js::jit::LessThanPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     PAR_RELATIONAL_OP(<, true);
 }
 
 bool
-js::jit::LessThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
+js::jit::LessThanOrEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     PAR_RELATIONAL_OP(<=, true);
 }
 
 bool
-js::jit::GreaterThanPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
+js::jit::GreaterThanPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     PAR_RELATIONAL_OP(>, true);
 }
 
 bool
-js::jit::GreaterThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
+js::jit::GreaterThanOrEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     PAR_RELATIONAL_OP(>=, true);
 }
 
 template<bool Equal>
 bool
-StringsEqualImplPar(ForkJoinSlice *slice, HandleString lhs, HandleString rhs, bool *res)
+StringsEqualImplPar(ForkJoinContext *cx, HandleString lhs, HandleString rhs, bool *res)
 {
     int32_t vsZero;
-    bool ret = CompareStringsPar(slice, lhs, rhs, &vsZero);
+    bool ret = CompareStringsPar(cx, lhs, rhs, &vsZero);
     if (ret != true)
         return ret;
     *res = (vsZero == 0) == Equal;
     return true;
 }
 
 bool
-js::jit::StringsEqualPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *res)
+js::jit::StringsEqualPar(ForkJoinContext *cx, HandleString v1, HandleString v2, bool *res)
 {
-    return StringsEqualImplPar<true>(slice, v1, v2, res);
+    return StringsEqualImplPar<true>(cx, v1, v2, res);
 }
 
 bool
-js::jit::StringsUnequalPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *res)
+js::jit::StringsUnequalPar(ForkJoinContext *cx, HandleString v1, HandleString v2, bool *res)
 {
-    return StringsEqualImplPar<false>(slice, v1, v2, res);
+    return StringsEqualImplPar<false>(cx, v1, v2, res);
 }
 
 bool
-jit::BitNotPar(ForkJoinSlice *slice, HandleValue in, int32_t *out)
+jit::BitNotPar(ForkJoinContext *cx, HandleValue in, int32_t *out)
 {
     if (in.isObject())
         return false;
     int i;
-    if (!NonObjectToInt32(slice, in, &i))
+    if (!NonObjectToInt32(cx, in, &i))
         return false;
     *out = ~i;
     return true;
 }
 
 #define BIT_OP(OP)                                                      \
     JS_BEGIN_MACRO                                                      \
     int32_t left, right;                                                \
     if (lhs.isObject() || rhs.isObject())                               \
         return TP_RETRY_SEQUENTIALLY;                                   \
-    if (!NonObjectToInt32(slice, lhs, &left) ||                         \
-        !NonObjectToInt32(slice, rhs, &right))                          \
+    if (!NonObjectToInt32(cx, lhs, &left) ||                         \
+        !NonObjectToInt32(cx, rhs, &right))                          \
     {                                                                   \
         return false;                                                   \
     }                                                                   \
     *out = (OP);                                                        \
     return true;                                                        \
     JS_END_MACRO
 
 bool
-jit::BitXorPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out)
+jit::BitXorPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out)
 {
     BIT_OP(left ^ right);
 }
 
 bool
-jit::BitOrPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out)
+jit::BitOrPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out)
 {
     BIT_OP(left | right);
 }
 
 bool
-jit::BitAndPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out)
+jit::BitAndPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out)
 {
     BIT_OP(left & right);
 }
 
 bool
-jit::BitLshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out)
+jit::BitLshPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out)
 {
     BIT_OP(left << (right & 31));
 }
 
 bool
-jit::BitRshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out)
+jit::BitRshPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out)
 {
     BIT_OP(left >> (right & 31));
 }
 
 #undef BIT_OP
 
 bool
-jit::UrshValuesPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs,
+jit::UrshValuesPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs,
                    Value *out)
 {
     uint32_t left;
     int32_t right;
     if (lhs.isObject() || rhs.isObject())
         return false;
-    if (!NonObjectToUint32(slice, lhs, &left) || !NonObjectToInt32(slice, rhs, &right))
+    if (!NonObjectToUint32(cx, lhs, &left) || !NonObjectToInt32(cx, rhs, &right))
         return false;
     left >>= right & 31;
     out->setNumber(uint32_t(left));
     return true;
 }
 
 void
 jit::AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
@@ -568,38 +567,38 @@ jit::AbortPar(ParallelBailoutCause cause
          currentScript, currentScript->filename(), currentScript->lineno(),
          (currentScript ? PCToLineNumber(currentScript, bytecode) : 0));
 
     JS_ASSERT(InParallelSection());
     JS_ASSERT(outermostScript != nullptr);
     JS_ASSERT(currentScript != nullptr);
     JS_ASSERT(outermostScript->hasParallelIonScript());
 
-    ForkJoinSlice *slice = ForkJoinSlice::current();
+    ForkJoinContext *cx = ForkJoinContext::current();
 
-    JS_ASSERT(slice->bailoutRecord->depth == 0);
-    slice->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode);
+    JS_ASSERT(cx->bailoutRecord->depth == 0);
+    cx->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode);
 }
 
 void
 jit::PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript)
 {
     Spew(SpewBailouts,
          "Propagate parallel abort via %p:%s:%d (%p:%s:%d)",
          outermostScript, outermostScript->filename(), outermostScript->lineno(),
          currentScript, currentScript->filename(), currentScript->lineno());
 
     JS_ASSERT(InParallelSection());
     JS_ASSERT(outermostScript->hasParallelIonScript());
 
     outermostScript->parallelIonScript()->setHasUncompiledCallTarget();
 
-    ForkJoinSlice *slice = ForkJoinSlice::current();
+    ForkJoinContext *cx = ForkJoinContext::current();
     if (currentScript)
-        slice->bailoutRecord->addTrace(currentScript, nullptr);
+        cx->bailoutRecord->addTrace(currentScript, nullptr);
 }
 
 void
 jit::CallToUncompiledScriptPar(JSObject *obj)
 {
     JS_ASSERT(InParallelSection());
 
 #ifdef DEBUG
@@ -637,30 +636,30 @@ jit::CallToUncompiledScriptPar(JSObject 
     } else {
         JS_ASSERT(func->isNative());
         Spew(SpewBailouts, "Call to native function");
     }
 #endif
 }
 
 JSObject *
-jit::InitRestParameterPar(ForkJoinSlice *slice, uint32_t length, Value *rest,
+jit::InitRestParameterPar(ForkJoinContext *cx, uint32_t length, Value *rest,
                           HandleObject templateObj, HandleObject res)
 {
     // In parallel execution, we should always have succeeded in allocation
     // before this point. We can do the allocation here like in the sequential
     // path, but duplicating the initGCThing logic is too tedious.
     JS_ASSERT(res);
     JS_ASSERT(res->is<ArrayObject>());
     JS_ASSERT(!res->getDenseInitializedLength());
     JS_ASSERT(res->type() == templateObj->type());
 
     if (length > 0) {
         JSObject::EnsureDenseResult edr =
-            res->ensureDenseElementsPreservePackedFlag(slice, 0, length);
+            res->ensureDenseElementsPreservePackedFlag(cx, 0, length);
         if (edr != JSObject::ED_OK)
             return nullptr;
         res->initDenseElementsUnbarriered(0, rest, length);
         res->as<ArrayObject>().setLengthInt32(length);
     }
 
     return res;
 }
--- a/js/src/jit/ParallelFunctions.h
+++ b/js/src/jit/ParallelFunctions.h
@@ -11,68 +11,67 @@
 #include "vm/ForkJoin.h"
 
 namespace js {
 
 class TypedDatum; // subclass of JSObject* defined in builtin/TypedObject.h
 
 namespace jit {
 
-ForkJoinSlice *ForkJoinSlicePar();
-JSObject *NewGCThingPar(ForkJoinSlice *slice, gc::AllocKind allocKind);
-bool ParallelWriteGuard(ForkJoinSlice *slice, JSObject *object);
-bool IsInTargetRegion(ForkJoinSlice *slice, TypedDatum *object);
-bool CheckOverRecursedPar(ForkJoinSlice *slice);
-bool CheckInterruptPar(ForkJoinSlice *slice);
+ForkJoinContext *ForkJoinContextPar();
+JSObject *NewGCThingPar(ForkJoinContext *cx, gc::AllocKind allocKind);
+bool ParallelWriteGuard(ForkJoinContext *cx, JSObject *object);
+bool IsInTargetRegion(ForkJoinContext *cx, TypedDatum *object);
+bool CheckOverRecursedPar(ForkJoinContext *cx);
+bool CheckInterruptPar(ForkJoinContext *cx);
 
 // Extends the given array with `length` new holes.  Returns nullptr on
 // failure or else `array`, which is convenient during code
 // generation.
-JSObject *ExtendArrayPar(ForkJoinSlice *slice, JSObject *array, uint32_t length);
+JSObject *ExtendArrayPar(ForkJoinContext *cx, JSObject *array, uint32_t length);
 
 // Set properties and elements on thread local objects.
-bool SetPropertyPar(ForkJoinSlice *slice, HandleObject obj, HandlePropertyName name,
+bool SetPropertyPar(ForkJoinContext *cx, HandleObject obj, HandlePropertyName name,
                     HandleValue value, bool strict, jsbytecode *pc);
-bool SetElementPar(ForkJoinSlice *slice, HandleObject obj, HandleValue index,
+bool SetElementPar(ForkJoinContext *cx, HandleObject obj, HandleValue index,
                    HandleValue value, bool strict);
 
 // String related parallel functions. These tend to call existing VM functions
 // that take a ThreadSafeContext.
-JSString *ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right);
-JSFlatString *IntToStringPar(ForkJoinSlice *slice, int i);
-JSString *DoubleToStringPar(ForkJoinSlice *slice, double d);
-JSString *PrimitiveToStringPar(ForkJoinSlice *slice, HandleValue input);
-bool StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out);
+JSString *ConcatStringsPar(ForkJoinContext *cx, HandleString left, HandleString right);
+JSFlatString *IntToStringPar(ForkJoinContext *cx, int i);
+JSString *DoubleToStringPar(ForkJoinContext *cx, double d);
+JSString *PrimitiveToStringPar(ForkJoinContext *cx, HandleValue input);
+bool StringToNumberPar(ForkJoinContext *cx, JSString *str, double *out);
 
 // Binary and unary operator functions on values. These tend to return
 // RETRY_SEQUENTIALLY if the values are objects.
-bool StrictlyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-bool StrictlyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-bool LooselyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-bool LooselyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-bool LessThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-bool LessThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-bool GreaterThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-bool GreaterThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool StrictlyEqualPar(ForkJoinContext *cx, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool StrictlyUnequalPar(ForkJoinContext *cx, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool LooselyEqualPar(ForkJoinContext *cx, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool LooselyUnequalPar(ForkJoinContext *cx, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool LessThanPar(ForkJoinContext *cx, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool LessThanOrEqualPar(ForkJoinContext *cx, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool GreaterThanPar(ForkJoinContext *cx, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool GreaterThanOrEqualPar(ForkJoinContext *cx, MutableHandleValue v1, MutableHandleValue v2, bool *);
 
-bool StringsEqualPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *);
-bool StringsUnequalPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *);
+bool StringsEqualPar(ForkJoinContext *cx, HandleString v1, HandleString v2, bool *);
+bool StringsUnequalPar(ForkJoinContext *cx, HandleString v1, HandleString v2, bool *);
 
-bool BitNotPar(ForkJoinSlice *slice, HandleValue in, int32_t *out);
-bool BitXorPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
-bool BitOrPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
-bool BitAndPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
-bool BitLshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
-bool BitRshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
+bool BitNotPar(ForkJoinContext *cx, HandleValue in, int32_t *out);
+bool BitXorPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out);
+bool BitOrPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out);
+bool BitAndPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out);
+bool BitLshPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out);
+bool BitRshPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out);
 
-bool UrshValuesPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs,
-                             Value *out);
+bool UrshValuesPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, Value *out);
 
 // Make a new rest parameter in parallel.
-JSObject *InitRestParameterPar(ForkJoinSlice *slice, uint32_t length, Value *rest,
+JSObject *InitRestParameterPar(ForkJoinContext *cx, uint32_t length, Value *rest,
                                HandleObject templateObj, HandleObject res);
 
 // Abort and debug tracing functions.
 void AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
               jsbytecode *bytecode);
 void PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript);
 
 void TraceLIR(IonLIRTraceData *current);
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -64,17 +64,17 @@ using parallel::SpewCompile;
             return true;                                                      \
         return insertWriteGuard(prop, prop->obj());                           \
     }
 
 class ParallelSafetyVisitor : public MInstructionVisitor
 {
     MIRGraph &graph_;
     bool unsafe_;
-    MDefinition *slice_;
+    MDefinition *cx_;
 
     bool insertWriteGuard(MInstruction *writeInstruction, MDefinition *valueBeingWritten);
 
     bool replaceWithNewPar(MInstruction *newInstruction, JSObject *templateObject);
     bool replace(MInstruction *oldInstruction, MInstruction *replacementInstruction);
 
     bool visitSpecializedInstruction(MInstruction *ins, MIRType spec, uint32_t flags);
 
@@ -90,25 +90,25 @@ class ParallelSafetyVisitor : public MIn
     TempAllocator &alloc() const {
         return graph_.alloc();
     }
 
   public:
     ParallelSafetyVisitor(MIRGraph &graph)
       : graph_(graph),
         unsafe_(false),
-        slice_(nullptr)
+        cx_(nullptr)
     { }
 
     void clearUnsafe() { unsafe_ = false; }
     bool unsafe() { return unsafe_; }
-    MDefinition *forkJoinSlice() {
-        if (!slice_)
-            slice_ = graph_.forkJoinSlice();
-        return slice_;
+    MDefinition *ForkJoinContext() {
+        if (!cx_)
+            cx_ = graph_.forkJoinContext();
+        return cx_;
     }
 
     bool convertToBailout(MBasicBlock *block, MInstruction *ins);
 
     // I am taking the policy of blacklisting everything that's not
     // obviously safe for now.  We can loosen as we need.
 
     SAFE_OP(Constant)
@@ -261,17 +261,17 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(SetFrameArgument)
     UNSAFE_OP(RunOncePrologue)
     CUSTOM_OP(Rest)
     SAFE_OP(RestPar)
     SAFE_OP(Floor)
     SAFE_OP(Round)
     UNSAFE_OP(InstanceOf)
     CUSTOM_OP(InterruptCheck)
-    SAFE_OP(ForkJoinSlice)
+    SAFE_OP(ForkJoinContext)
     SAFE_OP(NewPar)
     SAFE_OP(NewDenseArrayPar)
     SAFE_OP(NewCallObjectPar)
     SAFE_OP(LambdaPar)
     SAFE_OP(AbortPar)
     UNSAFE_OP(ArrayConcat)
     UNSAFE_OP(GetDOMProperty)
     UNSAFE_OP(GetDOMMember)
@@ -518,30 +518,30 @@ bool
 ParallelSafetyVisitor::visitCreateThisWithTemplate(MCreateThisWithTemplate *ins)
 {
     return replaceWithNewPar(ins, ins->templateObject());
 }
 
 bool
 ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins)
 {
-    replace(ins, MNewCallObjectPar::New(alloc(), forkJoinSlice(), ins));
+    replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
     return true;
 }
 
 bool
 ParallelSafetyVisitor::visitLambda(MLambda *ins)
 {
     if (ins->info().singletonType || ins->info().useNewTypeForClone) {
         // slow path: bail on parallel execution.
         return markUnsafe();
     }
 
     // fast path: replace with LambdaPar op
-    replace(ins, MLambdaPar::New(alloc(), forkJoinSlice(), ins));
+    replace(ins, MLambdaPar::New(alloc(), ForkJoinContext(), ins));
     return true;
 }
 
 bool
 ParallelSafetyVisitor::visitNewObject(MNewObject *newInstruction)
 {
     if (newInstruction->shouldUseVM()) {
         SpewMIR(newInstruction, "should use VM");
@@ -560,45 +560,45 @@ ParallelSafetyVisitor::visitNewArray(MNe
     }
 
     return replaceWithNewPar(newInstruction, newInstruction->templateObject());
 }
 
 bool
 ParallelSafetyVisitor::visitRest(MRest *ins)
 {
-    return replace(ins, MRestPar::New(alloc(), forkJoinSlice(), ins));
+    return replace(ins, MRestPar::New(alloc(), ForkJoinContext(), ins));
 }
 
 bool
 ParallelSafetyVisitor::visitMathFunction(MMathFunction *ins)
 {
     return replace(ins, MMathFunction::New(alloc(), ins->input(), ins->function(), nullptr));
 }
 
 bool
 ParallelSafetyVisitor::visitConcat(MConcat *ins)
 {
-    return replace(ins, MConcatPar::New(alloc(), forkJoinSlice(), ins));
+    return replace(ins, MConcatPar::New(alloc(), ForkJoinContext(), ins));
 }
 
 bool
 ParallelSafetyVisitor::visitToString(MToString *ins)
 {
     MIRType inputType = ins->input()->type();
     if (inputType != MIRType_Int32 && inputType != MIRType_Double)
         return markUnsafe();
     return true;
 }
 
 bool
 ParallelSafetyVisitor::replaceWithNewPar(MInstruction *newInstruction,
                                          JSObject *templateObject)
 {
-    replace(newInstruction, MNewPar::New(alloc(), forkJoinSlice(), templateObject));
+    replace(newInstruction, MNewPar::New(alloc(), ForkJoinContext(), templateObject));
     return true;
 }
 
 bool
 ParallelSafetyVisitor::replace(MInstruction *oldInstruction,
                                MInstruction *replacementInstruction)
 {
     MBasicBlock *block = oldInstruction->block();
@@ -685,17 +685,17 @@ ParallelSafetyVisitor::insertWriteGuard(
         SpewMIR(writeInstruction, "write to NewPar prop does not require guard");
         return true;
       default:
         break;
     }
 
     MBasicBlock *block = writeInstruction->block();
     MGuardThreadExclusive *writeGuard =
-        MGuardThreadExclusive::New(alloc(), forkJoinSlice(), object);
+        MGuardThreadExclusive::New(alloc(), ForkJoinContext(), object);
     block->insertBefore(writeInstruction, writeGuard);
     writeGuard->adjustInputs(alloc(), writeGuard);
     return true;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Calls
 //
@@ -735,23 +735,23 @@ ParallelSafetyVisitor::visitCall(MCall *
 // In sequential Ion code, the stack limit is stored in the JSRuntime.
 // We store it in the thread context.  We therefore need a separate
 // instruction to access it, one parameterized by the thread context.
 // Similar considerations apply to checking for interrupts.
 
 bool
 ParallelSafetyVisitor::visitCheckOverRecursed(MCheckOverRecursed *ins)
 {
-    return replace(ins, MCheckOverRecursedPar::New(alloc(), forkJoinSlice()));
+    return replace(ins, MCheckOverRecursedPar::New(alloc(), ForkJoinContext()));
 }
 
 bool
 ParallelSafetyVisitor::visitInterruptCheck(MInterruptCheck *ins)
 {
-    return replace(ins, MCheckInterruptPar::New(alloc(), forkJoinSlice()));
+    return replace(ins, MCheckInterruptPar::New(alloc(), ForkJoinContext()));
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Specialized ops
 //
 // Some ops, like +, can be specialized to ints/doubles.  Anything
 // else is terrifying.
 //
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -95,19 +95,19 @@ InvokeFunction(JSContext *cx, HandleObje
         types::TypeScript::Monitor(cx, script, pc, rv.get());
     }
 
     *rval = rv;
     return true;
 }
 
 JSObject *
-NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize, gc::InitialHeap initialHeap)
+NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap)
 {
-    return gc::NewGCThing<JSObject, CanGC>(cx, allocKind, thingSize, initialHeap);
+    return js::NewGCObject<CanGC>(cx, allocKind, 0, initialHeap);
 }
 
 bool
 CheckOverRecursed(JSContext *cx)
 {
     // IonMonkey's stackLimit is equal to nativeStackLimit by default. When we
     // want to trigger an operation callback, we set the ionStackLimit to nullptr,
     // which causes the stack limit check to fail.
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -10,17 +10,17 @@
 #include "jspubtd.h"
 
 #include "jit/CompileInfo.h"
 #include "jit/IonFrames.h"
 
 namespace js {
 
 class DeclEnvObject;
-class ForkJoinSlice;
+class ForkJoinContext;
 
 namespace jit {
 
 enum DataType {
     Type_Void,
     Type_Bool,
     Type_Int32,
     Type_Double,
@@ -108,17 +108,17 @@ struct VMFunction
 
     // Contains an combination of enumerated types used by the gc for marking
     // arguments of the VM wrapper.
     uint64_t argumentRootTypes;
 
     // The root type of the out param if outParam == Type_Handle.
     RootType outParamRootType;
 
-    // Does this function take a ForkJoinSlice * or a JSContext *?
+    // Does this function take a ForkJoinContext * or a JSContext *?
     ExecutionMode executionMode;
 
     // Number of Values the VM wrapper should pop from the stack when it returns.
     // Used by baseline IC stubs so that they can use tail calls to call the VM
     // wrapper.
     uint32_t extraValuesToPop;
 
     uint32_t argc() const {
@@ -381,17 +381,17 @@ template <> struct OutParamToRootType<Mu
 
 template <class> struct MatchContext { };
 template <> struct MatchContext<JSContext *> {
     static const ExecutionMode execMode = SequentialExecution;
 };
 template <> struct MatchContext<ExclusiveContext *> {
     static const ExecutionMode execMode = SequentialExecution;
 };
-template <> struct MatchContext<ForkJoinSlice *> {
+template <> struct MatchContext<ForkJoinContext *> {
     static const ExecutionMode execMode = ParallelExecution;
 };
 template <> struct MatchContext<ThreadSafeContext *> {
     // ThreadSafeContext functions can be called from either mode, but for
     // calling from parallel they should be wrapped first, so we default to
     // SequentialExecution here.
     static const ExecutionMode execMode = SequentialExecution;
 };
@@ -561,18 +561,17 @@ class AutoDetectInvalidation
 
     ~AutoDetectInvalidation() {
         if (!disabled_ && ionScript_->invalidated())
             cx_->runtime()->setIonReturnOverride(*rval_);
     }
 };
 
 bool InvokeFunction(JSContext *cx, HandleObject obj0, uint32_t argc, Value *argv, Value *rval);
-JSObject *NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize,
-                     gc::InitialHeap initialHeap);
+JSObject *NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap);
 
 bool CheckOverRecursed(JSContext *cx);
 bool CheckOverRecursedWithExtra(JSContext *cx, BaselineFrame *frame,
                                 uint32_t extra, uint32_t earlyCheck);
 
 bool DefVarOrConst(JSContext *cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
 bool SetConst(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, HandleValue rval);
 bool MutatePrototype(JSContext *cx, HandleObject obj, HandleValue value);
--- a/js/src/jit/arm/Architecture-arm.cpp
+++ b/js/src/jit/arm/Architecture-arm.cpp
@@ -34,16 +34,87 @@ namespace jit {
 
 uint32_t GetARMFlags()
 {
     static bool isSet = false;
     static uint32_t flags = 0;
     if (isSet)
         return flags;
 
+    static const char *env = getenv("ARMHWCAP");
+
+    if (env && env[0]) {
+        if (strstr(env, "help")) {
+            fflush(NULL);
+            printf(
+                   "\n"
+                   "usage: ARMHWCAP=option,option,option,... where options can be:\n"
+                   "\n"
+                   "  armv7    \n"
+                   "  vfp      \n"
+                   "  neon     \n"
+                   "  vfpv3    \n"
+                   "  vfpv3d16 \n"
+                   "  vfpv4    \n"
+                   "  idiva    \n"
+                   "  idivt    \n"
+                   "\n"
+                   );
+            exit(0);
+            /*NOTREACHED*/
+        } else {
+            // Canonicalize each token to have a leading and trailing space.
+            const char *start = env;  // Token start.
+            for (;;) {
+                char  ch = *start;
+                if (!ch) {
+                    // End of string.
+                    break;
+                }
+                if (ch == ' ' || ch == ',') {
+                    // Skip separator characters.
+                    start++;
+                    continue;
+                }
+                // Find the end of the token.
+                const char *end = start + 1;
+                for (; ; end++) {
+                    ch = *end;
+                    if (!ch || ch == ' ' || ch == ',')
+                        break;
+                }
+                size_t count = end - start;
+                if (count == 3 && strncmp(start, "vfp", 3) == 0)
+                    flags |= HWCAP_VFP;
+                else if (count == 5 && strncmp(start, "vfpv3", 5) == 0)
+                    flags |= HWCAP_VFPv3;
+                else if (count == 8 && strncmp(start, "vfpv3d16", 8) == 0)
+                    flags |= HWCAP_VFPv3D16;
+                else if (count == 5 && strncmp(start, "vfpv4", 5) == 0)
+                    flags |= HWCAP_VFPv4;
+                else if (count == 5 && strncmp(start, "idiva", 5) == 0)
+                    flags |= HWCAP_IDIVA;
+                else if (count == 5 && strncmp(start, "idivt", 5) == 0)
+                    flags |= HWCAP_IDIVT;
+                else if (count == 4 && strncmp(start, "neon", 4) == 0)
+                    flags |= HWCAP_NEON;
+                else if (count == 5 && strncmp(start, "armv7", 5) == 0)
+                    flags |= HWCAP_ARMv7;
+                else
+                    fprintf(stderr, "Warning: unexpected ARMHWCAP flag at: %s\n", start);
+                start = end;
+            }
+#ifdef DEBUG
+            IonSpew(IonSpew_Codegen, "ARMHWCAP: '%s'\n   flags: 0x%x\n", env, flags);
+#endif
+            isSet = true;
+            return flags;
+        }
+    }
+
 #ifdef JS_ARM_SIMULATOR
     isSet = true;
     flags = HWCAP_ARMv7 | HWCAP_VFP | HWCAP_VFPv4 | HWCAP_NEON;
     return flags;
 #else
 
 #if WTF_OS_LINUX
     int fd = open("/proc/self/auxv", O_RDONLY);
@@ -76,78 +147,95 @@ uint32_t GetARMFlags()
 
 #elif defined(WTF_OS_ANDROID) || defined(MOZ_B2G)
     FILE *fp = fopen("/proc/cpuinfo", "r");
     if (!fp)
         return false;
 
     char buf[1024];
     memset(buf, 0, sizeof(buf));
-    fread(buf, sizeof(char), sizeof(buf)-1, fp);
+    size_t len = fread(buf, sizeof(char), sizeof(buf) - 2, fp);
     fclose(fp);
-    if (strstr(buf, "vfp"))
+    // Canonicalize each token to have a leading and trailing space.
+    buf[len] = ' ';
+    buf[len + 1] = '\0';
+    for (size_t i = 0; i < len; i++) {
+        char  ch = buf[i];
+        if (!ch)
+            break;
+        else if (ch == '\n')
+            buf[i] = 0x20;
+        else
+            buf[i] = ch;
+    }
+
+    if (strstr(buf, " vfp "))
         flags |= HWCAP_VFP;
 
-    if (strstr(buf, "vfpv3"))
+    if (strstr(buf, " vfpv3 "))
         flags |= HWCAP_VFPv3;
 
-    if (strstr(buf, "vfpv3d16"))
+    if (strstr(buf, " vfpv3d16 "))
         flags |= HWCAP_VFPv3D16;
 
-    if (strstr(buf, "vfpv4"))
+    if (strstr(buf, " vfpv4 "))
         flags |= HWCAP_VFPv4;
 
-    if (strstr(buf, "idiva"))
+    if (strstr(buf, " idiva "))
         flags |= HWCAP_IDIVA;
 
-    if (strstr(buf, "idivt"))
+    if (strstr(buf, " idivt "))
         flags |= HWCAP_IDIVT;
 
-    if (strstr(buf, "neon"))
+    if (strstr(buf, " neon "))
         flags |= HWCAP_NEON;
 
     // not part of the HWCAP flag, but I need to know this, and we're not using
     //  that bit, so... I'm using it
     if (strstr(buf, "ARMv7"))
         flags |= HWCAP_ARMv7;
 
+#ifdef DEBUG
+    IonSpew(IonSpew_Codegen, "ARMHWCAP: '%s'\n   flags: 0x%x\n", buf, flags);
+#endif
+
     isSet = true;
     return flags;
 #endif
 
     return 0;
 #endif // JS_ARM_SIMULATOR
 }
 
 bool hasMOVWT()
 {
-    return js::jit::GetARMFlags() & HWCAP_ARMv7;
+    return GetARMFlags() & HWCAP_ARMv7;
 }
 bool hasVFPv3()
 {
-    return js::jit::GetARMFlags() & HWCAP_VFPv3;
+    return GetARMFlags() & HWCAP_VFPv3;
 }
 bool hasVFP()
 {
-    return js::jit::GetARMFlags() & HWCAP_VFP;
+    return GetARMFlags() & HWCAP_VFP;
 }
 
 bool has32DP()
 {
-    return !(js::jit::GetARMFlags() & HWCAP_VFPv3D16 && !(js::jit::GetARMFlags() & HWCAP_NEON));
+    return !(GetARMFlags() & HWCAP_VFPv3D16 && !(GetARMFlags() & HWCAP_NEON));
 }
 bool useConvReg()
 {
     return has32DP();
 }
 
 bool hasIDIV()
 {
 #if defined HWCAP_IDIVA
-    return js::jit::GetARMFlags() & HWCAP_IDIVA;
+    return GetARMFlags() & HWCAP_IDIVA;
 #else
     return false;
 #endif
 }
 
 Registers::Code
 Registers::FromName(const char *name)
 {
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -399,31 +399,38 @@ class AutoLockSimulatorRuntime
         srt_->lockOwner_ = nullptr;
         PR_Unlock(srt_->lock_);
 #endif
     }
 };
 
 bool Simulator::ICacheCheckingEnabled = false;
 
+int Simulator::StopSimAt = -1;
+
 SimulatorRuntime *
 CreateSimulatorRuntime()
 {
     SimulatorRuntime *srt = js_new<SimulatorRuntime>();
     if (!srt)
         return nullptr;
 
     if (!srt->init()) {
         js_delete(srt);
         return nullptr;
     }
 
     if (getenv("ARM_SIM_ICACHE_CHECKS"))
         Simulator::ICacheCheckingEnabled = true;
 
+    char *stopAtStr = getenv("ARM_SIM_STOP_AT");
+    int32_t stopAt;
+    if (stopAtStr && sscanf(stopAtStr, "%d", &stopAt) == 1)
+        Simulator::StopSimAt = stopAt;
+
     return srt;
 }
 
 void
 DestroySimulatorRuntime(SimulatorRuntime *srt)
 {
     js_delete(srt);
 }
@@ -672,23 +679,28 @@ ArmDebugger::debug()
 
             // Use sscanf to parse the individual parts of the command line. At the
             // moment no command expects more than two parameters.
             int argc = sscanf(line,
                               "%" XSTR(COMMAND_SIZE) "s "
                               "%" XSTR(ARG_SIZE) "s "
                               "%" XSTR(ARG_SIZE) "s",
                               cmd, arg1, arg2);
-            if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
+            if (argc < 0) {
+                continue;
+            } else if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
                 sim_->instructionDecode(reinterpret_cast<SimInstruction *>(sim_->get_pc()));
+                sim_->icount_++;
             } else if ((strcmp(cmd, "skip") == 0)) {
                 sim_->set_pc(sim_->get_pc() + 4);
+                sim_->icount_++;
             } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
                 // Execute the one instruction we broke at with breakpoints disabled.
                 sim_->instructionDecode(reinterpret_cast<SimInstruction *>(sim_->get_pc()));
+                sim_->icount_++;
                 // Leave the debugger shell.
                 done = true;
             } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
                 if (argc == 2 || (argc == 3 && strcmp(arg2, "fp") == 0)) {
                     int32_t value;
                     double dvalue;
                     if (strcmp(arg1, "all") == 0) {
                         for (uint32_t i = 0; i < Registers::Total; i++) {
@@ -3842,35 +3854,42 @@ Simulator::instructionDecode(SimInstruct
         // inlined message address.
     } else if (instr->isStop()) {
         set_pc(get_pc() + 2 * SimInstruction::kInstrSize);
     }
     if (!pc_modified_)
         set_register(pc, reinterpret_cast<int32_t>(instr) + SimInstruction::kInstrSize);
 }
 
+
+template<bool EnableStopSimAt>
 void
 Simulator::execute()
 {
     // Get the PC to simulate. Cannot use the accessor here as we need the
     // raw PC value and not the one used as input to arithmetic instructions.
     int program_counter = get_pc();
     AsmJSActivation *activation = TlsPerThreadData.get()->asmJSActivationStackFromOwnerThread();
 
     while (program_counter != end_sim_pc) {
-        SimInstruction *instr = reinterpret_cast<SimInstruction *>(program_counter);
-        icount_++;
-        instructionDecode(instr);
-
-        int32_t rpc = resume_pc_;
-        if (MOZ_UNLIKELY(rpc != 0)) {
-            // AsmJS signal handler ran and we have to adjust the pc.
-            activation->setResumePC((void *)get_pc());
-            set_pc(rpc);
-            resume_pc_ = 0;
+        if (EnableStopSimAt && (icount_ == Simulator::StopSimAt)) {
+            ArmDebugger dbg(this);
+            dbg.debug();
+        } else {
+            SimInstruction *instr = reinterpret_cast<SimInstruction *>(program_counter);
+            instructionDecode(instr);
+            icount_++;
+
+            int32_t rpc = resume_pc_;
+            if (MOZ_UNLIKELY(rpc != 0)) {
+                // AsmJS signal handler ran and we have to adjust the pc.
+                activation->setResumePC((void *)get_pc());
+                set_pc(rpc);
+                resume_pc_ = 0;
+            }
         }
         program_counter = get_pc();
     }
 }
 
 void
 Simulator::callInternal(uint8_t *entry)
 {
@@ -3902,17 +3921,20 @@ Simulator::callInternal(uint8_t *entry)
     set_register(r6, callee_saved_value);
     set_register(r7, callee_saved_value);
     set_register(r8, callee_saved_value);
     set_register(r9, callee_saved_value);
     set_register(r10, callee_saved_value);
     set_register(r11, callee_saved_value);
 
     // Start the simulation
-    execute();
+    if (Simulator::StopSimAt != -1)
+        execute<true>();
+    else
+        execute<false>();
 
     // Check that the callee-saved registers have been preserved.
     MOZ_ASSERT(callee_saved_value == get_register(r4));
     MOZ_ASSERT(callee_saved_value == get_register(r5));
     MOZ_ASSERT(callee_saved_value == get_register(r6));
     MOZ_ASSERT(callee_saved_value == get_register(r7));
     MOZ_ASSERT(callee_saved_value == get_register(r8));
     MOZ_ASSERT(callee_saved_value == get_register(r9));
--- a/js/src/jit/arm/Simulator-arm.h
+++ b/js/src/jit/arm/Simulator-arm.h
@@ -147,16 +147,17 @@ class Simulator
         resume_pc_ = value;
     }
 
     uintptr_t stackLimit() const;
     bool overRecursed(uintptr_t newsp = 0) const;
     bool overRecursedWithExtra(uint32_t extra) const;
 
     // Executes ARM instructions until the PC reaches end_sim_pc.
+    template<bool EnableStopSimAt>
     void execute();
 
     // Sets up the simulator state and grabs the result on return.
     int64_t call(uint8_t* entry, int argument_count, ...);
 
     // Debugger input.
     void setLastDebuggerInput(char *input);
     char *lastDebuggerInput() { return lastDebuggerInput_; }
@@ -263,16 +264,18 @@ class Simulator
 
     // Executes one instruction.
     void instructionDecode(SimInstruction *instr);
 
   public:
     static bool ICacheCheckingEnabled;
     static void FlushICache(void *start, size_t size);
 
+    static int StopSimAt;
+
     // Runtime call support.
     static void *RedirectNativeFunction(void *nativeFunction, ABIFunctionType type);
 
   private:
     // Handle arguments and return value for runtime FP functions.
     void getFpArgs(double *x, double *y, int32_t *z);
     void setCallResultDouble(double result);
     void setCallResultFloat(float result);
--- a/js/src/jsapi-tests/testException.cpp
+++ b/js/src/jsapi-tests/testException.cpp
@@ -12,14 +12,15 @@ BEGIN_TEST(testException_bug860435)
     JS::RootedValue fun(cx);
 
     EVAL("ReferenceError", fun.address());
     CHECK(fun.isObject());
 
     JS::RootedValue v(cx);
     JS_CallFunctionValue(cx, global, fun, 0, v.address(), v.address());
     CHECK(v.isObject());
+    JS::RootedObject obj(cx, &v.toObject());
 
-    JS_GetProperty(cx, &v.toObject(), "stack", &v);
+    JS_GetProperty(cx, obj, "stack", &v);
     CHECK(v.isString());
     return true;
 }
 END_TEST(testException_bug860435)
--- a/js/src/jsapi-tests/testStructuredClone.cpp
+++ b/js/src/jsapi-tests/testStructuredClone.cpp
@@ -26,21 +26,22 @@ BEGIN_TEST(testStructuredClone_object)
     }
 
     {
         JSAutoCompartment ac(cx, g2);
         JS::RootedValue v2(cx);
 
         CHECK(JS_StructuredClone(cx, v1, &v2, nullptr, nullptr));
         CHECK(v2.isObject());
+        JS::RootedObject obj(cx, &v2.toObject());
 
         JS::RootedValue prop(cx);
-        CHECK(JS_GetProperty(cx, &v2.toObject(), "prop", &prop));
+        CHECK(JS_GetProperty(cx, obj, "prop", &prop));
         CHECK(prop.isInt32());
-        CHECK(&v1.toObject() != &v2.toObject());
+        CHECK(&v1.toObject() != obj);
         CHECK_EQUAL(prop.toInt32(), 1337);
     }
 
     return true;
 }
 END_TEST(testStructuredClone_object)
 
 BEGIN_TEST(testStructuredClone_string)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -581,17 +581,17 @@ JS_Init(void)
     if (!TlsPerThreadData.initialized() && !TlsPerThreadData.init())
         return false;
 
 #if defined(JS_ION)
     if (!jit::InitializeIon())
         return false;
 #endif
 
-    if (!ForkJoinSlice::initialize())
+    if (!ForkJoinContext::initialize())
         return false;
 
 #if EXPOSE_INTL_API
     UErrorCode err = U_ZERO_ERROR;
     u_init(&err);
     if (U_FAILURE(err))
         return false;
 #endif // EXPOSE_INTL_API
@@ -1137,115 +1137,111 @@ JS_InitStandardClasses(JSContext *cx, Ha
 }
 
 #define CLASP(name)                 (&name##Class)
 #define OCLASP(name)                (&name##Object::class_)
 #define TYPED_ARRAY_CLASP(type)     (&TypedArrayObject::classes[ScalarTypeRepresentation::type])
 #define EAGER_ATOM(name)            NAME_OFFSET(name)
 #define EAGER_CLASS_ATOM(name)      NAME_OFFSET(name)
 
-static JSObject *
-DummyInit(JSContext *cx, HandleObject obj)
-{
-    MOZ_ASSUME_UNREACHABLE();
-    return nullptr;
-}
+static js::Class DummyClass;
+static js::Class SentinelClass;
 
 typedef struct JSStdName {
-    ClassInitializerOp init;
     size_t      atomOffset;     /* offset of atom pointer in JSAtomState */
     const Class *clasp;
-    bool isDummy() const { return init == DummyInit; };
+    bool isDummy() const { return clasp == &DummyClass; };
+    bool isSentinel() const { return clasp == &SentinelClass; };
 } JSStdName;
 
 static const JSStdName*
 LookupStdName(JSRuntime *rt, HandleString name, const JSStdName *table)
 {
     MOZ_ASSERT(name->isAtom());
-    for (unsigned i = 0; table[i].init; i++) {
+    for (unsigned i = 0; !table[i].isSentinel(); i++) {
         if (table[i].isDummy())
             continue;
         JSAtom *atom = AtomStateOffsetToName(rt->atomState, table[i].atomOffset);
         MOZ_ASSERT(atom);
         if (name == atom)
             return &table[i];
     }
 
     return nullptr;
 }
 
 /*
  * Table of standard classes, indexed by JSProtoKey. For entries where the
  * JSProtoKey does not correspond to a class with a meaningful constructor, we
  * insert a null entry into the table.
  */
-#define STD_NAME_ENTRY(name, code, init, clasp) { init, EAGER_CLASS_ATOM(name), clasp },
-#define STD_DUMMY_ENTRY(name, code, init, dummy) { DummyInit, 0, nullptr },
+#define STD_NAME_ENTRY(name, code, init, clasp) { EAGER_CLASS_ATOM(name), clasp },
+#define STD_DUMMY_ENTRY(name, code, init, dummy) { 0, &DummyClass },
 static const JSStdName standard_class_names[] = {
   JS_FOR_PROTOTYPES(STD_NAME_ENTRY, STD_DUMMY_ENTRY)
-  { nullptr, 0, nullptr }
+  { 0, &SentinelClass }
 };
 
 /*
  * Table of top-level function and constant names and the init function of the
  * corresponding standard class that sets them up.
  */
 static const JSStdName builtin_property_names[] = {
-    {js_InitObjectClass,        EAGER_ATOM(eval), &JSObject::class_},
+    { EAGER_ATOM(eval), &JSObject::class_ },
 
     /* Global properties and functions defined by the Number class. */
-    {js_InitNumberClass,        EAGER_ATOM(NaN), OCLASP(Number)},
-    {js_InitNumberClass,        EAGER_ATOM(Infinity), OCLASP(Number)},
-    {js_InitNumberClass,        EAGER_ATOM(isNaN), OCLASP(Number)},
-    {js_InitNumberClass,        EAGER_ATOM(isFinite), OCLASP(Number)},
-    {js_InitNumberClass,        EAGER_ATOM(parseFloat), OCLASP(Number)},
-    {js_InitNumberClass,        EAGER_ATOM(parseInt), OCLASP(Number)},
+    { EAGER_ATOM(NaN), OCLASP(Number) },
+    { EAGER_ATOM(Infinity), OCLASP(Number) },
+    { EAGER_ATOM(isNaN), OCLASP(Number) },
+    { EAGER_ATOM(isFinite), OCLASP(Number) },
+    { EAGER_ATOM(parseFloat), OCLASP(Number) },
+    { EAGER_ATOM(parseInt), OCLASP(Number) },
 
     /* String global functions. */
-    {js_InitStringClass,        EAGER_ATOM(escape), OCLASP(String)},
-    {js_InitStringClass,        EAGER_ATOM(unescape), OCLASP(String)},
-    {js_InitStringClass,        EAGER_ATOM(decodeURI), OCLASP(String)},
-    {js_InitStringClass,        EAGER_ATOM(encodeURI), OCLASP(String)},
-    {js_InitStringClass,        EAGER_ATOM(decodeURIComponent), OCLASP(String)},
-    {js_InitStringClass,        EAGER_ATOM(encodeURIComponent), OCLASP(String)},
+    { EAGER_ATOM(escape), OCLASP(String) },
+    { EAGER_ATOM(unescape), OCLASP(String) },
+    { EAGER_ATOM(decodeURI), OCLASP(String) },
+    { EAGER_ATOM(encodeURI), OCLASP(String) },
+    { EAGER_ATOM(decodeURIComponent), OCLASP(String) },
+    { EAGER_ATOM(encodeURIComponent), OCLASP(String) },
 #if JS_HAS_UNEVAL
-    {js_InitStringClass,        EAGER_ATOM(uneval), OCLASP(String)},
+    { EAGER_ATOM(uneval), OCLASP(String) },
 #endif
 #ifdef ENABLE_BINARYDATA
-    {js_InitSIMDClass,          EAGER_ATOM(SIMD), OCLASP(SIMD)},
-    {js_InitTypedObjectModuleObject, EAGER_ATOM(TypedObject), OCLASP(TypedObjectModule)},
+    { EAGER_ATOM(SIMD), OCLASP(SIMD) },
+    { EAGER_ATOM(TypedObject), OCLASP(TypedObjectModule) },
 #endif
 
-    {nullptr,                     0, nullptr}
+    { 0, &SentinelClass }
 };
 
 static const JSStdName object_prototype_names[] = {
     /* Object.prototype properties (global delegates to Object.prototype). */
-    {js_InitObjectClass,        EAGER_ATOM(proto), &JSObject::class_},
+    { EAGER_ATOM(proto), &JSObject::class_ },
 #if JS_HAS_TOSOURCE
-    {js_InitObjectClass,        EAGER_ATOM(toSource), &JSObject::class_},
+    { EAGER_ATOM(toSource), &JSObject::class_ },
 #endif
-    {js_InitObjectClass,        EAGER_ATOM(toString), &JSObject::class_},
-    {js_InitObjectClass,        EAGER_ATOM(toLocaleString), &JSObject::class_},
-    {js_InitObjectClass,        EAGER_ATOM(valueOf), &JSObject::class_},
+    { EAGER_ATOM(toString), &JSObject::class_ },
+    { EAGER_ATOM(toLocaleString), &JSObject::class_ },
+    { EAGER_ATOM(valueOf), &JSObject::class_ },
 #if JS_HAS_OBJ_WATCHPOINT
-    {js_InitObjectClass,        EAGER_ATOM(watch), &JSObject::class_},
-    {js_InitObjectClass,        EAGER_ATOM(unwatch), &JSObject::class_},
+    { EAGER_ATOM(watch), &JSObject::class_ },
+    { EAGER_ATOM(unwatch), &JSObject::class_ },
 #endif
-    {js_InitObjectClass,        EAGER_ATOM(hasOwnProperty), &JSObject::class_},
-    {js_InitObjectClass,        EAGER_ATOM(isPrototypeOf), &JSObject::class_},
-    {js_InitObjectClass,        EAGER_ATOM(propertyIsEnumerable), &JSObject::class_},
+    { EAGER_ATOM(hasOwnProperty), &JSObject::class_ },
+    { EAGER_ATOM(isPrototypeOf), &JSObject::class_ },
+    { EAGER_ATOM(propertyIsEnumerable), &JSObject::class_ },
 #if JS_OLD_GETTER_SETTER_METHODS
-    {js_InitObjectClass,        EAGER_ATOM(defineGetter), &JSObject::class_},
-    {js_InitObjectClass,        EAGER_ATOM(defineSetter), &JSObject::class_},
-    {js_InitObjectClass,        EAGER_ATOM(lookupGetter), &JSObject::class_},
-    {js_InitObjectClass,        EAGER_ATOM(lookupSetter), &JSObject::class_},
+    { EAGER_ATOM(defineGetter), &JSObject::class_ },
+    { EAGER_ATOM(defineSetter), &JSObject::class_ },
+    { EAGER_ATOM(lookupGetter), &JSObject::class_ },
+    { EAGER_ATOM(lookupSetter), &JSObject::class_ },
 #endif
 
-    {nullptr,                   0, nullptr}
+    { 0, &SentinelClass }
 };
 
 #undef CLASP
 #undef TYPED_ARRAY_CLASP
 #undef EAGER_ATOM
 #undef EAGER_CLASS_ATOM
 #undef EAGER_ATOM_CLASP
 
@@ -1301,64 +1297,33 @@ JS_ResolveStandardClass(JSContext *cx, H
     if (stdnm) {
         /*
          * If this standard class is anonymous, then we don't want to resolve
          * by name.
          */
         if (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS)
             return true;
 
-        if (obj->as<GlobalObject>().isStandardClassResolved(stdnm->clasp))
-            return true;
-
-        if (!stdnm->init(cx, obj))
+        if (!obj->as<GlobalObject>().ensureConstructor(cx, JSCLASS_CACHED_PROTO_KEY(stdnm->clasp)))
             return false;
+
         *resolved = true;
     }
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_EnumerateStandardClasses(JSContext *cx, HandleObject obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     MOZ_ASSERT(obj->is<GlobalObject>());
-
-    /*
-     * Check whether we need to bind 'undefined' and define it if so.
-     * Since ES5 15.1.1.3 undefined can't be deleted.
-     */
-    HandlePropertyName undefinedName = cx->names().undefined;
-    RootedValue undefinedValue(cx, UndefinedValue());
-    if (!obj->nativeContains(cx, undefinedName) &&
-        !JSObject::defineProperty(cx, obj, undefinedName, undefinedValue,
-                                  JS_PropertyStub, JS_StrictPropertyStub,
-                                  JSPROP_PERMANENT | JSPROP_READONLY)) {
-        return false;
-    }
-
-    /*
-     * Initialize any classes that have not been initialized yet. Note that
-     * resolving everything in standard_class_names has the effect of resolving
-     * everything in builtin_property_names, so we don't need to iterate over
-     * that separately. Moreover, we'll resolve the Object constructor as well,
-     * so we can also skip object_prototype_names.
-     */
-    for (unsigned i = 0; standard_class_names[i].init; i++) {
-        const JSStdName &stdnm = standard_class_names[i];
-        // Watch out for dummy entries.
-        if (!stdnm.isDummy() && !obj->as<GlobalObject>().isStandardClassResolved(stdnm.clasp)) {
-            if (!stdnm.init(cx, obj))
-                return false;
-        }
-    }
-
-    return true;
+    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+    return GlobalObject::initStandardClasses(cx, global);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetClassObject(JSContext *cx, JSProtoKey key, MutableHandleObject objp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     return js_GetClassObject(cx, key, objp);
@@ -1412,17 +1377,18 @@ JS_GetFunctionPrototype(JSContext *cx, H
     return forObj->global().getOrCreateFunctionPrototype(cx);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetArrayPrototype(JSContext *cx, HandleObject forObj)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, forObj);
-    return forObj->global().getOrCreateArrayPrototype(cx);
+    Rooted<GlobalObject*> global(cx, &forObj->global());
+    return GlobalObject::getOrCreateArrayPrototype(cx, global);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetGlobalForObject(JSContext *cx, JSObject *obj)
 {
     AssertHeapIsIdle(cx);
     assertSameCompartment(cx, obj);
     return &obj->global();
@@ -3361,21 +3327,18 @@ JS_GetPropertyDescriptor(JSContext *cx, 
                          MutableHandle<JSPropertyDescriptor> desc)
 {
     RootedObject obj(cx, objArg);
     JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_GetPropertyDescriptorById(cx, obj, AtomToId(atom), flags, desc);
 }
 
 JS_PUBLIC_API(bool)
-JS_GetPropertyById(JSContext *cx, JSObject *objArg, jsid idArg, MutableHandleValue vp)
-{
-    RootedObject obj(cx, objArg);
-    RootedId id(cx, idArg);
-
+JS_GetPropertyById(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
+{
     return JS_ForwardGetPropertyTo(cx, obj, id, obj, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_ForwardGetPropertyTo(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject onBehalfOf,
                         JS::MutableHandleValue vp)
 {
     AssertHeapIsIdle(cx);
@@ -3383,17 +3346,17 @@ JS_ForwardGetPropertyTo(JSContext *cx, J
     assertSameCompartment(cx, obj, id);
     assertSameCompartment(cx, onBehalfOf);
     JSAutoResolveFlags rf(cx, 0);
 
     return JSObject::getGeneric(cx, obj, onBehalfOf, id, vp);
 }
 
 JS_PUBLIC_API(bool)
-JS_GetElement(JSContext *cx, JSObject *objArg, uint32_t index, MutableHandleValue vp)
+JS_GetElement(JSContext *cx, HandleObject objArg, uint32_t index, MutableHandleValue vp)
 {
     return JS_ForwardGetElementTo(cx, objArg, index, objArg, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_ForwardGetElementTo(JSContext *cx, JSObject *objArg, uint32_t index, JSObject *onBehalfOfArg,
                        MutableHandleValue vp)
 {
@@ -3403,30 +3366,34 @@ JS_ForwardGetElementTo(JSContext *cx, JS
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAutoResolveFlags rf(cx, 0);
 
     return JSObject::getElement(cx, obj, onBehalfOf, index, vp);
 }
 
 JS_PUBLIC_API(bool)
-JS_GetProperty(JSContext *cx, JSObject *objArg, const char *name, MutableHandleValue vp)
-{
-    RootedObject obj(cx, objArg);
+JS_GetProperty(JSContext *cx, HandleObject obj, const char *name, MutableHandleValue vp)
+{
     JSAtom *atom = Atomize(cx, name, strlen(name));
-    return atom && JS_GetPropertyById(cx, obj, AtomToId(atom), vp);
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_GetPropertyById(cx, obj, id, vp);
 }
 
 JS_PUBLIC_API(bool)
-JS_GetUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
+JS_GetUCProperty(JSContext *cx, HandleObject obj, const jschar *name, size_t namelen,
                  MutableHandleValue vp)
 {
-    RootedObject obj(cx, objArg);
     JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
-    return atom && JS_GetPropertyById(cx, obj, AtomToId(atom), vp);
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_GetPropertyById(cx, obj, id, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_SetPropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue v)
 {
     RootedValue value(cx, v);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
@@ -3545,20 +3512,19 @@ JS_DeleteProperty2(JSContext *cx, Handle
 
     JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
     return JSObject::deleteByValue(cx, obj, StringValue(atom), result);
 }
 
 JS_PUBLIC_API(bool)
-JS_DeleteUCProperty2(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
+JS_DeleteUCProperty2(JSContext *cx, HandleObject obj, const jschar *name, size_t namelen,
                      bool *result)
 {
-    RootedObject obj(cx, objArg);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAutoResolveFlags rf(cx, 0);
 
     JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
     return JSObject::deleteByValue(cx, obj, StringValue(atom), result);
@@ -4558,17 +4524,17 @@ JS::FinishOffThreadScript(JSContext *may
 {
 #ifdef JS_THREADSAFE
     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
     Maybe<AutoLastFrameCheck> lfc;
     if (maybecx)
         lfc.construct(maybecx);
 
-    return rt->workerThreadState->finishParseTask(maybecx, rt, token);
+    return WorkerThreadState().finishParseTask(maybecx, rt, token);
 #else
     MOZ_ASSUME_UNREACHABLE("Off thread compilation is not available.");
 #endif
 }
 
 JS_PUBLIC_API(JSScript *)
 JS_CompileScript(JSContext *cx, JS::HandleObject obj, const char *ascii,
                  size_t length, const JS::CompileOptions &options)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3022,20 +3022,20 @@ extern JS_PUBLIC_API(bool)
 JS_GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
                              JS::MutableHandle<JSPropertyDescriptor> desc);
 
 extern JS_PUBLIC_API(bool)
 JS_GetPropertyDescriptor(JSContext *cx, JSObject *obj, const char *name, unsigned flags,
                          JS::MutableHandle<JSPropertyDescriptor> desc);
 
 extern JS_PUBLIC_API(bool)
-JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, JS::MutableHandleValue vp);
+JS_GetProperty(JSContext *cx, JS::HandleObject obj, const char *name, JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
-JS_GetPropertyById(JSContext *cx, JSObject *obj, jsid id, JS::MutableHandleValue vp);
+JS_GetPropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
 JS_ForwardGetPropertyTo(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject onBehalfOf,
                         JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
 JS_SetProperty(JSContext *cx, JS::HandleObject obj, const char *name, JS::HandleValue v);
 
@@ -3077,27 +3077,27 @@ JS_HasUCProperty(JSContext *cx, JS::Hand
                  bool *vp);
 
 extern JS_PUBLIC_API(bool)
 JS_LookupUCProperty(JSContext *cx, JS::HandleObject obj,
                     const jschar *name, size_t namelen,
                     JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
-JS_GetUCProperty(JSContext *cx, JSObject *obj,
+JS_GetUCProperty(JSContext *cx, JS::HandleObject obj,
                  const jschar *name, size_t namelen,
                  JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
 JS_SetUCProperty(JSContext *cx, JS::HandleObject obj,
                  const jschar *name, size_t namelen,
                  JS::HandleValue v);
 
 extern JS_PUBLIC_API(bool)
-JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen,
+JS_DeleteUCProperty2(JSContext *cx, JS::HandleObject obj, const jschar *name, size_t namelen,
                      bool *succeeded);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_NewArrayObject(JSContext *cx, int length, jsval *vector);
 
 extern JS_PUBLIC_API(bool)
 JS_IsArrayObject(JSContext *cx, JS::HandleValue value);
 
@@ -3119,17 +3119,17 @@ JS_AlreadyHasOwnElement(JSContext *cx, J
 
 extern JS_PUBLIC_API(bool)
 JS_HasElement(JSContext *cx, JS::HandleObject obj, uint32_t index, bool *foundp);
 
 extern JS_PUBLIC_API(bool)
 JS_LookupElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
-JS_GetElement(JSContext *cx, JSObject *obj, uint32_t index, JS::MutableHandleValue vp);
+JS_GetElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
 JS_ForwardGetElementTo(JSContext *cx, JSObject *obj, uint32_t index, JSObject *onBehalfOf,
                        JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
 JS_SetElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::HandleValue v);
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -443,17 +443,17 @@ js::CanonicalizeArrayLengthValue(typenam
                              JSMSG_BAD_ARRAY_LENGTH);
     return false;
 }
 
 template bool
 js::CanonicalizeArrayLengthValue<SequentialExecution>(JSContext *cx,
                                                       HandleValue v, uint32_t *newLen);
 template bool
-js::CanonicalizeArrayLengthValue<ParallelExecution>(ForkJoinSlice *slice,
+js::CanonicalizeArrayLengthValue<ParallelExecution>(ForkJoinContext *cx,
                                                     HandleValue v, uint32_t *newLen);
 
 /* ES6 20130308 draft 8.4.2.4 ArraySetLength */
 template <ExecutionMode mode>
 bool
 js::ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cxArg,
                    Handle<ArrayObject*> arr, HandleId id,
                    unsigned attrs, HandleValue value, bool setterIsStrict)
@@ -723,17 +723,17 @@ js::ArraySetLength(typename ExecutionMod
     return true;
 }
 
 template bool
 js::ArraySetLength<SequentialExecution>(JSContext *cx, Handle<ArrayObject*> arr,
                                         HandleId id, unsigned attrs, HandleValue value,
                                         bool setterIsStrict);
 template bool
-js::ArraySetLength<ParallelExecution>(ForkJoinSlice *slice, Handle<ArrayObject*> arr,
+js::ArraySetLength<ParallelExecution>(ForkJoinContext *cx, Handle<ArrayObject*> arr,
                                       HandleId id, unsigned attrs, HandleValue value,
                                       bool setterIsStrict);
 
 bool
 js::WouldDefinePastNonwritableLength(ThreadSafeContext *cx,
                                      HandleObject obj, uint32_t index, bool strict,
                                      bool *definesPast)
 {
@@ -753,18 +753,18 @@ js::WouldDefinePastNonwritableLength(Thr
         *definesPast = false;
         return true;
     }
 
     *definesPast = true;
 
     // Error in strict mode code or warn with strict option.
     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING);
-    if (cx->isForkJoinSlice())
-        return cx->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, flags);
+    if (cx->isForkJoinContext())
+        return cx->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, flags);
 
     if (!cx->isJSContext())
         return true;
 
     JSContext *ncx = cx->asJSContext();
 
     if (!strict && !ncx->options().extraWarnings())
         return true;
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -359,18 +359,18 @@ js_ReportOutOfMemory(ThreadSafeContext *
     /*
      * OOMs are non-deterministic, especially across different execution modes
      * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr
      * so that the fuzzers can detect this.
      */
     fprintf(stderr, "js_ReportOutOfMemory called\n");
 #endif
 
-    if (cxArg->isForkJoinSlice()) {
-        cxArg->asForkJoinSlice()->setPendingAbortFatal(ParallelBailoutOutOfMemory);
+    if (cxArg->isForkJoinContext()) {
+        cxArg->asForkJoinContext()->setPendingAbortFatal(ParallelBailoutOutOfMemory);
         return;
     }
 
     if (!cxArg->isJSContext())
         return;
     JSContext *cx = cxArg->asJSContext();
 
     cx->runtime()->hadOutOfMemory = true;
@@ -439,18 +439,18 @@ js_ReportOverRecursed(ThreadSafeContext 
 }
 
 void
 js_ReportAllocationOverflow(ThreadSafeContext *cxArg)
 {
     if (!cxArg)
         return;
 
-    if (cxArg->isForkJoinSlice()) {
-        cxArg->asForkJoinSlice()->setPendingAbortFatal(ParallelBailoutOutOfMemory);
+    if (cxArg->isForkJoinContext()) {
+        cxArg->asForkJoinContext()->setPendingAbortFatal(ParallelBailoutOutOfMemory);
         return;
     }
 
     if (!cxArg->isJSContext())
         return;
     JSContext *cx = cxArg->asJSContext();
 
     AutoSuppressGC suppressGC(cx);
@@ -1047,32 +1047,29 @@ js_HandleExecutionInterrupt(JSContext *c
 }
 
 js::ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
   : ContextFriendFields(rt),
     contextKind_(kind),
     perThreadData(pt),
     allocator_(nullptr)
 {
-#ifdef JS_THREADSAFE
-    JS_ASSERT_IF(kind == Context_Exclusive, rt->workerThreadState != nullptr);
-#endif
 }
 
 bool
-ThreadSafeContext::isForkJoinSlice() const
+ThreadSafeContext::isForkJoinContext() const
 {
     return contextKind_ == Context_ForkJoin;
 }
 
-ForkJoinSlice *
-ThreadSafeContext::asForkJoinSlice()
+ForkJoinContext *
+ThreadSafeContext::asForkJoinContext()
 {
-    JS_ASSERT(isForkJoinSlice());
-    return reinterpret_cast<ForkJoinSlice *>(this);
+    JS_ASSERT(isForkJoinContext());
+    return reinterpret_cast<ForkJoinContext *>(this);
 }
 
 JSContext::JSContext(JSRuntime *rt)
   : ExclusiveContext(rt, &rt->mainThread, Context_JS),
     throwing(false),
     unwrappedException_(UndefinedValue()),
     options_(),
     reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -101,20 +101,19 @@ class AutoCycleDetector
 };
 
 /* Updates references in the cycle detection set if the GC moves them. */
 extern void
 TraceCycleDetectionSet(JSTracer *trc, ObjectSet &set);
 
 struct AutoResolving;
 class DtoaCache;
-class ForkJoinSlice;
+class ForkJoinContext;
 class RegExpCompartment;
 class RegExpStatics;
-class ForkJoinSlice;
 
 namespace frontend { struct CompileError; }
 
 /*
  * Execution Context Overview:
  *
  * Several different structures may be used to provide a context for operations
  * on the VM. Each context is thread local, but varies in what data it can
@@ -134,17 +133,17 @@ namespace frontend { struct CompileError
  * operate in any compartment or zone which is not used by an ExclusiveContext
  * or ThreadSafeContext, and will only run in parallel with threads using such
  * contexts.
  *
  * An ExclusiveContext coerces to a ThreadSafeContext, and a JSContext coerces
  * to an ExclusiveContext or ThreadSafeContext.
  *
  * Contexts which are a ThreadSafeContext but not an ExclusiveContext are used
- * to represent a ForkJoinSlice, the per-thread parallel context used in PJS.
+ * to represent a ForkJoinContext, the per-thread parallel context used in PJS.
  */
 
 struct ThreadSafeContext : ContextFriendFields,
                            public MallocProvider<ThreadSafeContext>
 {
     friend struct StackBaseShape;
     friend UnownedBaseShape *BaseShape::lookupUnowned(ThreadSafeContext *cx,
                                                       const StackBaseShape &base);
@@ -208,18 +207,18 @@ struct ThreadSafeContext : ContextFriend
         return nullptr;
     }
 
     ExclusiveContext *asExclusiveContext() const {
         JS_ASSERT(isExclusiveContext());
         return maybeExclusiveContext();
     }
 
-    bool isForkJoinSlice() const;
-    ForkJoinSlice *asForkJoinSlice();
+    bool isForkJoinContext() const;
+    ForkJoinContext *asForkJoinContext();
 
     // The generational GC nursery may only be used on the main thread.
 #ifdef JSGC_GENERATIONAL
     inline bool hasNursery() const {
         return isJSContext();
     }
 
     inline js::Nursery &nursery() {
@@ -278,18 +277,16 @@ struct ThreadSafeContext : ContextFriend
 
     // Accessors for immutable runtime data.
     JSAtomState &names() { return runtime_->atomState; }
     StaticStrings &staticStrings() { return runtime_->staticStrings; }
     const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; }
     PropertyName *emptyString() { return runtime_->emptyString; }
     FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
     bool useHelperThreads() { return runtime_->useHelperThreads(); }
-    unsigned cpuCount() { return runtime_->cpuCount(); }
-    size_t workerThreadCount() { return runtime_->workerThreadCount(); }
     void *runtimeAddressForJit() { return runtime_; }
     void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
     void *stackLimitAddressForJitCode(StackKind kind);
     size_t gcSystemPageSize() { return runtime_->gcSystemPageSize; }
     bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); }
     bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
 
     // Thread local data that may be accessed freely.
@@ -386,25 +383,16 @@ class ExclusiveContext : public ThreadSa
     }
     JSCompartment *atomsCompartment() {
         return runtime_->atomsCompartment();
     }
     ScriptDataTable &scriptDataTable() {
         return runtime_->scriptDataTable();
     }
 
-#ifdef JS_THREADSAFE
-    // Since JSRuntime::workerThreadState is necessarily initialized from the
-    // main thread before the first worker thread can access it, there is no
-    // possibility for a race read/writing it.
-    WorkerThreadState *workerThreadState() {
-        return runtime_->workerThreadState;
-    }
-#endif
-
     // Methods specific to any WorkerThread for the context.
     frontend::CompileError &addPendingCompileError();
     void addPendingOverRecursed();
 };
 
 inline void
 MaybeCheckStackRoots(ExclusiveContext *cx)
 {
@@ -1036,17 +1024,17 @@ bool intrinsic_NewParallelArray(JSContex
 class AutoLockForExclusiveAccess
 {
 #ifdef JS_THREADSAFE
     JSRuntime *runtime;
 
     void init(JSRuntime *rt) {
         runtime = rt;
         if (runtime->numExclusiveThreads) {
-            runtime->assertCanLock(JSRuntime::ExclusiveAccessLock);
+            runtime->assertCanLock(ExclusiveAccessLock);
             PR_Lock(runtime->exclusiveAccessLock);
 #ifdef DEBUG
             runtime->exclusiveAccessOwner = PR_GetCurrentThread();
 #endif
         } else {
             JS_ASSERT(!runtime->mainThreadHasExclusiveAccess);
             runtime->mainThreadHasExclusiveAccess = true;
         }
@@ -1091,17 +1079,17 @@ class AutoLockForExclusiveAccess
 class AutoLockForCompilation
 {
 #ifdef JS_THREADSAFE
     JSRuntime *runtime;
 
     void init(JSRuntime *rt) {
         runtime = rt;
         if (runtime->numCompilationThreads) {
-            runtime->assertCanLock(JSRuntime::CompilationLock);
+            runtime->assertCanLock(CompilationLock);
             PR_Lock(runtime->compilationLock);
 #ifdef DEBUG
             runtime->compilationLockOwner = PR_GetCurrentThread();
 #endif
         } else {
 #ifdef DEBUG
             JS_ASSERT(!runtime->mainThreadHasCompilationLock);
             runtime->mainThreadHasCompilationLock = true;
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -479,19 +479,19 @@ template <JSThreadSafeNative threadSafeN
 inline bool
 JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     return threadSafeNative(cx, argc, vp);
 }
 
 template <JSThreadSafeNative threadSafeNative>
 inline bool
-JSParallelNativeThreadSafeWrapper(js::ForkJoinSlice *slice, unsigned argc, JS::Value *vp)
+JSParallelNativeThreadSafeWrapper(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp)
 {
-    return threadSafeNative(slice, argc, vp);
+    return threadSafeNative(cx, argc, vp);
 }
 
 /* static */ inline JSContext *
 js::ExecutionModeTraits<js::SequentialExecution>::toContextType(ExclusiveContext *cx)
 {
     return cx->asJSContext();
 }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1816,20 +1816,20 @@ inline int CheckIsParallelNative(JSParal
  * Initializing with a normal function pointer seems to work fine. Hence
  * the ugliness that you see before you.
  */
 #define JS_JITINFO_NATIVE_PARALLEL(infoName, parallelOp)                \
     const JSJitInfo infoName =                                          \
         {{JS_CAST_PARALLEL_NATIVE_TO(parallelOp, JSJitGetterOp)},0,0,JSJitInfo::ParallelNative,JSJitInfo::AliasEverything,JSVAL_TYPE_MISSING,false,false,false,false,0}
 
 #define JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(infoName, wrapperName, serialOp) \
-    bool wrapperName##_ParallelNativeThreadSafeWrapper(js::ForkJoinSlice *slice, unsigned argc, \
+    bool wrapperName##_ParallelNativeThreadSafeWrapper(js::ForkJoinContext *cx, unsigned argc, \
                                                        JS::Value *vp)   \
     {                                                                   \
-        return JSParallelNativeThreadSafeWrapper<serialOp>(slice, argc, vp); \
+        return JSParallelNativeThreadSafeWrapper<serialOp>(cx, argc, vp); \
     }                                                                   \
     JS_JITINFO_NATIVE_PARALLEL(infoName, wrapperName##_ParallelNativeThreadSafeWrapper)
 
 static MOZ_ALWAYS_INLINE const JSJitInfo *
 FUNCTION_VALUE_TO_JITINFO(const JS::Value& v)
 {
     JS_ASSERT(js::GetObjectClass(&v.toObject()) == js::FunctionClassPtr);
     return reinterpret_cast<js::shadow::Function *>(&v.toObject())->jitinfo;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -203,19 +203,20 @@ ResolveInterpretedFunctionPrototype(JSCo
     JS_ASSERT(!IsInternalFunctionObject(obj));
     JS_ASSERT(!obj->isBoundFunction());
 
     // Make the prototype object an instance of Object with the same parent as
     // the function object itself, unless the function is an ES6 generator.  In
     // that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is
     // the GeneratorObjectPrototype singleton.
     bool isStarGenerator = obj->as<JSFunction>().isStarGenerator();
+    Rooted<GlobalObject*> global(cx, &obj->global());
     JSObject *objProto;
     if (isStarGenerator)
-        objProto = obj->global().getOrCreateStarGeneratorObjectPrototype(cx);
+        objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
     else
         objProto = obj->global().getOrCreateObjectPrototype(cx);
     if (!objProto)
         return nullptr;
     const Class *clasp = &JSObject::class_;
 
     RootedObject proto(cx, NewObjectWithGivenProto(cx, clasp, objProto, nullptr, SingletonObject));
     if (!proto)
@@ -391,17 +392,17 @@ js::XDRInterpretedFunction(XDRState<mode
     }
 
     if (!xdr->codeUint32(&firstword))
         return false;
 
     if (mode == XDR_DECODE) {
         JSObject *proto = nullptr;
         if (firstword & IsStarGenerator) {
-            proto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx);
+            proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
             if (!proto)
                 return false;
         }
         fun = NewFunctionWithProto(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED,
                                    NullPtr(), NullPtr(), proto,
                                    JSFunction::FinalizeKind, TenuredObject);
         if (!fun)
             return false;
@@ -439,17 +440,17 @@ template bool
 js::XDRInterpretedFunction(XDRState<XDR_DECODE> *, HandleObject, HandleScript, MutableHandleObject);
 
 JSObject *
 js::CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun)
 {
     /* NB: Keep this in sync with XDRInterpretedFunction. */
     JSObject *cloneProto = nullptr;
     if (srcFun->isStarGenerator()) {
-        cloneProto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx);
+        cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
         if (!cloneProto)
             return nullptr;
     }
     RootedFunction clone(cx, NewFunctionWithProto(cx, NullPtr(), nullptr, 0,
                                                   JSFunction::INTERPRETED, NullPtr(), NullPtr(),
                                                   cloneProto, JSFunction::FinalizeKind,
                                                   TenuredObject));
     if (!clone)
@@ -1649,17 +1650,17 @@ FunctionConstructor(JSContext *cx, unsig
      * NB: (new Function) is not lexically closed by its caller, it's just an
      * anonymous function in the top-level scope that its constructor inhabits.
      * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
      * and so would a call to f from another top-level's script or function.
      */
     RootedAtom anonymousAtom(cx, cx->names().anonymous);
     JSObject *proto = nullptr;
     if (isStarGenerator) {
-        proto = global->getOrCreateStarGeneratorFunctionPrototype(cx);
+        proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, global);
         if (!proto)
             return false;
     }
     RootedFunction fun(cx, NewFunctionWithProto(cx, js::NullPtr(), nullptr, 0,
                                                 JSFunction::INTERPRETED_LAMBDA, global,
                                                 anonymousAtom, proto,
                                                 JSFunction::FinalizeKind, TenuredObject));
     if (!fun)
@@ -1771,17 +1772,17 @@ js::CloneFunctionObject(JSContext *cx, H
                          !types::UseNewTypeForClone(fun);
 
     if (!useSameScript && fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
         return nullptr;
 
     NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject;
     JSObject *cloneProto = nullptr;
     if (fun->isStarGenerator()) {
-        cloneProto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx);
+        cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
         if (!cloneProto)
             return nullptr;
     }
     JSObject *cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, cloneProto,
                                                  SkipScopeParent(parent), allocKind, newKind);
     if (!cloneobj)
         return nullptr;
     RootedFunction clone(cx, &cloneobj->as<JSFunction>());
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1708,19 +1708,19 @@ ArenaLists::refillFreeList(ThreadSafeCon
              * return whatever value we get. If we aren't in a ForkJoin
              * session (i.e. we are in a worker thread async with the main
              * thread), we need to first ensure the main thread is not in a GC
              * session.
              */
             mozilla::Maybe<AutoLockWorkerThreadState> lock;
             JSRuntime *rt = zone->runtimeFromAnyThread();
             if (rt->exclusiveThreadsPresent()) {
-                lock.construct<WorkerThreadState &>(*rt->workerThreadState);
+                lock.construct();
                 while (rt->isHeapBusy())
-                    rt->workerThreadState->wait(WorkerThreadState::PRODUCER);
+                    WorkerThreadState().wait(GlobalWorkerThreadState::PRODUCER);
             }
 
             void *thing = cx->allocator()->arenas.allocateFromArenaInline(zone, thingKind);
             if (thing)
                 return thing;
 #else
             MOZ_CRASH();
 #endif
@@ -2108,17 +2108,17 @@ TriggerOperationCallback(JSRuntime *rt, 
     rt->triggerOperationCallback(JSRuntime::TriggerCallbackMainThread);
 }
 
 bool
 js::TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason)
 {
     /* Wait till end of parallel section to trigger GC. */
     if (InParallelSection()) {
-        ForkJoinSlice::current()->requestGC(reason);
+        ForkJoinContext::current()->requestGC(reason);
         return true;
     }
 
     /* Don't trigger GCs when allocating under the operation callback lock. */
     if (rt->currentThreadOwnsOperationCallbackLock())
         return false;
 
     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
@@ -2135,17 +2135,17 @@ js::TriggerGC(JSRuntime *rt, JS::gcreaso
 bool
 js::TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
 {
     /*
      * If parallel threads are running, wait till they
      * are stopped to trigger GC.
      */
     if (InParallelSection()) {
-        ForkJoinSlice::current()->requestZoneGC(zone, reason);
+        ForkJoinContext::current()->requestZoneGC(zone, reason);
         return true;
     }
 
     /* Zones in use by a thread with an exclusive context can't be collected. */
     if (zone->usedByExclusiveThread)
         return false;
 
     JSRuntime *rt = zone->runtimeFromMainThread();
@@ -4295,37 +4295,37 @@ AutoTraceSession::AutoTraceSession(JSRun
     // this lock during GC and the other thread is waiting, make sure we hold
     // the exclusive access lock during GC sessions.
     JS_ASSERT(rt->currentThreadHasExclusiveAccess());
 
     if (rt->exclusiveThreadsPresent()) {
         // Lock the worker thread state when changing the heap state in the
         // presence of exclusive threads, to avoid racing with refillFreeList.
 #ifdef JS_THREADSAFE
-        AutoLockWorkerThreadState lock(*rt->workerThreadState);
+        AutoLockWorkerThreadState lock;
         rt->heapState = heapState;
 #else
         MOZ_CRASH();
 #endif
     } else {
         rt->heapState = heapState;
     }
 }
 
 AutoTraceSession::~AutoTraceSession()
 {
     JS_ASSERT(runtime->isHeapBusy());
 
     if (runtime->exclusiveThreadsPresent()) {
 #ifdef JS_THREADSAFE
-        AutoLockWorkerThreadState lock(*runtime->workerThreadState);
+        AutoLockWorkerThreadState lock;
         runtime->heapState = prevState;
 
         // Notify any worker threads waiting for the trace session to end.
-        runtime->workerThreadState->notifyAll(WorkerThreadState::PRODUCER);
+        WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
 #else
         MOZ_CRASH();
 #endif
     } else {
         runtime->heapState = prevState;
     }
 }
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -143,16 +143,28 @@ template <> struct MapTypeToTraceKind<Un
 template <> struct MapTypeToTraceKind<types::TypeObject>{ static const JSGCTraceKind kind = JSTRACE_TYPE_OBJECT; };
 template <> struct MapTypeToTraceKind<JSAtom>           { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<JSString>         { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<JSFlatString>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<JSLinearString>   { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<PropertyName>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<jit::JitCode>     { static const JSGCTraceKind kind = JSTRACE_JITCODE; };
 
+/* Map from C++ type to finalize kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
+template <typename T> struct MapTypeToFinalizeKind {};
+template <> struct MapTypeToFinalizeKind<JSScript>          { static const AllocKind kind = FINALIZE_SCRIPT; };
+template <> struct MapTypeToFinalizeKind<LazyScript>        { static const AllocKind kind = FINALIZE_LAZY_SCRIPT; };
+template <> struct MapTypeToFinalizeKind<Shape>             { static const AllocKind kind = FINALIZE_SHAPE; };
+template <> struct MapTypeToFinalizeKind<BaseShape>         { static const AllocKind kind = FINALIZE_BASE_SHAPE; };
+template <> struct MapTypeToFinalizeKind<types::TypeObject> { static const AllocKind kind = FINALIZE_TYPE_OBJECT; };
+template <> struct MapTypeToFinalizeKind<JSShortString>     { static const AllocKind kind = FINALIZE_SHORT_STRING; };
+template <> struct MapTypeToFinalizeKind<JSString>          { static const AllocKind kind = FINALIZE_STRING; };
+template <> struct MapTypeToFinalizeKind<JSExternalString>  { static const AllocKind kind = FINALIZE_EXTERNAL_STRING; };
+template <> struct MapTypeToFinalizeKind<jit::JitCode>      { static const AllocKind kind = FINALIZE_JITCODE; };
+
 #if defined(JSGC_GENERATIONAL) || defined(DEBUG)
 static inline bool
 IsNurseryAllocable(AllocKind kind)
 {
     JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT);
     static const bool map[] = {
         false,     /* FINALIZE_OBJECT0 */
         true,      /* FINALIZE_OBJECT0_BACKGROUND */
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -48,17 +48,17 @@ ThreadSafeContext::allocator()
     JS_ASSERT_IF(isJSContext(), &asJSContext()->zone()->allocator == allocator_);
     return allocator_;
 }
 
 template <typename T>
 inline bool
 ThreadSafeContext::isThreadLocal(T thing) const
 {
-    if (!isForkJoinSlice())
+    if (!isForkJoinContext())
         return true;
 
     if (!IsInsideNursery(runtime_, thing) &&
         allocator_->arenas.containsArena(runtime_, thing->arenaHeader()))
     {
         // GC should be suppressed in preparation for mutating thread local
         // objects, as we don't want to trip any barriers.
         JS_ASSERT(!thing->zoneFromAnyThread()->needsBarrier());
@@ -374,158 +374,217 @@ class GCZoneGroupIter {
 
 typedef CompartmentsIterT<GCZoneGroupIter> GCCompartmentGroupIter;
 
 #ifdef JSGC_GENERATIONAL
 /*
  * Attempt to allocate a new GC thing out of the nursery. If there is not enough
  * room in the nursery or there is an OOM, this method will return nullptr.
  */
-template <typename T, AllowGC allowGC>
-inline T *
-TryNewNurseryGCThing(ThreadSafeContext *cxArg, size_t thingSize)
+template <AllowGC allowGC>
+inline JSObject *
+TryNewNurseryObject(ThreadSafeContext *cxArg, size_t thingSize, size_t nDynamicSlots)
 {
-    /* TODO: Integrate PJS with generational GC. */
     JSContext *cx = cxArg->asJSContext();
 
     JS_ASSERT(!IsAtomsCompartment(cx->compartment()));
     JSRuntime *rt = cx->runtime();
     Nursery &nursery = rt->gcNursery;
-    T *t = static_cast<T *>(nursery.allocate(thingSize));
-    if (t)
-        return t;
+    JSObject *obj = nursery.allocateObject(cx, thingSize, nDynamicSlots);
+    if (obj)
+        return obj;
     if (allowGC && !rt->mainThread.suppressGC) {
         MinorGC(cx, JS::gcreason::OUT_OF_NURSERY);
 
         /* Exceeding gcMaxBytes while tenuring can disable the Nursery. */
         if (nursery.isEnabled()) {
-            t = static_cast<T *>(nursery.allocate(thingSize));
-            JS_ASSERT(t);
-            return t;
+            JSObject *obj = nursery.allocateObject(cx, thingSize, nDynamicSlots);
+            JS_ASSERT(obj);
+            return obj;
         }
     }
     return nullptr;
 }
 #endif /* JSGC_GENERATIONAL */
 
+template <AllowGC allowGC>
+static inline bool
+CheckAllocatorState(ThreadSafeContext *cx, AllocKind kind)
+{
+    if (!cx->isJSContext())
+        return true;
+
+    JSContext *ncx = cx->asJSContext();
+#if defined(JS_GC_ZEAL) || defined(DEBUG)
+    JSRuntime *rt = ncx->runtime();
+    JS_ASSERT_IF(rt->isAtomsCompartment(ncx->compartment()),
+                 kind == FINALIZE_STRING ||
+                 kind == FINALIZE_SHORT_STRING ||
+                 kind == FINALIZE_JITCODE);
+    JS_ASSERT(!rt->isHeapBusy());
+    JS_ASSERT(!rt->noGCOrAllocationCheck);
+#endif
+
+    /* For testing out of memory conditions */
+    JS_OOM_POSSIBLY_FAIL_REPORT(ncx);
+
+#ifdef JS_GC_ZEAL
+    if (allowGC && rt->needZealousGC())
+        js::gc::RunDebugGC(ncx);
+#endif
+
+    if (allowGC)
+        MaybeCheckStackRoots(ncx);
+
+    return true;
+}
+
+template <typename T>
+static inline void
+CheckIncrementalZoneState(ThreadSafeContext *cx, T *t)
+{
+#ifdef DEBUG
+    if (!cx->isJSContext())
+        return;
+
+    Zone *zone = cx->asJSContext()->zone();
+    JS_ASSERT_IF(t && zone->wasGCStarted() && (zone->isGCMarking() || zone->isGCSweeping()),
+                 t->arenaHeader()->allocatedDuringIncremental);
+#endif
+}
+
 /*
- * Allocates a new GC thing. After a successful allocation the caller must
+ * Allocate a new GC thing. After a successful allocation the caller must
  * fully initialize the thing before calling any function that can potentially
  * trigger GC. This will ensure that GC tracing never sees junk values stored
  * in the partially initialized thing.
  */
-template <typename T, AllowGC allowGC>
-inline T *
-NewGCThing(ThreadSafeContext *cx, AllocKind kind, size_t thingSize, InitialHeap heap)
-{
-    JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
 
-    if (cx->isJSContext()) {
-        JSContext *ncx = cx->asJSContext();
-#if defined(JS_GC_ZEAL) || defined(DEBUG)
-        JSRuntime *rt = ncx->runtime();
-        JS_ASSERT_IF(rt->isAtomsCompartment(ncx->compartment()),
-                     kind == FINALIZE_STRING ||
-                     kind == FINALIZE_SHORT_STRING ||
-                     kind == FINALIZE_JITCODE);
-        JS_ASSERT(!rt->isHeapBusy());
-        JS_ASSERT(!rt->noGCOrAllocationCheck);
-#endif
+template <AllowGC allowGC>
+inline JSObject *
+AllocateObject(ThreadSafeContext *cx, AllocKind kind, size_t nDynamicSlots, InitialHeap heap)
+{
+    size_t thingSize = Arena::thingSize(kind);
 
-        /* For testing out of memory conditions */
-        JS_OOM_POSSIBLY_FAIL_REPORT(ncx);
-
-#ifdef JS_GC_ZEAL
-        if (allowGC && rt->needZealousGC())
-            js::gc::RunDebugGC(ncx);
-#endif
-
-        if (allowGC)
-            MaybeCheckStackRoots(ncx);
-    }
+    JS_ASSERT(thingSize == Arena::thingSize(kind));
+    if (!CheckAllocatorState<allowGC>(cx, kind))
+        return nullptr;
 
 #ifdef JSGC_GENERATIONAL
     if (cx->hasNursery() && ShouldNurseryAllocate(cx->nursery(), kind, heap)) {
-        T *t = TryNewNurseryGCThing<T, allowGC>(cx, thingSize);
-        if (t)
-            return t;
+        JSObject *obj = TryNewNurseryObject<allowGC>(cx, thingSize, nDynamicSlots);
+        if (obj)
+            return obj;
     }
 #endif
 
+    HeapSlot *slots = nullptr;
+    if (nDynamicSlots) {
+        slots = cx->pod_malloc<HeapSlot>(nDynamicSlots);
+        if (MOZ_UNLIKELY(!slots))
+            return nullptr;
+        js::Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
+    }
+
+    JSObject *obj = static_cast<JSObject *>(cx->allocator()->arenas.allocateFromFreeList(kind, thingSize));
+    if (!obj)
+        obj = static_cast<JSObject *>(js::gc::ArenaLists::refillFreeList<allowGC>(cx, kind));
+
+    if (obj)
+        obj->setInitialSlots(slots);
+    else
+        js_free(slots);
+
+    CheckIncrementalZoneState(cx, obj);
+    return obj;
+}
+
+template <typename T, AllowGC allowGC>
+inline T *
+AllocateNonObject(ThreadSafeContext *cx)
+{
+    AllocKind kind = MapTypeToFinalizeKind<T>::kind;
+    size_t thingSize = sizeof(T);
+
+    JS_ASSERT(thingSize == Arena::thingSize(kind));
+    if (!CheckAllocatorState<allowGC>(cx, kind))
+        return nullptr;
+
     T *t = static_cast<T *>(cx->allocator()->arenas.allocateFromFreeList(kind, thingSize));
     if (!t)
         t = static_cast<T *>(js::gc::ArenaLists::refillFreeList<allowGC>(cx, kind));
 
-#ifdef DEBUG
-    if (cx->isJSContext()) {
-        Zone *zone = cx->asJSContext()->zone();
-        JS_ASSERT_IF(t && zone->wasGCStarted() && (zone->isGCMarking() || zone->isGCSweeping()),
-                     t->arenaHeader()->allocatedDuringIncremental);
-    }
-#endif
-
+    CheckIncrementalZoneState(cx, t);
     return t;
 }
 
 } /* namespace gc */
-} /* namespace js */
 
 template <js::AllowGC allowGC>
 inline JSObject *
-js_NewGCObject(js::ThreadSafeContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap)
+NewGCObject(js::ThreadSafeContext *cx, js::gc::AllocKind kind, size_t nDynamicSlots, js::gc::InitialHeap heap)
 {
     JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
-    return js::gc::NewGCThing<JSObject, allowGC>(cx, kind, js::gc::Arena::thingSize(kind), heap);
+    return js::gc::AllocateObject<allowGC>(cx, kind, nDynamicSlots, heap);
+}
+
+template <js::AllowGC allowGC>
+inline jit::JitCode *
+NewJitCode(js::ThreadSafeContext *cx)
+{
+    return gc::AllocateNonObject<jit::JitCode, allowGC>(cx);
 }
 
+inline
+types::TypeObject *
+NewTypeObject(js::ThreadSafeContext *cx)
+{
+    return gc::AllocateNonObject<types::TypeObject, js::CanGC>(cx);
+}
+
+} /* namespace js */
+
 template <js::AllowGC allowGC>
 inline JSString *
 js_NewGCString(js::ThreadSafeContext *cx)
 {
-    return js::gc::NewGCThing<JSString, allowGC>(cx, js::gc::FINALIZE_STRING,
-                                                 sizeof(JSString), js::gc::TenuredHeap);
+    return js::gc::AllocateNonObject<JSString, allowGC>(cx);
 }
 
 template <js::AllowGC allowGC>
 inline JSShortString *
 js_NewGCShortString(js::ThreadSafeContext *cx)
 {
-    return js::gc::NewGCThing<JSShortString, allowGC>(cx, js::gc::FINALIZE_SHORT_STRING,
-                                                      sizeof(JSShortString), js::gc::TenuredHeap);
+    return js::gc::AllocateNonObject<JSShortString, allowGC>(cx);
 }
 
 inline JSExternalString *
 js_NewGCExternalString(js::ThreadSafeContext *cx)
 {
-    return js::gc::NewGCThing<JSExternalString, js::CanGC>(cx, js::gc::FINALIZE_EXTERNAL_STRING,
-                                                           sizeof(JSExternalString), js::gc::TenuredHeap);
+    return js::gc::AllocateNonObject<JSExternalString, js::CanGC>(cx);
 }
 
 inline JSScript *
 js_NewGCScript(js::ThreadSafeContext *cx)
 {
-    return js::gc::NewGCThing<JSScript, js::CanGC>(cx, js::gc::FINALIZE_SCRIPT,
-                                                   sizeof(JSScript), js::gc::TenuredHeap);
+    return js::gc::AllocateNonObject<JSScript, js::CanGC>(cx);
 }
 
 inline js::LazyScript *
 js_NewGCLazyScript(js::ThreadSafeContext *cx)
 {
-    return js::gc::NewGCThing<js::LazyScript, js::CanGC>(cx, js::gc::FINALIZE_LAZY_SCRIPT,
-                                                         sizeof(js::LazyScript), js::gc::TenuredHeap);
+    return js::gc::AllocateNonObject<js::LazyScript, js::CanGC>(cx);
 }
 
 inline js::Shape *
 js_NewGCShape(js::ThreadSafeContext *cx)
 {
-    return js::gc::NewGCThing<js::Shape, js::CanGC>(cx, js::gc::FINALIZE_SHAPE,
-                                                    sizeof(js::Shape), js::gc::TenuredHeap);
+    return js::gc::AllocateNonObject<js::Shape, js::CanGC>(cx);
 }
 
 template <js::AllowGC allowGC>
 inline js::BaseShape *
 js_NewGCBaseShape(js::ThreadSafeContext *cx)
 {
-    return js::gc::NewGCThing<js::BaseShape, allowGC>(cx, js::gc::FINALIZE_BASE_SHAPE,
-                                                      sizeof(js::BaseShape), js::gc::TenuredHeap);
+    return js::gc::AllocateNonObject<js::BaseShape, allowGC>(cx);
 }
 
 #endif /* jsgcinlines_h */
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1719,17 +1719,17 @@ bool
 TemporaryTypeSet::isDOMClass()
 {
     if (unknownObject())
         return false;
 
     unsigned count = getObjectCount();
     for (unsigned i = 0; i < count; i++) {
         const Class *clasp = getObjectClass(i);
-        if (clasp && !(clasp->flags & JSCLASS_IS_DOMJSCLASS))
+        if (clasp && (!(clasp->flags & JSCLASS_IS_DOMJSCLASS) || clasp->isProxy()))
             return false;
     }
 
     return count > 0;
 }
 
 bool
 TemporaryTypeSet::maybeCallable()
@@ -1760,17 +1760,17 @@ TemporaryTypeSet::maybeEmulatesUndefined
         return true;
 
     unsigned count = getObjectCount();
     for (unsigned i = 0; i < count; i++) {
         // The object emulates undefined if clasp->emulatesUndefined() or if
         // it's a WrapperObject, see EmulatesUndefined. Since all wrappers are
         // proxies, we can just check for that.
         const Class *clasp = getObjectClass(i);
-        if (clasp && (clasp->emulatesUndefined() || IsProxyClass(clasp)))
+        if (clasp && (clasp->emulatesUndefined() || clasp->isProxy()))
             return true;
     }
 
     return false;
 }
 
 JSObject *
 TemporaryTypeSet::getCommonPrototype()
@@ -1856,18 +1856,17 @@ TypeCompartment::newTypeObject(Exclusive
     if (!cx->typeInferenceEnabled())
         initialFlags |= OBJECT_FLAG_UNKNOWN_MASK;
 
     if (cx->isJSContext()) {
         if (proto.isObject() && IsInsideNursery(cx->asJSContext()->runtime(), proto.toObject()))
             initialFlags |= OBJECT_FLAG_NURSERY_PROTO;
     }
 
-    TypeObject *object = gc::NewGCThing<TypeObject, CanGC>(cx, gc::FINALIZE_TYPE_OBJECT,
-                                                           sizeof(TypeObject), gc::TenuredHeap);
+    TypeObject *object = js::NewTypeObject(cx);
     if (!object)
         return nullptr;
     new(object) TypeObject(clasp, proto, initialFlags);
 
     return object;
 }
 
 static inline jsbytecode *
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -173,20 +173,20 @@ template <> struct ExecutionModeTraits<S
     typedef JSContext * ContextType;
     typedef ExclusiveContext * ExclusiveContextType;
 
     static inline JSContext *toContextType(ExclusiveContext *cx);
 };
 
 template <> struct ExecutionModeTraits<ParallelExecution>
 {
-    typedef ForkJoinSlice * ContextType;
-    typedef ForkJoinSlice * ExclusiveContextType;
+    typedef ForkJoinContext * ContextType;
+    typedef ForkJoinContext * ExclusiveContextType;
 
-    static inline ForkJoinSlice *toContextType(ForkJoinSlice *cx) { return cx; }
+    static inline ForkJoinContext *toContextType(ForkJoinContext *cx) { return cx; }
 };
 
 namespace jit {
     struct IonScript;
     class IonAllocPolicy;
     class TempAllocator;
 }
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1546,24 +1546,24 @@ js_NewGenerator(JSContext *cx, const Fra
         RootedValue pval(cx);
         RootedObject fun(cx, stackfp->fun());
         // FIXME: This would be faster if we could avoid doing a lookup to get
         // the prototype for the instance.  Bug 906600.
         if (!JSObject::getProperty(cx, fun, fun, cx->names().prototype, &pval))
             return nullptr;
         JSObject *proto = pval.isObject() ? &pval.toObject() : nullptr;
         if (!proto) {
-            proto = global->getOrCreateStarGeneratorObjectPrototype(cx);
+            proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
             if (!proto)
                 return nullptr;
         }
         obj = NewObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto, global);
     } else {
         JS_ASSERT(stackfp->script()->isLegacyGenerator());
-        JSObject *proto = global->getOrCreateLegacyGeneratorObjectPrototype(cx);
+        JSObject *proto = GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global);
         if (!proto)
             return nullptr;
         obj = NewObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto, global);
     }
     if (!obj)
         return nullptr;
 
     /* Load and compute stack slot counts. */
@@ -1705,18 +1705,21 @@ SendToGenerator(JSContext *cx, JSGenerat
 
 MOZ_ALWAYS_INLINE bool
 star_generator_next(JSContext *cx, CallArgs args)
 {
     RootedObject thisObj(cx, &args.thisv().toObject());
     JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
 
     if (gen->state == JSGEN_CLOSED) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_GENERATOR_FINISHED);
-        return false;
+        RootedObject obj(cx, CreateItrResultObject(cx, JS::UndefinedHandleValue, true));
+        if (!obj)
+            return false;
+        args.rval().setObject(*obj);
+        return true;
     }
 
     if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
         RootedValue val(cx, args[0]);
         js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
                             JSDVG_SEARCH_STACK, val, js::NullPtr());
         return false;
     }
@@ -1727,17 +1730,17 @@ star_generator_next(JSContext *cx, CallA
 
 MOZ_ALWAYS_INLINE bool
 star_generator_throw(JSContext *cx, CallArgs args)
 {
     RootedObject thisObj(cx, &args.thisv().toObject());
 
     JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
     if (gen->state == JSGEN_CLOSED) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_GENERATOR_FINISHED);
+        cx->setPendingException(args.get(0));
         return false;
     }
 
     return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), StarGenerator,
                            args.rval());
 }
 
 MOZ_ALWAYS_INLINE bool
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4397,17 +4397,17 @@ js::NativeSet(typename ExecutionModeTrai
     return true;
 }
 
 template bool
 js::NativeSet<SequentialExecution>(JSContext *cx,
                                    Handle<JSObject*> obj, Handle<JSObject*> receiver,
                                    HandleShape shape, bool strict, MutableHandleValue vp);
 template bool
-js::NativeSet<ParallelExecution>(ForkJoinSlice *slice,
+js::NativeSet<ParallelExecution>(ForkJoinContext *cx,
                                  Handle<JSObject*> obj, Handle<JSObject*> receiver,
                                  HandleShape shape, bool strict, MutableHandleValue vp);
 
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE bool
 GetPropertyHelperInline(JSContext *cx,
                         typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
                         typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
@@ -4780,50 +4780,50 @@ js::ReportIfUndeclaredVarAssignment(JSCo
                                         JSREPORT_STRICT_MODE_ERROR,
                                         js_GetErrorMessage, nullptr,
                                         JSMSG_UNDECLARED_VAR, bytes.ptr());
 }
 
 bool
 JSObject::reportReadOnly(ThreadSafeContext *cxArg, jsid id, unsigned report)
 {
-    if (cxArg->isForkJoinSlice())
-        return cxArg->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, report);
+    if (cxArg->isForkJoinContext())
+        return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
 
     if (!cxArg->isJSContext())
         return true;
 
     JSContext *cx = cxArg->asJSContext();
     RootedValue val(cx, IdToValue(id));
     return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
                                     JSDVG_IGNORE_STACK, val, js::NullPtr(),
                                     nullptr, nullptr);
 }
 
 bool
 JSObject::reportNotConfigurable(ThreadSafeContext *cxArg, jsid id, unsigned report)
 {
-    if (cxArg->isForkJoinSlice())
-        return cxArg->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, report);
+    if (cxArg->isForkJoinContext())
+        return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
 
     if (!cxArg->isJSContext())
         return true;
 
     JSContext *cx = cxArg->asJSContext();
     RootedValue val(cx, IdToValue(id));
     return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
                                     JSDVG_IGNORE_STACK, val, js::NullPtr(),
                                     nullptr, nullptr);
 }
 
 bool
 JSObject::reportNotExtensible(ThreadSafeContext *cxArg, unsigned report)
 {
-    if (cxArg->isForkJoinSlice())
-        return cxArg->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, report);
+    if (cxArg->isForkJoinContext())
+        return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
 
     if (!cxArg->isJSContext())
         return true;
 
     JSContext *cx = cxArg->asJSContext();
     RootedValue val(cx, ObjectValue(*this));
     return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
                                     JSDVG_IGNORE_STACK, val, js::NullPtr(),
@@ -5075,17 +5075,17 @@ baseops::SetPropertyHelper(typename Exec
 }
 
 template bool
 baseops::SetPropertyHelper<SequentialExecution>(JSContext *cx, HandleObject obj,
                                                 HandleObject receiver,
                                                 HandleId id, unsigned defineHow,
                                                 MutableHandleValue vp, bool strict);
 template bool
-baseops::SetPropertyHelper<ParallelExecution>(ForkJoinSlice *slice, HandleObject obj,
+baseops::SetPropertyHelper<ParallelExecution>(ForkJoinContext *cx, HandleObject obj,
                                               HandleObject receiver,
                                               HandleId id, unsigned defineHow,
                                               MutableHandleValue vp, bool strict);
 
 bool
 baseops::SetElementHelper(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
                           unsigned defineHow, MutableHandleValue vp, bool strict)
 {
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -494,41 +494,25 @@ JSObject::create(js::ExclusiveContext *c
     JS_ASSERT(shape && type);
     JS_ASSERT(type->clasp() == shape->getObjectClass());
     JS_ASSERT(type->clasp() != &js::ArrayObject::class_);
     JS_ASSERT(js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots());
     JS_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind));
     JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
     JS_ASSERT_IF(extantSlots, dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan()));
 
-    js::HeapSlot *slots = extantSlots;
-    if (!slots) {
-        size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan());
-        if (nDynamicSlots) {
-            slots = cx->pod_malloc<js::HeapSlot>(nDynamicSlots);
-            if (!slots)
-                return nullptr;
-            js::Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
-        }
-    }
-
-    JSObject *obj = js_NewGCObject<js::CanGC>(cx, kind, heap);
-    if (!obj) {
-        js_free(slots);
+    size_t nDynamicSlots = extantSlots ? 0 : dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan());
+    JSObject *obj = js::NewGCObject<js::CanGC>(cx, kind, nDynamicSlots, heap);
+    if (!obj)
         return nullptr;
-    }
-
-#ifdef JSGC_GENERATIONAL
-    if (slots && heap != js::gc::TenuredHeap)
-        cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, slots);
-#endif
 
     obj->shape_.init(shape);
     obj->type_.init(type);
-    obj->slots = slots;
+    if (extantSlots)
+        obj->slots = extantSlots;
     obj->elements = js::emptyObjectElements;
 
     const js::Class *clasp = type->clasp();
     if (clasp->hasPrivate())
         obj->privateRef(shape->numFixedSlots()) = nullptr;
 
     size_t span = shape->slotSpan();
     if (span && clasp != &js::ArrayBufferObject::class_)
@@ -548,47 +532,25 @@ JSObject::createArray(js::ExclusiveConte
     JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
 
     /*
      * Arrays use their fixed slots to store elements, and must have enough
      * space for the elements header and also be marked as having no space for
      * named properties stored in those fixed slots.
      */
     JS_ASSERT(shape->numFixedSlots() == 0);
-
-    /*
-     * The array initially stores its elements inline, there must be enough
-     * space for an elements header.
-     */
-    JS_ASSERT(js::gc::GetGCKindSlots(kind) >= js::ObjectElements::VALUES_PER_HEADER);
-
-    js::HeapSlot *slots = nullptr;
-    if (size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan())) {
-        slots = cx->pod_malloc<js::HeapSlot>(nDynamicSlots);
-        if (!slots)
-            return nullptr;
-        js::Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
-    }
-
-    JSObject *obj = js_NewGCObject<js::CanGC>(cx, kind, heap);
-    if (!obj) {
-        js_free(slots);
+    size_t nDynamicSlots = dynamicSlotsCount(0, shape->slotSpan());
+    JSObject *obj = js::NewGCObject<js::CanGC>(cx, kind, nDynamicSlots, heap);
+    if (!obj)
         return nullptr;
-    }
-
-#ifdef JSGC_GENERATIONAL
-    if (slots && heap != js::gc::TenuredHeap)
-        cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, slots);
-#endif
 
     uint32_t capacity = js::gc::GetGCKindSlots(kind) - js::ObjectElements::VALUES_PER_HEADER;
 
     obj->shape_.init(shape);
     obj->type_.init(type);
-    obj->slots = slots;
     obj->setFixedElements();
     new (obj->getElementsHeader()) js::ObjectElements(capacity, length);
 
     size_t span = shape->slotSpan();
     if (span)
         obj->initializeSlotRange(0, span);
 
     return &obj->as<js::ArrayObject>();
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -865,17 +865,17 @@ js_InitJSONClass(JSContext *cx, HandleOb
 {
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
     /*
      * JSON requires that Boolean.prototype.valueOf be created and stashed in a
      * reserved slot on the global object; see js::BooleanGetPrimitiveValueSlow
      * called from PreprocessValue above.
      */
-    if (!global->getOrCreateBooleanPrototype(cx))
+    if (!GlobalObject::getOrCreateBooleanPrototype(cx, global))
         return nullptr;
 
     RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
     RootedObject JSON(cx, NewObjectWithClassProto(cx, &JSONClass, proto, global, SingletonObject));
     if (!JSON)
         return nullptr;
 
     if (!JS_DefineProperty(cx, global, js_JSON_str, OBJECT_TO_JSVAL(JSON),
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -329,24 +329,19 @@ class Proxy
     static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id,
                          MutableHandleValue vp);
 };
 
 // Use these in places where you don't want to #include vm/ProxyObject.h.
 extern JS_FRIEND_DATA(const js::Class* const) CallableProxyClassPtr;
 extern JS_FRIEND_DATA(const js::Class* const) UncallableProxyClassPtr;
 
-inline bool IsProxyClass(const Class *clasp)
-{
-    return clasp->isProxy();
-}
-
 inline bool IsProxy(JSObject *obj)
 {
-    return IsProxyClass(GetObjectClass(obj));
+    return GetObjectClass(obj)->isProxy();
 }
 
 BaseProxyHandler *
 GetProxyHandler(JSObject *obj);
 
 inline bool IsScriptedProxy(JSObject *obj)
 {
     if (!IsProxy(obj))
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -260,17 +260,17 @@ namespace js {
  * Parallel operations in general can have one of three states. They may
  * succeed, fail, or "bail", where bail indicates that the code encountered an
  * unexpected condition and should be re-run sequentially. Different
  * subcategories of the "bail" state are encoded as variants of TP_RETRY_*.
  */
 enum ParallelResult { TP_SUCCESS, TP_RETRY_SEQUENTIALLY, TP_RETRY_AFTER_GC, TP_FATAL };
 
 struct ThreadSafeContext;
-struct ForkJoinSlice;
+struct ForkJoinContext;
 class ExclusiveContext;
 
 class Allocator;
 
 class SkipRoot;
 
 enum ThingRootKind
 {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1297,21 +1297,25 @@ ScriptSource::setSourceCopy(ExclusiveCon
     // worker threads:
     //  - If we are on a worker thread, there must be another worker thread to
     //    execute our compression task.
     //  - If we are on the main thread, there must be at least two worker
     //    threads since at most one worker thread can be blocking on the main
     //    thread (see WorkerThreadState::canStartParseTask) which would cause a
     //    deadlock if there wasn't a second worker thread that could make
     //    progress on our compression task.
+#ifdef JS_THREADSAFE
+    bool canCompressOffThread =
+        WorkerThreadState().cpuCount > 1 &&
+        WorkerThreadState().threadCount >= 2;
+#else
+    bool canCompressOffThread = false;
+#endif
     const size_t HUGE_SCRIPT = 5 * 1024 * 1024;
-    if (length < HUGE_SCRIPT &&
-        cx->cpuCount() > 1 &&
-        cx->workerThreadCount() >= 2)
-    {
+    if (length < HUGE_SCRIPT && canCompressOffThread) {
         task->ss = this;
         task->chars = src;
         ready_ = false;
         if (!StartOffThreadCompression(cx, task))
             return false;
     } else {
         if (!adjustDataSize(sizeof(jschar) * length))
             return false;
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -55,18 +55,23 @@ LazyScript::functionDelazifying(JSContex
 
 } // namespace js
 
 inline JSFunction *
 JSScript::functionDelazifying() const
 {
     js::AutoThreadSafeAccess ts(this);
     JS_ASSERT(js::CurrentThreadCanWriteCompilationData());
-    if (function_ && function_->isInterpretedLazy())
+    if (function_ && function_->isInterpretedLazy()) {
         function_->setUnlazifiedScript(const_cast<JSScript *>(this));
+        // If this script has a LazyScript, make sure the LazyScript has a
+        // reference to the script when delazifying its canonical function.
+        if (lazyScript && !lazyScript->maybeScript())
+            lazyScript->initScript(const_cast<JSScript *>(this));
+    }
     return function_;
 }
 
 inline void
 JSScript::setFunction(JSFunction *fun)
 {
     JS_ASSERT(fun->isTenured());
     function_ = fun;
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -9,175 +9,163 @@
 #ifdef JS_THREADSAFE
 
 #include "mozilla/DebugOnly.h"
 
 #include "jsnativestack.h"
 #include "prmjtime.h"
 
 #include "frontend/BytecodeCompiler.h"
-#include "jit/ExecutionModeInlines.h"
 #include "jit/IonBuilder.h"
 #include "vm/Debugger.h"
 
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 
+namespace js {
+
+GlobalWorkerThreadState gWorkerThreadState;
+
+} // namespace js
+
 bool
 js::EnsureWorkerThreadsInitialized(ExclusiveContext *cx)
 {
     // If 'cx' is not a JSContext, we are already off the main thread and the
     // worker threads would have already been initialized.
-    if (!cx->isJSContext()) {
-        JS_ASSERT(cx->workerThreadState() != nullptr);
-        return true;
-    }
-
-    JSRuntime *rt = cx->asJSContext()->runtime();
-    if (rt->workerThreadState)
+    if (!cx->isJSContext())
         return true;
 
-    rt->workerThreadState = rt->new_<WorkerThreadState>(rt);
-    if (!rt->workerThreadState)
-        return false;
+    return WorkerThreadState().ensureInitialized();
+}
+
+static size_t
+ThreadCountForCPUCount(size_t cpuCount)
+{
+    return Max(cpuCount, (size_t)2);
+}
 
-    if (!rt->workerThreadState->init()) {
-        js_delete(rt->workerThreadState);
-        rt->workerThreadState = nullptr;
-        return false;
-    }
+void
+js::SetFakeCPUCount(size_t count)
+{
+    // This must be called before the threads have been initialized.
+    JS_ASSERT(!WorkerThreadState().threads);
 
-    return true;
+    WorkerThreadState().cpuCount = count;
+    WorkerThreadState().threadCount = ThreadCountForCPUCount(count);
 }
 
 #ifdef JS_ION
 
 bool
 js::StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData)
 {
     // Threads already initialized by the AsmJS compiler.
-    JS_ASSERT(cx->workerThreadState() != nullptr);
     JS_ASSERT(asmData->mir);
     JS_ASSERT(asmData->lir == nullptr);
 
-    WorkerThreadState &state = *cx->workerThreadState();
-    JS_ASSERT(state.numThreads);
-
-    AutoLockWorkerThreadState lock(state);
+    AutoLockWorkerThreadState lock;
 
     // Don't append this task if another failed.
-    if (state.asmJSWorkerFailed())
+    if (WorkerThreadState().asmJSWorkerFailed())
         return false;
 
-    if (!state.asmJSWorklist.append(asmData))
+    if (!WorkerThreadState().asmJSWorklist().append(asmData))
         return false;
 
-    state.notifyOne(WorkerThreadState::PRODUCER);
+    WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
     return true;
 }
 
 bool
 js::StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder)
 {
     if (!EnsureWorkerThreadsInitialized(cx))
         return false;
 
-    WorkerThreadState &state = *cx->runtime()->workerThreadState;
-    JS_ASSERT(state.numThreads);
+    AutoLockWorkerThreadState lock;
 
-    AutoLockWorkerThreadState lock(state);
-
-    if (!state.ionWorklist.append(builder))
+    if (!WorkerThreadState().ionWorklist().append(builder))
         return false;
 
     cx->runtime()->addCompilationThread();
 
-    state.notifyAll(WorkerThreadState::PRODUCER);
+    WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
     return true;
 }
 
 /*
  * Move an IonBuilder for which compilation has either finished, failed, or
- * been cancelled into the Ion compartment's finished compilations list.
- * All off thread compilations which are started must eventually be finished.
+ * been cancelled into the global finished compilation list. All off thread
+ * compilations which are started must eventually be finished.
  */
 static void
 FinishOffThreadIonCompile(jit::IonBuilder *builder)
 {
-    JSCompartment *compartment = builder->script()->compartment();
-    JS_ASSERT(compartment->runtimeFromAnyThread()->workerThreadState);
-    JS_ASSERT(compartment->runtimeFromAnyThread()->workerThreadState->isLocked());
-
-    compartment->jitCompartment()->finishedOffThreadCompilations().append(builder);
+    WorkerThreadState().ionFinishedList().append(builder);
 }
 
 #endif // JS_ION
 
 static inline bool
 CompiledScriptMatches(JSCompartment *compartment, JSScript *script, JSScript *target)
 {
     if (script)
         return target == script;
     return target->compartment() == compartment;
 }
 
 void
 js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
 {
 #ifdef JS_ION
-    JSRuntime *rt = compartment->runtimeFromMainThread();
-
-    if (!rt->workerThreadState)
-        return;
-
-    WorkerThreadState &state = *rt->workerThreadState;
-
     jit::JitCompartment *jitComp = compartment->jitCompartment();
     if (!jitComp)
         return;
 
-    AutoLockWorkerThreadState lock(state);
+    AutoLockWorkerThreadState lock;
+
+    if (!WorkerThreadState().threads)
+        return;
 
     /* Cancel any pending entries for which processing hasn't started. */
-    for (size_t i = 0; i < state.ionWorklist.length(); i++) {
-        jit::IonBuilder *builder = state.ionWorklist[i];
+    GlobalWorkerThreadState::IonBuilderVector &worklist = WorkerThreadState().ionWorklist();
+    for (size_t i = 0; i < worklist.length(); i++) {
+        jit::IonBuilder *builder = worklist[i];
         if (CompiledScriptMatches(compartment, script, builder->script())) {
             FinishOffThreadIonCompile(builder);
-            state.ionWorklist[i--] = state.ionWorklist.back();
-            state.ionWorklist.popBack();
+            WorkerThreadState().remove(worklist, &i);
         }
     }
 
     /* Wait for in progress entries to finish up. */
-    for (size_t i = 0; i < state.numThreads; i++) {
-        const WorkerThread &helper = state.threads[i];
+    for (size_t i = 0; i < WorkerThreadState().threadCount; i++) {
+        const WorkerThread &helper = WorkerThreadState().threads[i];
         while (helper.ionBuilder &&
                CompiledScriptMatches(compartment, script, helper.ionBuilder->script()))
         {
             helper.ionBuilder->cancel();
-            state.wait(WorkerThreadState::CONSUMER);
+            WorkerThreadState().wait(GlobalWorkerThreadState::C