Merge last PGO-green inbound changeset to m-c.
authorMs2ger <ms2ger@gmail.com>
Tue, 10 Sep 2013 10:13:12 +0200
changeset 160145 25bfaa9538928bc70683aea449dc25b6ece6d4d1
parent 160081 be1053dc223b5ee70f0ad2ab0cb7cfea9635ba7d (current diff)
parent 160144 ea33604f6232a07d3e4be77718226d569490407f (diff)
child 160158 9f9733d4c20eadae1a3246bd7684c1fcd48b23c6
push id407
push userlsblakk@mozilla.com
push dateTue, 03 Dec 2013 03:32:50 +0000
treeherdermozilla-release@babf8c9ebc52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.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 last PGO-green inbound changeset to m-c.
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/OwningNonNull.h
dom/bindings/UnionMember.h
js/src/builtin/Iterator-inl.h
js/src/vm/RegExpObject-inl.h
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -302,19 +302,17 @@
 #endif
 @BINPATH@/components/txtsvc.xpt
 @BINPATH@/components/txmgr.xpt
 #ifdef MOZ_USE_NATIVE_UCONV
 @BINPATH@/components/ucnative.xpt
 #endif
 @BINPATH@/components/uconv.xpt
 @BINPATH@/components/unicharutil.xpt
-#ifdef MOZ_UPDATER
 @BINPATH@/components/update.xpt
-#endif
 @BINPATH@/components/uriloader.xpt
 @BINPATH@/components/urlformatter.xpt
 @BINPATH@/components/webBrowser_core.xpt
 @BINPATH@/components/webbrowserpersist.xpt
 @BINPATH@/components/webshell_idls.xpt
 @BINPATH@/components/widget.xpt
 #ifdef XP_MACOSX
 @BINPATH@/components/widget_cocoa.xpt
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -5,17 +5,18 @@
 // the "exported" symbols
 let SocialUI,
     SocialChatBar,
     SocialFlyout,
     SocialMarks,
     SocialShare,
     SocialMenu,
     SocialToolbar,
-    SocialSidebar;
+    SocialSidebar,
+    SocialStatus;
 
 (function() {
 
 // The minimum sizes for the auto-resize panel code.
 const PANEL_MIN_HEIGHT = 100;
 const PANEL_MIN_WIDTH = 330;
 
 XPCOMUtils.defineLazyModuleGetter(this, "SharedFrame",
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6363,17 +6363,17 @@ function formatURL(aFormat, aIsPref) {
  */
 var gIdentityHandler = {
   // Mode strings used to control CSS display
   IDENTITY_MODE_IDENTIFIED                             : "verifiedIdentity", // High-quality identity information
   IDENTITY_MODE_DOMAIN_VERIFIED                        : "verifiedDomain",   // Minimal SSL CA-signed domain verification
   IDENTITY_MODE_UNKNOWN                                : "unknownIdentity",  // No trusted identity information
   IDENTITY_MODE_MIXED_DISPLAY_LOADED                   : "unknownIdentity mixedContent mixedDisplayContent",  // SSL with unauthenticated display content
   IDENTITY_MODE_MIXED_ACTIVE_LOADED                    : "unknownIdentity mixedContent mixedActiveContent",  // SSL with unauthenticated active (and perhaps also display) content
-  IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED    : "unknownIdentity mixedContent mixedDisplayContent",  // SSL with unauthenticated display content; unauthenticated active content is blocked.
+  IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED    : "unknownIdentity mixedContent mixedDisplayContentLoadedActiveBlocked",  // SSL with unauthenticated display content; unauthenticated active content is blocked.
   IDENTITY_MODE_CHROMEUI                               : "chromeUI",         // Part of the product's UI
 
   // Cache the most recent SSLStatus and Location seen in checkIdentity
   _lastStatus : null,
   _lastUri : null,
   _mode : "unknownIdentity",
 
   // smart getters
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -186,22 +186,29 @@ const DownloadsPanel = {
     DownloadsSummary.active = false;
     DownloadsCommon.log("DownloadsPanel terminated.");
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// Panel interface
 
   /**
-   * Main panel element in the browser window.
+   * Main panel element in the browser window, or null if the panel overlay
+   * hasn't been loaded yet.
    */
   get panel()
   {
+    // If the downloads panel overlay hasn't loaded yet, just return null
+    // without reseting this.panel.
+    let downloadsPanel = document.getElementById("downloadsPanel");
+    if (!downloadsPanel)
+      return null;
+
     delete this.panel;
-    return this.panel = document.getElementById("downloadsPanel");
+    return this.panel = downloadsPanel;
   },
 
   /**
    * Starts opening the downloads panel interface, anchored to the downloads
    * button of the browser window.  The list of downloads to display is
    * initialized the first time this method is called, and the panel is shown
    * only when data is ready.
    */
--- a/browser/components/downloads/test/browser/browser_basic_functionality.js
+++ b/browser/components/downloads/test/browser/browser_basic_functionality.js
@@ -14,16 +14,19 @@ function test_task()
     { state: nsIDM.DOWNLOAD_NOTSTARTED },
     { state: nsIDM.DOWNLOAD_PAUSED },
     { state: nsIDM.DOWNLOAD_FINISHED },
     { state: nsIDM.DOWNLOAD_FAILED },
     { state: nsIDM.DOWNLOAD_CANCELED },
   ];
 
   try {
+    // Wait for focus first
+    yield promiseFocus();
+
     // Ensure that state is reset in case previous tests didn't finish.
     yield task_resetState();
 
     // For testing purposes, show all the download items at once.
     var originalCountLimit = DownloadsView.kItemCountLimit;
     DownloadsView.kItemCountLimit = DownloadData.length;
     registerCleanupFunction(function () {
       DownloadsView.kItemCountLimit = originalCountLimit;
--- a/browser/components/downloads/test/browser/browser_first_download_panel.js
+++ b/browser/components/downloads/test/browser/browser_first_download_panel.js
@@ -5,16 +5,24 @@
 
 /**
  * Make sure the downloads panel only opens automatically on the first
  * download it notices. All subsequent downloads, even across sessions, should
  * not open the panel automatically.
  */
 function test_task()
 {
+  // Clear the download panel has shown preference first as this test is used to
+  // verify this preference's behaviour.
+  let oldPrefValue = true;
+  try {
+    oldPrefValue = Services.prefs.getBoolPref("browser.download.panel.shown");
+  } catch(ex) { }
+  Services.prefs.setBoolPref("browser.download.panel.shown", false);
+
   try {
     // Ensure that state is reset in case previous tests didn't finish.
     yield task_resetState();
 
     // With this set to false, we should automatically open the panel the first
     // time a download is started.
     DownloadsCommon.getData(window).panelHasShownBefore = false;
 
@@ -44,10 +52,14 @@ function test_task()
       setTimeout(deferTimeout.resolve, 2000);
       yield deferTimeout.promise;
     } finally {
       DownloadsPanel.onPopupShown = originalOnPopupShown;
     }
   } finally {
     // Clean up when the test finishes.
     yield task_resetState();
+    // Set the preference instead of clearing it afterwards to ensure the
+    // right value is used no matter what the default was. This ensures the
+    // panel doesn't appear and affect other tests.
+    Services.prefs.setBoolPref("browser.download.panel.shown", oldPrefValue);
   }
 }
--- a/browser/components/downloads/test/browser/head.js
+++ b/browser/components/downloads/test/browser/head.js
@@ -46,16 +46,20 @@ function promiseFocus()
   waitForFocus(deferred.resolve);
   return deferred.promise;
 }
 
 function promisePanelOpened()
 {
   let deferred = Promise.defer();
 
+  if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") {
+    return deferred.resolve();
+  }
+
   // Hook to wait until the panel is shown.
   let originalOnPopupShown = DownloadsPanel.onPopupShown;
   DownloadsPanel.onPopupShown = function () {
     DownloadsPanel.onPopupShown = originalOnPopupShown;
     originalOnPopupShown.apply(this, arguments);
 
     // Defer to the next tick of the event loop so that we don't continue
     // processing during the DOM event handler itself.
@@ -70,19 +74,16 @@ function task_resetState()
   // Remove all downloads.
   let publicList = yield Downloads.getPublicDownloadList();
   let downloads = yield publicList.getAll();
   for (let download of downloads) {
     publicList.remove(download);
     yield download.finalize(true);
   }
 
-  // Reset any prefs that might have been changed.
-  Services.prefs.clearUserPref("browser.download.panel.shown");
-
   DownloadsPanel.hidePanel();
 
   yield promiseFocus();
 }
 
 function task_addDownloads(aItems)
 {
   let startTimeMs = Date.now();
--- a/browser/devtools/shared/test/browser_observableobject.js
+++ b/browser/devtools/shared/test/browser_observableobject.js
@@ -44,16 +44,19 @@ function test() {
     {type: "get", path: "bar", value: {}},
     {type: "set", path: "foo.1", value: {}},
   ];
 
   function callback(event, path, value) {
     oe.off("get", callback);
     ok(event, "event defined");
     ok(path, "path defined");
+    if (index >= expected.length) {
+      return;
+    }
     let e = expected[index];
     is(event, e.type, "[" + index + "] Right event received");
     is(path.join("."), e.path, "[" + index + "] Path valid");
     is(str(value), str(e.value), "[" + index + "] Value valid");
     index++;
     areObjectsSynced();
     oe.on("get", callback);
   }
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -1399,16 +1399,20 @@ toolbar[mode="icons"] #zoom-in-button {
   .mixedActiveContent > #page-proxy-favicon[pageproxystate="valid"] {
     list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-active@2x.png);
   }
 
   .mixedDisplayContent > #page-proxy-favicon[pageproxystate="valid"] {
     list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-display@2x.png);
   }
 
+  .mixedDisplayContentLoadedActiveBlocked > #page-proxy-favicon[pageproxystate="valid"] {
+    list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-display@2x.png);
+  }
+
   #identity-box:hover:active > #page-proxy-favicon,
   #identity-box[open=true] > #page-proxy-favicon {
     -moz-image-region: rect(0, 64px, 32px, 32px);
   }
 
   /* The chromeUI identity-icon set includes three states,
      but OS X only uses two of them. */
   #identity-box.chromeUI:hover:active > #page-proxy-favicon,
--- a/browser/themes/shared/identity-block.inc.css
+++ b/browser/themes/shared/identity-block.inc.css
@@ -71,16 +71,20 @@
 .mixedActiveContent > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-active.png);
 }
 
 .mixedDisplayContent > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-display.png);
 }
 
+.mixedDisplayContentLoadedActiveBlocked > #page-proxy-favicon[pageproxystate="valid"] {
+  list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-display.png);
+}
+
 #page-proxy-favicon[pageproxystate="invalid"] {
   opacity: 0.3;
 }
 
 #identity-popup.chromeUI > #identity-popup-container > #identity-popup-icon {
   list-style-image: url("chrome://branding/content/icon64.png");
 }
 
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -61,16 +61,17 @@ MACH_MODULES = [
     'addon-sdk/mach_commands.py',
     'layout/tools/reftest/mach_commands.py',
     'python/mach_commands.py',
     'python/mach/mach/commands/commandinfo.py',
     'python/mozboot/mozboot/mach_commands.py',
     'python/mozbuild/mozbuild/config.py',
     'python/mozbuild/mozbuild/mach_commands.py',
     'python/mozbuild/mozbuild/frontend/mach_commands.py',
+    'testing/mach_commands.py',
     'testing/marionette/mach_commands.py',
     'testing/mochitest/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
     'testing/talos/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
     'tools/mercurial/mach_commands.py',
     'tools/mach_commands.py',
 ]
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1529,16 +1529,17 @@ public:
   static nsresult ProcessViewportInfo(nsIDocument *aDocument,
                                       const nsAString &viewportInfo);
 
   static nsIScriptContext* GetContextForEventHandlers(nsINode* aNode,
                                                       nsresult* aRv);
 
   static JSContext *GetCurrentJSContext();
   static JSContext *GetSafeJSContext();
+  static JSContext *GetDefaultJSContextForThread();
 
   /**
    * Case insensitive comparison between two strings. However it only ignores
    * case for ASCII characters a-z.
    */
   static bool EqualsIgnoreASCIICase(const nsAString& aStr1,
                                     const nsAString& aStr2);
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1761,16 +1761,17 @@ nsContentUtils::IsCallerChrome()
   // If the check failed, look for UniversalXPConnect on the cx compartment.
   return xpc::IsUniversalXPConnectEnabled(GetCurrentJSContext());
 }
 
 namespace mozilla {
 namespace dom {
 namespace workers {
 extern bool IsCurrentThreadRunningChromeWorker();
+extern JSContext* GetCurrentThreadJSContext();
 }
 }
 }
 
 bool
 nsContentUtils::ThreadsafeIsCallerChrome()
 {
   return NS_IsMainThread() ?
@@ -5193,16 +5194,27 @@ nsContentUtils::GetCurrentJSContext()
 JSContext *
 nsContentUtils::GetSafeJSContext()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return sXPConnect->GetSafeJSContext();
 }
 
 /* static */
+JSContext *
+nsContentUtils::GetDefaultJSContextForThread()
+{
+  if (MOZ_LIKELY(NS_IsMainThread())) {
+    return GetSafeJSContext();
+  } else {
+    return workers::GetCurrentThreadJSContext();
+  }
+}
+
+/* static */
 nsresult
 nsContentUtils::ASCIIToLower(nsAString& aStr)
 {
   PRUnichar* iter = aStr.BeginWriting();
   PRUnichar* end = aStr.EndWriting();
   if (MOZ_UNLIKELY(!iter || !end)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.cpp
@@ -10,16 +10,17 @@
 #include "nsAutoPtr.h"
 #include "nsDOMClassInfoID.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsStringStream.h"
 #include "nsTArray.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
+#include "nsIXPConnect.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsDOMMultipartFile, nsDOMFile,
                              nsIJSNativeInitializer)
 
--- a/content/events/src/Touch.h
+++ b/content/events/src/Touch.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Touch_h
 #define mozilla_dom_Touch_h
 
 #include "mozilla/Attributes.h"
 #include "nsWrapperCache.h"
+#include "nsAutoPtr.h"
 #include "Units.h"
 
 class nsPresContext;
 class nsEvent;
 
 namespace mozilla {
 namespace dom {
 
--- a/content/media/AbstractMediaDecoder.h
+++ b/content/media/AbstractMediaDecoder.h
@@ -70,17 +70,17 @@ public:
   virtual int64_t GetMediaDuration() = 0;
 
   // Set the duration of the media in microseconds.
   virtual void SetMediaDuration(int64_t aDuration) = 0;
 
   // Sets the duration of the media in microseconds. The MediaDecoder
   // fires a durationchange event to its owner (e.g., an HTML audio
   // tag).
-  virtual void UpdateMediaDuration(int64_t aDuration) = 0;
+  virtual void UpdateEstimatedMediaDuration(int64_t aDuration) = 0;
 
   // Set the media as being seekable or not.
   virtual void SetMediaSeekable(bool aMediaSeekable) = 0;
 
   // Set the transport level as being seekable or not.
   virtual void SetTransportSeekable(bool aTransportSeekable) = 0;
 
   virtual VideoFrameContainer* GetVideoFrameContainer() = 0;
--- a/content/media/BufferDecoder.cpp
+++ b/content/media/BufferDecoder.cpp
@@ -104,17 +104,17 @@ BufferDecoder::GetMediaDuration()
 
 void
 BufferDecoder::SetMediaDuration(int64_t aDuration)
 {
   // ignore
 }
 
 void
-BufferDecoder::UpdateMediaDuration(int64_t aDuration)
+BufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
 {
   // ignore
 }
 
 void
 BufferDecoder::SetMediaSeekable(bool aMediaSeekable)
 {
   // ignore
--- a/content/media/BufferDecoder.h
+++ b/content/media/BufferDecoder.h
@@ -45,17 +45,17 @@ public:
   void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_FINAL MOZ_OVERRIDE;
 
   int64_t GetEndMediaTime() const MOZ_FINAL MOZ_OVERRIDE;
 
   int64_t GetMediaDuration() MOZ_FINAL MOZ_OVERRIDE;
 
   void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
 
-  void UpdateMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
+  void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
 
   void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;
 
   void SetTransportSeekable(bool aTransportSeekable) MOZ_OVERRIDE;
 
   VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE;
   layers::ImageContainer* GetImageContainer() MOZ_OVERRIDE;
 
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -1267,20 +1267,24 @@ void MediaDecoder::SetDuration(double aD
 }
 
 void MediaDecoder::SetMediaDuration(int64_t aDuration)
 {
   NS_ENSURE_TRUE_VOID(GetStateMachine());
   GetStateMachine()->SetDuration(aDuration);
 }
 
-void MediaDecoder::UpdateMediaDuration(int64_t aDuration)
+void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mPlayState <= PLAY_STATE_LOADING) {
+    return;
+  }
   NS_ENSURE_TRUE_VOID(GetStateMachine());
-  GetStateMachine()->UpdateDuration(aDuration);
+  GetStateMachine()->UpdateEstimatedDuration(aDuration);
 }
 
 void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread());
   mMediaSeekable = aMediaSeekable;
   if (mDecoderStateMachine) {
     mDecoderStateMachine->SetMediaSeekable(aMediaSeekable);
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -492,18 +492,28 @@ public:
   // Call on the main thread only.
   virtual bool IsEnded() const;
 
   // Set the duration of the media resource in units of seconds.
   // This is called via a channel listener if it can pick up the duration
   // from a content header. Must be called from the main thread only.
   virtual void SetDuration(double aDuration);
 
+  // Sets the initial duration of the media. Called while the media metadata
+  // is being read and the decode is being setup.
   void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
-  void UpdateMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
+  // Updates the media duration. This is called while the media is being
+  // played, calls before the media has reached loaded metadata are ignored.
+  // The duration is assumed to be an estimate, and so a degree of
+  // instability is expected; if the incoming duration is not significantly
+  // different from the existing duration, the change request is ignored.
+  // If the incoming duration is significantly different, the duration is
+  // changed, this causes a durationchanged event to fire to the media
+  // element.
+  void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
 
   // Set a flag indicating whether seeking is supported
   virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;
   virtual void SetTransportSeekable(bool aTransportSeekable) MOZ_FINAL MOZ_OVERRIDE;
   // Returns true if this media supports seeking. False for example for WebM
   // files without an index and chained ogg files.
   virtual bool IsMediaSeekable() MOZ_FINAL MOZ_OVERRIDE;
   // Returns true if seeking is supported on a transport level (e.g. the server
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -113,16 +113,22 @@ static const uint32_t QUICK_BUFFERING_LO
 // quick buffering in a timely fashion, as the decode pauses when it
 // reaches AMPLE_AUDIO_USECS decoded data, and thus we'll never reach
 // QUICK_BUFFERING_LOW_DATA_USECS.
 PR_STATIC_ASSERT(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS);
 
 // This value has been chosen empirically.
 static const uint32_t AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS = 200000;
 
+// The amount of instability we tollerate in calls to
+// MediaDecoderStateMachine::UpdateEstimatedDuration(); changes of duration
+// less than this are ignored, as they're assumed to be the result of
+// instability in the duration estimation.
+static const int64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
+
 static TimeDuration UsecsToDuration(int64_t aUsecs) {
   return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
 }
 
 static int64_t DurationToUsecs(TimeDuration aDuration) {
   return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S);
 }
 
@@ -1439,19 +1445,22 @@ void MediaDecoderStateMachine::SetDurati
   if (mStartTime != -1) {
     mEndTime = mStartTime + aDuration;
   } else {
     mStartTime = 0;
     mEndTime = aDuration;
   }
 }
 
-void MediaDecoderStateMachine::UpdateDuration(int64_t aDuration)
+void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration)
 {
-  if (aDuration != GetDuration()) {
+  mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
+  int64_t duration = GetDuration();
+  if (aDuration != duration &&
+      abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
     SetDuration(aDuration);
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
     NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   }
 }
 
 void MediaDecoderStateMachine::SetMediaEndTime(int64_t aEndTime)
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -155,19 +155,22 @@ public:
   // aDuration is in microseconds.
   void SetDuration(int64_t aDuration);
 
   // Called while decoding metadata to set the end time of the media
   // resource. The decoder monitor must be obtained before calling this.
   // aEndTime is in microseconds.
   void SetMediaEndTime(int64_t aEndTime);
 
-  // Called from decode thread to update the duration. Can result in
-  // a durationchangeevent. aDuration is in microseconds.
-  void UpdateDuration(int64_t aDuration);
+  // Called from main thread to update the duration with an estimated value.
+  // The duration is only changed if its significantly different than the
+  // the current duration, as the incoming duration is an estimate and so
+  // often is unstable as more data is read and the estimate is updated.
+  // Can result in a durationchangeevent. aDuration is in microseconds.
+  void UpdateEstimatedDuration(int64_t aDuration);
 
   // Functions used by assertions to ensure we're calling things
   // on the appropriate threads.
   bool OnDecodeThread() const {
     return IsCurrentThread(mDecodeThread);
   }
   bool OnStateMachineThread() const;
   bool OnAudioThread() const {
--- a/content/media/directshow/DirectShowReader.cpp
+++ b/content/media/directshow/DirectShowReader.cpp
@@ -31,22 +31,24 @@ GetDirectShowLog() {
 #define LOG(...) PR_LOG(GetDirectShowLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 
 #else
 #define LOG(...)
 #endif
 
 DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder),
+    mMP3FrameParser(aDecoder->GetResource()->GetLength()),
 #ifdef DEBUG
     mRotRegister(0),
 #endif
     mNumChannels(0),
     mAudioRate(0),
-    mBytesPerSample(0)
+    mBytesPerSample(0),
+    mDuration(0)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   MOZ_COUNT_CTOR(DirectShowReader);
 }
 
 DirectShowReader::~DirectShowReader()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
@@ -361,9 +363,26 @@ DirectShowReader::OnDecodeThreadStart()
 
 void
 DirectShowReader::OnDecodeThreadFinish()
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   CoUninitialize();
 }
 
+void
+DirectShowReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mMP3FrameParser.IsMP3()) {
+    return;
+  }
+  mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
+  int64_t duration = mMP3FrameParser.GetDuration();
+  if (duration != mDuration) {
+    mDuration = duration;
+    MOZ_ASSERT(mDecoder);
+    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+    mDecoder->UpdateEstimatedMediaDuration(mDuration);
+  }
+}
+
 } // namespace mozilla
--- a/content/media/directshow/DirectShowReader.h
+++ b/content/media/directshow/DirectShowReader.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(DirectShowReader_h_)
 #define DirectShowReader_h_
 
 #include "Windows.h" // HRESULT, DWORD
 #include "MediaDecoderReader.h"
 #include "mozilla/RefPtr.h"
+#include "MP3FrameParser.h"
 
 class IGraphBuilder;
 class IMediaControl;
 class IMediaSeeking;
 class IMediaEventEx;
 
 namespace mozilla {
 
@@ -65,16 +66,20 @@ public:
                 int64_t aCurrentTime) MOZ_OVERRIDE;
 
   nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered,
                        int64_t aStartTime) MOZ_OVERRIDE;
 
   void OnDecodeThreadStart() MOZ_OVERRIDE;
   void OnDecodeThreadFinish() MOZ_OVERRIDE;
 
+  void NotifyDataArrived(const char* aBuffer,
+                         uint32_t aLength,
+                         int64_t aOffset) MOZ_OVERRIDE;
+
 private:
 
   // Calls mAudioQueue.Finish(), and notifies the filter graph that playback
   // is complete. aStatus is the code to send to the filter graph.
   // Always returns false, so that we can just "return Finish()" from
   // DecodeAudioData().
   bool Finish(HRESULT aStatus);
 
@@ -86,28 +91,36 @@ private:
 
   // Wraps the MediaResource, and feeds undecoded data into the filter graph.
   RefPtr<SourceFilter> mSourceFilter;
 
   // Sits at the end of the graph, removing decoded samples from the graph.
   // The graph will block while this is blocked, i.e. it will pause decoding.
   RefPtr<AudioSinkFilter> mAudioSinkFilter;
 
+  // Some MP3s are variable bitrate, so DirectShow's duration estimation
+  // can make its duration estimation based on the wrong bitrate. So we parse
+  // the MP3 frames to get a more accuate estimate of the duration.
+  MP3FrameParser mMP3FrameParser;
+
 #ifdef DEBUG
   // Used to add/remove the filter graph to the Running Object Table. You can
   // connect GraphEdit/GraphStudio to the graph to observe and/or debug its
   // topology and state.
   DWORD mRotRegister;
 #endif
 
   // Number of channels in the audio stream.
   uint32_t mNumChannels;
 
   // Samples per second in the audio stream.
   uint32_t mAudioRate;
 
   // Number of bytes per sample. Can be either 1 or 2.
   uint32_t mBytesPerSample;
+
+  // Duration of the stream, in microseconds.
+  int64_t mDuration;
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/omx/OmxDecoder.cpp
+++ b/content/media/omx/OmxDecoder.cpp
@@ -627,17 +627,17 @@ void OmxDecoder::NotifyDataArrived(const
 
   int64_t durationUs = mMP3FrameParser.GetDuration();
 
   if (durationUs != mDurationUs) {
     mDurationUs = durationUs;
 
     MOZ_ASSERT(mDecoder);
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-    mDecoder->UpdateMediaDuration(mDurationUs);
+    mDecoder->UpdateEstimatedMediaDuration(mDurationUs);
   }
 }
 
 void OmxDecoder::ReleaseVideoBuffer() {
   if (mVideoBuffer) {
     mVideoBuffer->release();
     mVideoBuffer = nullptr;
   }
--- a/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp
+++ b/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp
@@ -30,16 +30,17 @@
 #include "txXSLTProcessor.h"
 #include "nsIPrincipal.h"
 #include "nsThreadUtils.h"
 #include "jsapi.h"
 #include "txExprParser.h"
 #include "nsIErrorService.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsJSUtils.h"
+#include "nsIXPConnect.h"
 
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kXMLDocumentCID, NS_XMLDOCUMENT_CID);
 
 /**
  * Output Handler Factories
  */
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -7,16 +7,17 @@
 #ifndef nsDOMClassInfo_h___
 #define nsDOMClassInfo_h___
 
 #include "mozilla/Attributes.h"
 #include "nsIXPCScriptable.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIDOMScriptObjectFactory.h"
 #include "js/Id.h"
+#include "nsIXPConnect.h"
 
 #ifdef XP_WIN
 #undef GetClassName
 #endif
 
 class nsContentList;
 class nsDocument;
 class nsGlobalWindow;
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -668,16 +668,38 @@ bool
 NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
                                          JS::Handle<JSObject*> aScope,
                                          JS::Value* aRetval,
                                          xpcObjectHelper& aHelper,
                                          const nsIID* aIID,
                                          bool aAllowNativeWrapper)
 {
   nsresult rv;
+  // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
+  // on all threads.
+  nsWrapperCache *cache = aHelper.GetWrapperCache();
+
+  if (cache && cache->IsDOMBinding()) {
+      JS::RootedObject obj(aCx, cache->GetWrapper());
+      if (!obj) {
+          obj = cache->WrapObject(aCx, aScope);
+      }
+
+      if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, obj.address())) {
+        return false;
+      }
+
+      if (obj) {
+        *aRetval = JS::ObjectValue(*obj);
+        return true;
+      }
+  }
+
+  MOZ_ASSERT(NS_IsMainThread());
+
   if (!XPCConvert::NativeInterface2JSObject(aRetval, NULL, aHelper, aIID,
                                             NULL, aAllowNativeWrapper, &rv)) {
     // I can't tell if NativeInterface2JSObject throws JS exceptions
     // or not.  This is a sloppy stab at the right semantics; the
     // method really ought to be fixed to behave consistently.
     if (!JS_IsExceptionPending(aCx)) {
       Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
     }
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -12,16 +12,17 @@
 #include "nsIScriptGlobalObject.h"
 #include "nsIXPConnect.h"
 #include "nsIScriptContext.h"
 #include "nsPIDOMWindow.h"
 #include "nsJSUtils.h"
 #include "nsCxPusher.h"
 #include "nsIScriptSecurityManager.h"
 #include "xpcprivate.h"
+#include "WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CallbackObject)
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::CallbackObject)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
@@ -44,86 +45,96 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
 CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
                                      ErrorResult& aRv,
                                      ExceptionHandling aExceptionHandling,
                                      JSCompartment* aCompartment)
   : mCx(nullptr)
   , mCompartment(aCompartment)
   , mErrorResult(aRv)
   , mExceptionHandling(aExceptionHandling)
+  , mIsMainThread(NS_IsMainThread())
 {
+  if (mIsMainThread) {
+    nsContentUtils::EnterMicroTask();
+  }
   // We need to produce a useful JSContext here.  Ideally one that the callback
   // is in some sense associated with, so that we can sort of treat it as a
   // "script entry point".  Though once we actually have script entry points,
   // we'll need to do the script entry point bits once we have an actual
   // callable.
 
   // First, find the real underlying callback.
   JSObject* realCallback = js::UncheckedUnwrap(aCallback);
+  JSContext* cx = nullptr;
 
-  // Now get the nsIScriptGlobalObject for this callback.
-  JSContext* cx = nullptr;
-  nsIScriptContext* ctx = nullptr;
-  nsIScriptGlobalObject* sgo = nsJSUtils::GetStaticScriptGlobal(realCallback);
-  if (sgo) {
-    // Make sure that if this is a window it's the current inner, since the
-    // nsIScriptContext and hence JSContext are associated with the outer
-    // window.  Which means that if someone holds on to a function from a
-    // now-unloaded document we'd have the new document as the script entry
-    // point...
-    nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(sgo);
-    if (win) {
-      MOZ_ASSERT(win->IsInnerWindow());
-      nsPIDOMWindow* outer = win->GetOuterWindow();
-      if (!outer || win != outer->GetCurrentInnerWindow()) {
-        // Just bail out from here
-        return;
+  if (mIsMainThread) {
+    // Now get the nsIScriptGlobalObject for this callback.
+    nsIScriptContext* ctx = nullptr;
+    nsIScriptGlobalObject* sgo = nsJSUtils::GetStaticScriptGlobal(realCallback);
+    if (sgo) {
+      // Make sure that if this is a window it's the current inner, since the
+      // nsIScriptContext and hence JSContext are associated with the outer
+      // window.  Which means that if someone holds on to a function from a
+      // now-unloaded document we'd have the new document as the script entry
+      // point...
+      nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(sgo);
+      if (win) {
+        MOZ_ASSERT(win->IsInnerWindow());
+        nsPIDOMWindow* outer = win->GetOuterWindow();
+        if (!outer || win != outer->GetCurrentInnerWindow()) {
+          // Just bail out from here
+          return;
+        }
+      }
+      // if not a window at all, just press on
+
+      ctx = sgo->GetContext();
+      if (ctx) {
+        // We don't check whether scripts are enabled on ctx, because
+        // CheckFunctionAccess will do that anyway... and because we ignore them
+        // being disabled if the callee is system.
+        cx = ctx->GetNativeContext();
       }
     }
-    // if not a window at all, just press on
 
-    ctx = sgo->GetContext();
-    if (ctx) {
-      // We don't check whether scripts are enabled on ctx, because
-      // CheckFunctionAccess will do that anyway... and because we ignore them
-      // being disabled if the callee is system.
-      cx = ctx->GetNativeContext();
+    if (!cx) {
+      // We didn't manage to hunt down a script global to work with.  Just fall
+      // back on using the safe context.
+      cx = nsContentUtils::GetSafeJSContext();
     }
+
+    // Make sure our JSContext is pushed on the stack.
+    mCxPusher.Push(cx);
+  } else {
+    cx = workers::GetCurrentThreadJSContext();
   }
 
-  if (!cx) {
-    // We didn't manage to hunt down a script global to work with.  Just fall
-    // back on using the safe context.
-    cx = nsContentUtils::GetSafeJSContext();
-  }
-
-  // Make sure our JSContext is pushed on the stack.
-  mCxPusher.Push(cx);
-
   // Unmark the callable, and stick it in a Rooted before it can go gray again.
   // Nothing before us in this function can trigger a CC, so it's safe to wait
   // until here it do the unmark. This allows us to order the following two
   // operations _after_ the Push() above, which lets us take advantage of the
   // JSAutoRequest embedded in the pusher.
   //
   // We can do this even though we're not in the right compartment yet, because
   // Rooted<> does not care about compartments.
   JS::ExposeObjectToActiveJS(aCallback);
   mRootedCallable.construct(cx, aCallback);
 
-  // Check that it's ok to run this callback at all.
-  // FIXME: Bug 807371: we want a less silly check here.
-  // Make sure to unwrap aCallback before passing it in, because
-  // getting principals from wrappers is silly.
-  nsresult rv = nsContentUtils::GetSecurityManager()->
-    CheckFunctionAccess(cx, js::UncheckedUnwrap(aCallback), nullptr);
+  if (mIsMainThread) {
+    // Check that it's ok to run this callback at all.
+    // FIXME: Bug 807371: we want a less silly check here.
+    // Make sure to unwrap aCallback before passing it in, because
+    // getting principals from wrappers is silly.
+    nsresult rv = nsContentUtils::GetSecurityManager()->
+      CheckFunctionAccess(cx, js::UncheckedUnwrap(aCallback), nullptr);
 
-  if (NS_FAILED(rv)) {
-    // Security check failed.  We're done here.
-    return;
+    if (NS_FAILED(rv)) {
+      // Security check failed.  We're done here.
+      return;
+    }
   }
 
   // Enter the compartment of our callback, so we can actually work with it.
   mAc.construct(cx, aCallback);
 
   // And now we're ready to go.
   mCx = cx;
 
@@ -199,22 +210,29 @@ CallbackObject::CallSetup::~CallSetup()
   // XXXbz For that matter why do we need to manually call ScriptEvaluated at
   // all?  nsCxPusher::Pop will do that nowadays if !mScriptIsRunning, so the
   // concerns from bug 295983 don't seem relevant anymore.  Do we want to make
   // sure it's still called when !mScriptIsRunning?  I guess play it safe for
   // now and do what CallEventHandler did, which is call always.
 
   // Popping an nsCxPusher is safe even if it never got pushed.
   mCxPusher.Pop();
+
+  // It is important that this is the last thing we do, after leaving the
+  // compartment and popping the context.
+  if (mIsMainThread) {
+    nsContentUtils::LeaveMicroTask();
+  }
 }
 
 already_AddRefed<nsISupports>
 CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
                                           const nsIID& aIID) const
 {
+  MOZ_ASSERT(NS_IsMainThread());
   if (!aCallback) {
     return nullptr;
   }
 
   AutoSafeJSContext cx;
 
   JS::Rooted<JSObject*> callback(cx, aCallback->Callback());
 
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -146,20 +146,16 @@ protected:
     JSContext* mCx;
 
     // Caller's compartment. This will only have a sensible value if
     // mExceptionHandling == eRethrowContentExceptions.
     JSCompartment* mCompartment;
 
     // And now members whose construction/destruction order we need to control.
 
-    // Put our nsAutoMicrotask first, so it gets destroyed after everything else
-    // is gone
-    nsAutoMicroTask mMt;
-
     nsCxPusher mCxPusher;
 
     // Constructed the rooter within the scope of mCxPusher above, so that it's
     // always within a request during its lifetime.
     Maybe<JS::Rooted<JSObject*> > mRootedCallable;
 
     // Can't construct a JSAutoCompartment without a JSContext either.  Also,
     // Put mAc after mCxPusher so that we exit the compartment before we pop the
@@ -167,16 +163,17 @@ protected:
     // things.
     Maybe<JSAutoCompartment> mAc;
 
     // An ErrorResult to possibly re-throw exceptions on and whether
     // we should re-throw them.
     ErrorResult& mErrorResult;
     const ExceptionHandling mExceptionHandling;
     uint32_t mSavedJSContextOptions;
+    const bool mIsMainThread;
   };
 };
 
 template<class WebIDLCallbackT, class XPCOMCallbackT>
 class CallbackObjectHolder;
 
 template<class T, class U>
 void ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField);
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6,17 +6,17 @@
 
 import operator
 import os
 import re
 import string
 import math
 import itertools
 
-from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType
+from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute
 from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor
 
 AUTOGENERATED_WARNING_COMMENT = \
     "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
 ADDPROPERTY_HOOK_NAME = '_addProperty'
 FINALIZE_HOOK_NAME = '_finalize'
 TRACE_HOOK_NAME = '_trace'
 CONSTRUCT_HOOK_NAME = '_constructor'
@@ -2892,24 +2892,21 @@ for (uint32_t i = 0; i < length; ++i) {
             holderArgs = None
 
         return JSToNativeConversionInfo(templateBody, declType=typeName,
                                         holderType=holderType,
                                         dealWithOptional=isOptional,
                                         holderArgs=holderArgs)
 
     if type.isUnion():
-        if isMember:
-            raise TypeError("Can't handle unions as members, we have a "
-                            "holderType")
         nullable = type.nullable();
         if nullable:
             type = type.inner
 
-        unionArgumentObj = "${holderName}"
+        unionArgumentObj = "${declName}" if isMember else "${holderName}"
         if nullable:
             unionArgumentObj += ".ref()"
 
         memberTypes = type.flatMemberTypes
         names = []
 
         interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
         if len(interfaceMemberTypes) > 0:
@@ -3062,33 +3059,33 @@ for (uint32_t i = 0; i < length; ++i) {
                           '  ThrowErrorMessage(cx, MSG_NOT_IN_UNION, "%s", "%s");\n'
                           "%s\n"
                           "}" % (exceptionCodeIndented.define(),
                                  firstCap(sourceDescription),
                                  ", ".join(names),
                                  exceptionCodeIndented.define()))
         templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
 
-        typeName = type.name
+        typeName = type.name + ("ReturnValue" if isMember else "")
         argumentTypeName = typeName + "Argument"
         if nullable:
             typeName = "Nullable<" + typeName + " >"
 
         def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
             null = CGGeneric("if (%s${val}.isNullOrUndefined()) {\n"
                              "  %s.SetNull();\n"
                              "}" % (extraConditionForNull, setToNullVar))
             templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}")
             return CGList([null, templateBody], " else ")
 
         if type.hasNullableType:
             templateBody = handleNull(templateBody, unionArgumentObj)
 
         declType = CGGeneric(typeName)
-        holderType = CGGeneric(argumentTypeName)
+        holderType = CGGeneric(argumentTypeName) if not isMember else None
 
         # If we're isOptional and not nullable the normal optional handling will
         # handle lazy construction of our holder.  If we're nullable we do it
         # all by hand because we do not want our holder constructed if we're
         # null.
         declLoc = "${declName}"
         constructDecl = None
         if nullable:
@@ -3152,20 +3149,18 @@ for (uint32_t i = 0; i < length; ++i) {
             type.unroll().inner.identifier.name)
 
         if descriptor.nativeType == 'JSObject':
             # XXXbz Workers code does this sometimes
             assert descriptor.workers
             return handleJSObjectType(type, isMember, failureCode)
 
         if (descriptor.interface.isCallback() and
-            descriptor.interface.identifier.name != "EventListener"):
-            if descriptor.workers:
-                return handleJSObjectType(type, isMember, failureCode)
-
+            (descriptor.interface.identifier.name != "EventListener" or
+             descriptorProvider.workers)):
             name = descriptor.interface.identifier.name
             if type.nullable() or isCallbackReturnValue:
                 declType = CGGeneric("nsRefPtr<%s>" % name);
             else:
                 declType = CGGeneric("OwningNonNull<%s>" % name)
             conversion = (
                 "  ${declName} = new %s(&${val}.toObject());\n" % name)
 
@@ -3411,20 +3406,26 @@ for (uint32_t i = 0; i < length; ++i) {
         if isOptional:
             declType = "Optional<nsAString>"
         elif isInUnionReturnValue:
             declType = "nsString"
         else:
             declType = "NonNull<nsAString>"
 
         # No need to deal with optional here; we handled it already
+        decl = ""
+        if isInUnionReturnValue:
+            decl += "FakeDependentString str;\n"
         return JSToNativeConversionInfo(
+            "%s"
             "%s\n"
-            "${declName} = &${holderName};" %
-            getConversionCode("${holderName}"),
+            "${declName} = %s" %
+              (decl,
+               getConversionCode("str" if isInUnionReturnValue else "${holderName}"),
+               ("str;" if isInUnionReturnValue else "&${holderName};")),
             declType=CGGeneric(declType),
             holderType=CGGeneric("FakeDependentString"))
 
     if type.isByteString():
         assert not isEnforceRange and not isClamp
 
         nullable = toStringBool(type.nullable())
 
@@ -4117,17 +4118,18 @@ if (!returnArray) {
 }\n""" % (result, exceptionCodeIndented.define(),
           index, index, index,
           innerTemplate, index,
           CGIndenter(exceptionCodeIndented, 4).define())) +
                 setValue("JS::ObjectValue(*returnArray)"), False)
 
     if (type.isGeckoInterface() and
         (not type.isCallbackInterface() or
-         type.unroll().inner.identifier.name == "EventListener")):
+         (type.unroll().inner.identifier.name == "EventListener" and
+          not descriptorProvider.workers))):
         descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
         if type.nullable():
             wrappingCode = ("if (!%s) {\n" % (result) +
                             CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
                             "}\n")
         else:
             wrappingCode = ""
 
@@ -4623,16 +4625,25 @@ class CGCallGenerator(CGThing):
             self.cgRoot.append(CGGeneric("rv.WouldReportJSException();"))
             self.cgRoot.append(CGGeneric("if (rv.Failed()) {"))
             self.cgRoot.append(CGIndenter(errorReport))
             self.cgRoot.append(CGGeneric("}"))
 
     def define(self):
         return self.cgRoot.define()
 
+def getUnionMemberName(type):
+    if type.isGeckoInterface():
+        return type.inner.identifier.name
+    if type.isEnum():
+        return type.inner.identifier.name
+    if type.isArray() or type.isSequence():
+        return str(type)
+    return type.name
+
 class MethodNotCreatorError(Exception):
     def __init__(self, typename):
         self.typename = typename
 
 # A counter for making sure that when we're wrapping up things in
 # nested sequences we don't use the same variable name to iterate over
 # different sequences.
 sequenceWrapLevel = 0
@@ -4705,18 +4716,25 @@ def wrapTypeIntoCurrentCompartment(type,
                     member,
                     "%s.%s" % (value, CGDictionary.makeMemberName(member.identifier.name)))
                 if memberWrap:
                     memberWraps.append(memberWrap)
             myDict = myDict.parent
         return CGList(memberWraps, "\n") if len(memberWraps) != 0 else None
 
     if type.isUnion():
-        raise TypeError("Can't handle wrapping of unions in constructor "
-                        "arguments yet")
+        memberWraps = []
+        for member in type.flatMemberTypes:
+            memberWrap = wrapTypeIntoCurrentCompartment(
+               member,
+               "%s.%s" % (value, getUnionMemberName(member)))
+            if memberWrap:
+                memberWrap = CGIfWrapper(memberWrap, "mType == %s" % member)
+                memberWraps.append(memberWrap)
+        return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None
 
     if (type.isString() or type.isPrimitive() or type.isEnum() or
         type.isGeckoInterface() or type.isCallback() or type.isDate()):
         # All of these don't need wrapping
         return None
 
     raise TypeError("Unknown type; we don't know how to wrap it in constructor "
                     "arguments: %s" % type)
@@ -5328,18 +5346,22 @@ class FakeArgument():
     """
     def __init__(self, type, interfaceMember, name="arg"):
         self.type = type
         self.optional = False
         self.variadic = False
         self.defaultValue = None
         self.treatNullAs = interfaceMember.treatNullAs
         self.treatUndefinedAs = interfaceMember.treatUndefinedAs
-        self.enforceRange = False
-        self.clamp = False
+        if isinstance(interfaceMember, IDLAttribute):
+            self.enforceRange = interfaceMember.enforceRange
+            self.clamp = interfaceMember.clamp
+        else:
+            self.enforceRange = False
+            self.clamp = False
         class FakeIdentifier():
             def __init__(self):
                 self.name = name
         self.identifier = FakeIdentifier()
 
 class CGSetterCall(CGPerSignatureCall):
     """
     A class to generate a native object setter call for a particular IDL
@@ -6117,86 +6139,75 @@ def getUnionAccessorSignatureType(type, 
 def getUnionTypeTemplateVars(unionType, type, descriptorProvider, isReturnValue=False):
     # For dictionaries and sequences we need to pass None as the failureCode
     # for getJSToNativeConversionInfo.
     # Also, for dictionaries we would need to handle conversion of
     # null/undefined to the dictionary correctly.
     if type.isDictionary() or type.isSequence():
         raise TypeError("Can't handle dictionaries or sequences in unions")
 
-    if type.isGeckoInterface():
-        name = type.inner.identifier.name
-    elif type.isEnum():
-        name = type.inner.identifier.name
-    elif type.isArray() or type.isSequence():
-        name = str(type)
-    else:
-        name = type.name
+    name = getUnionMemberName(type)
 
     ctorArgs = "cx" if type.isSpiderMonkeyInterface() else ""
 
     tryNextCode = ("tryNext = true;\n"
                    "return true;")
     if type.isGeckoInterface():
-         tryNextCode = ("if (mUnion.mType != mUnion.eUninitialized) {"
-                        "  mUnion.Destroy%s();"
-                        "}" % name) + tryNextCode
+         prefix = "" if isReturnValue else "mUnion."
+         tryNextCode = ("if (%smType != %seUninitialized) {"
+                        "  %sDestroy%s();"
+                        "}") % (prefix, prefix, prefix, name) + tryNextCode
     conversionInfo = getJSToNativeConversionInfo(
         type, descriptorProvider, failureCode=tryNextCode,
         isDefinitelyObject=True, isInUnionReturnValue=isReturnValue,
         sourceDescription="member of %s" % unionType)
 
     # This is ugly, but UnionMember needs to call a constructor with no
     # arguments so the type can't be const.
     structType = conversionInfo.declType.define()
     if structType.startswith("const "):
         structType = structType[6:]
     externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
 
     if type.isObject():
         body = ("mUnion.mValue.mObject.SetValue(cx, obj);\n"
                 "mUnion.mType = mUnion.eObject;")
-        setters = [ClassMethod("SetToObject", "void",
-                               [Argument("JSContext*", "cx"),
-                                Argument("JSObject*", "obj")],
-                               inline=True, bodyInHeader=True,
-                               body=body)]
+        setter = ClassMethod("SetToObject", "void",
+                             [Argument("JSContext*", "cx"),
+                              Argument("JSObject*", "obj")],
+                             inline=True, bodyInHeader=True,
+                             body=body)
 
     else:
         jsConversion = string.Template(conversionInfo.template).substitute(
             {
                 "val": "value",
                 "mutableVal": "pvalue",
                 "declName": "SetAs" + name + "(%s)" % ctorArgs,
                 "holderName": "m" + name + "Holder",
                 }
             )
         jsConversion = CGWrapper(CGGeneric(jsConversion),
                                  pre="tryNext = false;\n",
                                  post="\n"
                                       "return true;")
-        setters = [ClassMethod("TrySetTo" + name, "bool",
-                               [Argument("JSContext*", "cx"),
-                                Argument("JS::Handle<JS::Value>", "value"),
-                                Argument("JS::MutableHandle<JS::Value>", "pvalue"),
-                                Argument("bool&", "tryNext")],
-                               inline=True, bodyInHeader=True,
-                               body=jsConversion.define())]
-        if type.isString():
-            setters.append(ClassMethod("SetStringData", "void",
-                [Argument("const nsDependentString::char_type*", "aData"),
-                 Argument("nsDependentString::size_type", "aLength")],
-                inline=True, bodyInHeader=True,
-                body="mStringHolder.SetData(aData, aLength);"))
+        setter = ClassMethod("TrySetTo" + name, "bool",
+                              [Argument("JSContext*", "cx"),
+                               Argument("JS::Handle<JS::Value>", "value"),
+                               Argument("JS::MutableHandle<JS::Value>", "pvalue"),
+                               Argument("bool&", "tryNext")],
+                              inline=not isReturnValue,
+                              bodyInHeader=not isReturnValue,
+                              body=jsConversion.define())
 
     return {
                 "name": name,
                 "structType": structType,
                 "externalType": externalType,
-                "setters": setters,
+                "setter": setter,
                 "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None,
                 "ctorArgs": ctorArgs,
                 "ctorArgList": [Argument("JSContext*", "cx")] if type.isSpiderMonkeyInterface() else []
                 }
 
 def mapTemplate(template, templateVarArray):
     return map(lambda v: string.Template(template).substitute(v),
                templateVarArray)
@@ -6249,16 +6260,26 @@ class CGUnionStruct(CGThing):
                 # bodyInHeader must be false for return values because they own
                 # their union members and we don't want include headers in
                 # UnionTypes.h just to call Addref/Release
                 methods.append(ClassMethod("SetAs" + vars["name"],
                                            vars["structType"] + "&",
                                            vars["ctorArgList"],
                                            bodyInHeader=not self.isReturnValue,
                                            body=body))
+                if self.isReturnValue:
+                    methods.append(vars["setter"])
+                    if t.isString():
+                        methods.append(
+                            ClassMethod("SetStringData", "void",
+                                [Argument("const nsString::char_type*", "aData"),
+                                 Argument("nsString::size_type", "aLength")],
+                                inline=True, bodyInHeader=True,
+                                body="mValue.mString.Value().Assign(aData, aLength);"))
+
             body = string.Template('MOZ_ASSERT(Is${name}(), "Wrong type!");\n'
                                    'mValue.m${name}.Destroy();\n'
                                    'mType = eUninitialized;').substitute(vars)
             methods.append(ClassMethod("Destroy" + vars["name"],
                                        "void",
                                        [],
                                        visibility="private",
                                        bodyInHeader=not self.isReturnValue,
@@ -6347,26 +6368,33 @@ class CGUnionConversionStruct(CGThing):
             methods.append(ClassMethod("SetNull", "bool", [],
                                        body=("mUnion.mType = mUnion.eNull;\n"
                                              "return true;"),
                                        inline=True, bodyInHeader=True))
 
         for t in self.type.flatMemberTypes:
             vars = getUnionTypeTemplateVars(self.type,
                                             t, self.descriptorProvider)
-            methods.extend(vars["setters"])
+            methods.append(vars["setter"])
             if vars["name"] != "Object":
                 body=string.Template("mUnion.mType = mUnion.e${name};\n"
                                      "return mUnion.mValue.m${name}.SetValue(${ctorArgs});").substitute(vars)
                 methods.append(ClassMethod("SetAs" + vars["name"],
                                            vars["structType"] + "&",
                                            vars["ctorArgList"],
                                            bodyInHeader=True,
                                            body=body,
                                            visibility="private"))
+                if t.isString():
+                    methods.append(ClassMethod("SetStringData", "void",
+                                     [Argument("const nsDependentString::char_type*", "aData"),
+                                      Argument("nsDependentString::size_type", "aLength")],
+                                     inline=True, bodyInHeader=True,
+                                     body="mStringHolder.SetData(aData, aLength);"))
+
             if vars["holderType"] is not None:
                 members.append(ClassMember("m%sHolder" % vars["name"],
                                            vars["holderType"]))
 
         return CGClass(structName + "Argument",
                        members=members,
                        constructors=[ctor],
                        methods=methods,
@@ -8110,18 +8138,22 @@ class CGDictionary(CGThing):
         self.dictionary = dictionary
         self.descriptorProvider = descriptorProvider
         self.needToInitIds = len(dictionary.members) > 0
         self.memberInfo = [
             (member,
              getJSToNativeConversionInfo(
                             member.type,
                             descriptorProvider,
+                            isEnforceRange=member.enforceRange,
+                            isClamp=member.clamp,
                             isMember="Dictionary",
                             isOptional=(not member.defaultValue),
+                            # Set this to true so that we get an owning union.
+                            isInUnionReturnValue=True,
                             defaultValue=member.defaultValue,
                             sourceDescription=("'%s' member of %s" %
                                                (member.identifier.name,
                                                 dictionary.identifier.name))))
             for member in dictionary.members ]
         self.structs = self.getStructs()
 
     def declare(self):
@@ -10519,16 +10551,17 @@ struct PrototypeTraits;
 
         (includes, implincludes,
          declarations, unions) = UnionTypes(config.getDescriptors(),
                                             config.getDictionaries(),
                                             config.getCallbacks(),
                                             config)
         includes.add("mozilla/dom/OwningNonNull.h")
         includes.add("mozilla/dom/UnionMember.h")
+        implincludes.add("mozilla/dom/PrimitiveConversions.h")
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom'], unions)
 
         curr = CGWrapper(curr, post='\n')
 
         namespaces = []
         stack = [CGList([])]
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -204,20 +204,17 @@ class Descriptor(DescriptorProvider):
         # Read the desc, and fill in the relevant defaults.
         ifaceName = self.interface.identifier.name
         if self.interface.isExternal():
             if self.workers:
                 nativeTypeDefault = "JSObject"
             else:
                 nativeTypeDefault = "nsIDOM" + ifaceName
         elif self.interface.isCallback():
-            if self.workers:
-                nativeTypeDefault = "JSObject"
-            else:
-                nativeTypeDefault = "mozilla::dom::" + ifaceName
+            nativeTypeDefault = "mozilla::dom::" + ifaceName
         else:
             if self.workers:
                 nativeTypeDefault = "mozilla::dom::workers::" + ifaceName
             else:
                 nativeTypeDefault = "mozilla::dom::" + ifaceName
 
         self.nativeType = desc.get('nativeType', nativeTypeDefault)
         self.jsImplParent = desc.get('jsImplParent', self.nativeType)
--- a/dom/bindings/UnionMember.h
+++ b/dom/bindings/UnionMember.h
@@ -34,16 +34,20 @@ public:
     return *mStorage.addr();
   }
   template<typename T1, typename T2>
   T& SetValue(const T1& aValue1, const T2& aValue2)
   {
     new (mStorage.addr()) T(aValue1, aValue2);
     return *mStorage.addr();
   }
+  T& Value()
+  {
+    return *mStorage.addr();
+  }
   const T& Value() const
   {
     return *mStorage.addr();
   }
   void Destroy()
   {
     mStorage.addr()->~T();
   }
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -2559,16 +2559,18 @@ class IDLAttribute(IDLInterfaceMember):
         assert isinstance(type, IDLType)
         self.type = type
         self.readonly = readonly
         self.inherit = inherit
         self.static = static
         self.lenientThis = False
         self._unforgeable = False
         self.stringifier = stringifier
+        self.enforceRange = False
+        self.clamp = False
 
         if static and identifier.name == "prototype":
             raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
                               [location])
 
         if readonly and inherit:
             raise WebIDLError("An attribute cannot be both 'readonly' and 'inherit'",
                               [self.location])
@@ -2682,16 +2684,26 @@ class IDLAttribute(IDLInterfaceMember):
         elif identifier == "LenientFloat":
             if self.readonly:
                 raise WebIDLError("[LenientFloat] used on a readonly attribute",
                                   [attr.location, self.location])
             if not self.type.includesRestrictedFloat():
                 raise WebIDLError("[LenientFloat] used on an attribute with a "
                                   "non-restricted-float type",
                                   [attr.location, self.location])
+        elif identifier == "EnforceRange":
+            if self.readonly:
+                raise WebIDLError("[EnforceRange] used on a readonly attribute",
+                                  [attr.location, self.location])
+            self.enforceRange = True
+        elif identifier == "Clamp":
+            if self.readonly:
+                raise WebIDLError("[Clamp] used on a readonly attribute",
+                                  [attr.location, self.location])
+            self.clamp = True
         elif (identifier == "Pref" or
               identifier == "SetterThrows" or
               identifier == "Pure" or
               identifier == "Throws" or
               identifier == "GetterThrows" or
               identifier == "ChromeOnly" or
               identifier == "SameObject" or
               identifier == "Constant" or
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -681,16 +681,20 @@ public:
   void SetIndirectlyImplementedProperty(bool);
   void IndirectlyImplementedMethod();
   uint32_t DiamondImplementedProperty();
 
   // Test EnforceRange/Clamp
   void DontEnforceRangeOrClamp(int8_t);
   void DoEnforceRange(int8_t);
   void DoClamp(int8_t);
+  void SetEnforcedByte(int8_t);
+  int8_t EnforcedByte();
+  void SetClampedByte(int8_t);
+  int8_t ClampedByte();
 
 private:
   // We add signatures here that _could_ start matching if the codegen
   // got data types wrong.  That way if it ever does we'll have a call
   // to these private deleted methods and compilation will fail.
   void SetReadonlyByte(int8_t) MOZ_DELETE;
   template<typename T>
   void SetWritableByte(T) MOZ_DELETE;
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -531,16 +531,18 @@ interface TestInterface {
   void passDictContainingDict(optional DictContainingDict arg);
   void passDictContainingSequence(optional DictContainingSequence arg);
   DictContainingSequence receiveDictContainingSequence();
 
   // EnforceRange/Clamp tests
   void dontEnforceRangeOrClamp(byte arg);
   void doEnforceRange([EnforceRange] byte arg);
   void doClamp([Clamp] byte arg);
+  [EnforceRange] attribute byte enforcedByte;
+  [Clamp] attribute byte clampedByte;
 
   // Typedefs
   const myLong myLongConstant = 5;
   void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
   AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
 
   // Static methods and attributes
@@ -702,16 +704,18 @@ DiamondBranch1A implements DiamondImplem
 DiamondBranch1B implements DiamondImplements;
 
 dictionary Dict : ParentDict {
   TestEnum someEnum;
   long x;
   long a;
   long b = 8;
   long z = 9;
+  [EnforceRange] unsigned long enforcedUnsignedLong;
+  [Clamp] unsigned long clampedUnsignedLong;
   DOMString str;
   DOMString empty = "";
   TestEnum otherEnum = "b";
   DOMString otherStr = "def";
   DOMString? yetAnotherStr = null;
   DOMString template;
   object someObj;
   boolean prototype;
@@ -731,16 +735,18 @@ dictionary Dict : ParentDict {
   unrestricted double  urDouble = 0;
   unrestricted double  urDouble2 = 1.1;
   unrestricted double  urDouble3 = -1.1;
   unrestricted double? urDouble4 = null;
   unrestricted double  infUrDouble = Infinity;
   unrestricted double  negativeInfUrDouble = -Infinity;
   unrestricted double  nanUrDouble = NaN;
 
+  (float or DOMString) floatOrString = "str";
+
   ArrayBuffer arrayBuffer;
   ArrayBuffer? nullableArrayBuffer;
   Uint8Array uint8Array;
   Float64Array? float64Array = null;
 };
 
 dictionary ParentDict : GrandparentDict {
   long c = 5;
@@ -759,16 +765,17 @@ dictionary DictContainingSequence {
   sequence<TestInterface> ourSequence2;
   sequence<any> ourSequence3;
   sequence<object> ourSequence4;
   sequence<object?> ourSequence5;
   sequence<object>? ourSequence6;
   sequence<object?>? ourSequence7;
   sequence<object>? ourSequence8 = null;
   sequence<object?>? ourSequence9 = null;
+  sequence<(float or DOMString)> ourSequence10;
 };
 
 dictionary DictForConstructor {
   Dict dict;
   DictContainingDict dict2;
   sequence<Dict> seq1;
   sequence<sequence<Dict>>? seq2;
   sequence<sequence<Dict>?> seq3;
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -428,16 +428,18 @@ interface TestExampleInterface {
   void passDictContainingDict(optional DictContainingDict arg);
   void passDictContainingSequence(optional DictContainingSequence arg);
   //UNSUPPORTED DictContainingSequence receiveDictContainingSequence();
 
   // EnforceRange/Clamp tests
   void dontEnforceRangeOrClamp(byte arg);
   void doEnforceRange([EnforceRange] byte arg);
   void doClamp([Clamp] byte arg);
+  [EnforceRange] attribute byte enforcedByte;
+  [Clamp] attribute byte clampedByte;
 
   // Typedefs
   const myLong myLongConstant = 5;
   void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
   AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
 
   // Static methods and attributes
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -453,16 +453,18 @@ interface TestJSImplInterface {
   void passDictContainingSequence(optional DictContainingSequence arg);
   // FIXME: Bug 863949 no dictionary return values
   //   DictContainingSequence receiveDictContainingSequence();
 
   // EnforceRange/Clamp tests
   void dontEnforceRangeOrClamp(byte arg);
   void doEnforceRange([EnforceRange] byte arg);
   void doClamp([Clamp] byte arg);
+  [EnforceRange] attribute byte enforcedByte;
+  [Clamp] attribute byte clampedByte;
 
   // Typedefs
   const myLong myLongConstant = 5;
   void exerciseTypedefInterfaces1(AnotherNameForTestJSImplInterface arg);
   AnotherNameForTestJSImplInterface exerciseTypedefInterfaces2(NullableTestJSImplInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestJSImplInterface arg);
 
   // Static methods and attributes
--- a/dom/workers/EventTarget.cpp
+++ b/dom/workers/EventTarget.cpp
@@ -1,18 +1,20 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "EventTarget.h"
+#include "mozilla/dom/EventListenerBinding.h"
 
 USING_WORKERS_NAMESPACE
 using mozilla::ErrorResult;
-using namespace mozilla::dom;
+using mozilla::dom::EventListener;
+using mozilla::dom::Nullable;
 
 void
 EventTarget::_trace(JSTracer* aTrc)
 {
   mListenerManager._trace(aTrc);
   DOMBindingBase::_trace(aTrc);
 }
 
@@ -53,17 +55,17 @@ EventTarget::SetEventListener(const nsAS
   }
 
   mListenerManager.SetEventListener(cx, INTERNED_STRING_TO_JSID(cx, type),
                                     aListener, aRv);
 }
 
 void
 EventTarget::AddEventListener(const nsAString& aType,
-                              JS::Handle<JSObject*> aListener,
+                              EventListener* aListener,
                               bool aCapturing, Nullable<bool> aWantsUntrusted,
                               ErrorResult& aRv)
 {
   if (!aListener) {
     return;
   }
 
   JSContext* cx = GetJSContext();
@@ -72,33 +74,33 @@ EventTarget::AddEventListener(const nsAS
     JS_NewUCStringCopyN(cx, aType.BeginReading(), aType.Length());
   if (!type || !(type = JS_InternJSString(cx, type))) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   bool wantsUntrusted = !aWantsUntrusted.IsNull() && aWantsUntrusted.Value();
   mListenerManager.AddEventListener(cx, INTERNED_STRING_TO_JSID(cx, type),
-                                    aListener, aCapturing, wantsUntrusted,
-                                    aRv);
+                                    aListener->Callback(), aCapturing,
+                                    wantsUntrusted, aRv);
 }
 
 void
 EventTarget::RemoveEventListener(const nsAString& aType,
-                                 JS::Handle<JSObject*> aListener,
+                                 EventListener* aListener,
                                  bool aCapturing, ErrorResult& aRv)
 {
   if (!aListener) {
     return;
   }
 
   JSContext* cx = GetJSContext();
 
   JSString* type =
     JS_NewUCStringCopyN(cx, aType.BeginReading(), aType.Length());
   if (!type || !(type = JS_InternJSString(cx, type))) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   mListenerManager.RemoveEventListener(cx, INTERNED_STRING_TO_JSID(cx, type),
-                                       aListener, aCapturing);
+                                       aListener->Callback(), aCapturing);
 }
--- a/dom/workers/EventTarget.h
+++ b/dom/workers/EventTarget.h
@@ -9,16 +9,21 @@
 #include "mozilla/dom/workers/bindings/DOMBindingBase.h"
 
 // I hate having to export this...
 #include "mozilla/dom/workers/bindings/EventListenerManager.h"
 
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/ErrorResult.h"
 
+namespace mozilla {
+namespace dom {
+class EventListener;
+} // namespace mozilla
+} // namespace dom
 
 BEGIN_WORKERS_NAMESPACE
 
 class EventTarget : public DOMBindingBase
 {
   EventListenerManager mListenerManager;
 
 protected:
@@ -32,22 +37,22 @@ protected:
 public:
   virtual void
   _trace(JSTracer* aTrc) MOZ_OVERRIDE;
 
   virtual void
   _finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
 
   void
-  AddEventListener(const nsAString& aType, JS::Handle<JSObject*> aListener,
+  AddEventListener(const nsAString& aType, EventListener* aListener,
                    bool aCapture, Nullable<bool> aWantsUntrusted,
                    ErrorResult& aRv);
 
   void
-  RemoveEventListener(const nsAString& aType, JS::Handle<JSObject*> aListener,
+  RemoveEventListener(const nsAString& aType, EventListener* aListener,
                       bool aCapture, ErrorResult& aRv);
 
   bool
   DispatchEvent(JS::Handle<JSObject*> aEvent, ErrorResult& aRv) const
   {
     return mListenerManager.DispatchEvent(GetJSContext(), *this, aEvent, aRv);
   }
 
--- a/dom/workers/Events.cpp
+++ b/dom/workers/Events.cpp
@@ -2,47 +2,42 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Util.h"
 
 #include "Events.h"
+#include "mozilla/dom/BindingUtils.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "nsTraceRefcnt.h"
 
 #include "WorkerInlines.h"
 #include "WorkerPrivate.h"
 
-#define PROPERTY_FLAGS \
-  (JSPROP_ENUMERATE | JSPROP_SHARED)
-
 #define FUNCTION_FLAGS \
   JSPROP_ENUMERATE
 
-#define CONSTANT_FLAGS \
-  JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY
-
 using namespace mozilla;
 USING_WORKERS_NAMESPACE
 
 namespace {
 
 class Event : public PrivatizableBase
 {
   static JSClass sClass;
   static JSClass sMainRuntimeClass;
 
   static const JSPropertySpec sProperties[];
   static const JSFunctionSpec sFunctions[];
-  static const JSPropertySpec sStaticProperties[];
+  static const dom::ConstantSpec sStaticConstants[];
 
 protected:
   bool mStopPropagationCalled;
   bool mStopImmediatePropagationCalled;
 
 public:
   static bool
   IsThisClass(JSClass* aClass)
@@ -72,19 +67,28 @@ public:
           parentProto = JSVAL_TO_OBJECT(protoVal);
         }
       }
     }
 
     JSClass* clasp = parentProto ? &sMainRuntimeClass : &sClass;
 
     JS::Rooted<JSObject*> proto(aCx, JS_InitClass(aCx, aObj, parentProto, clasp, Construct, 0,
-                                                  sProperties, sFunctions, sStaticProperties,
-                                                  nullptr));
-    if (proto && !JS_DefineProperties(aCx, proto, sStaticProperties)) {
+                                                  sProperties, sFunctions, nullptr, nullptr));
+    if (!proto) {
+      return NULL;
+    }
+
+    JS::Rooted<JSObject*> ctor(aCx, JS_GetConstructor(aCx, proto));
+    if (!ctor) {
+      return NULL;
+    }
+
+    if (!dom::DefineConstants(aCx, ctor, sStaticConstants) ||
+        !dom::DefineConstants(aCx, proto, sStaticConstants)) {
       return NULL;
     }
 
     return proto;
   }
 
   static JSObject*
   Create(JSContext* aCx, JS::Handle<JSObject*> aParent, JS::Handle<JSString*> aType,
@@ -140,17 +144,17 @@ protected:
     MOZ_COUNT_CTOR(mozilla::dom::workers::Event);
   }
 
   virtual ~Event()
   {
     MOZ_COUNT_DTOR(mozilla::dom::workers::Event);
   }
 
-  enum {
+  enum EventPhase {
     CAPTURING_PHASE = 1,
     AT_TARGET = 2,
     BUBBLING_PHASE = 3
   };
 
   enum SLOT {
     SLOT_type = 0,
     SLOT_target,
@@ -215,43 +219,43 @@ private:
   static void
   Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(IsThisClass(JS_GetClass(aObj)));
     delete GetJSPrivateSafeish<Event>(aObj);
   }
 
   static bool
-  GetProperty(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-              JS::MutableHandle<JS::Value> aVp)
+  IsEvent(JS::Handle<JS::Value> v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-
-    int32_t slot = JSID_TO_INT(aIdval);
+    return v.isObject() && GetPrivate(&v.toObject()) != nullptr;
+  }
 
-    const char* name = sProperties[slot - SLOT_FIRST].name;
-    if (!GetInstancePrivate(aCx, aObj, name)) {
-      return false;
-    }
-
-    aVp.set(JS_GetReservedSlot(aObj, slot));
+  template<SLOT Slot>
+  static bool
+  GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    aArgs.rval().set(JS_GetReservedSlot(&aArgs.thisv().toObject(), Slot));
     return true;
   }
 
-  static bool
-  GetConstant(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> idval,
-              JS::MutableHandle<JS::Value> aVp)
+  // This struct (versus just templating the method directly) is needed only for
+  // gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
+  // GetProperty<Slot> and friends in the JSPropertySpec[] below.
+  template<SLOT Slot>
+  struct Property
   {
-    JS_ASSERT(JSID_IS_INT(idval));
-    JS_ASSERT(JSID_TO_INT(idval) >= CAPTURING_PHASE &&
-              JSID_TO_INT(idval) <= BUBBLING_PHASE);
-
-    aVp.set(INT_TO_JSVAL(JSID_TO_INT(idval)));
-    return true;
-  }
+    static bool
+    Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+    {
+      static_assert(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
+      JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+      return JS::CallNonGenericMethod<IsEvent, GetPropertyImpl<Slot> >(aCx, args);
+    }
+  };
 
   static bool
   StopPropagation(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
     if (!obj) {
       return false;
     }
@@ -338,52 +342,50 @@ private:
   };
 
 DECL_EVENT_CLASS(Event::sClass, "WorkerEvent")
 DECL_EVENT_CLASS(Event::sMainRuntimeClass, "WorkerEvent")
 
 #undef DECL_EVENT_CLASS
 
 const JSPropertySpec Event::sProperties[] = {
-  { "type", SLOT_type, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "target", SLOT_target, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "currentTarget", SLOT_currentTarget, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "eventPhase", SLOT_eventPhase, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "bubbles", SLOT_bubbles, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "cancelable", SLOT_cancelable, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "timeStamp", SLOT_timeStamp, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "defaultPrevented", SLOT_defaultPrevented, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "isTrusted", SLOT_isTrusted, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("type", Property<SLOT_type>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("target", Property<SLOT_target>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("currentTarget", Property<SLOT_currentTarget>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("eventPhase", Property<SLOT_eventPhase>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("bubbles", Property<SLOT_bubbles>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("cancelable", Property<SLOT_cancelable>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("timeStamp", Property<SLOT_timeStamp>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("defaultPrevented", Property<SLOT_defaultPrevented>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("isTrusted", Property<SLOT_isTrusted>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 const JSFunctionSpec Event::sFunctions[] = {
   JS_FN("stopPropagation", StopPropagation, 0, FUNCTION_FLAGS),
   JS_FN("preventDefault", PreventDefault, 0, FUNCTION_FLAGS),
   JS_FN("initEvent", InitEvent, 3, FUNCTION_FLAGS),
   JS_FN("stopImmediatePropagation", StopImmediatePropagation, 0, FUNCTION_FLAGS),
   JS_FS_END
 };
 
-const JSPropertySpec Event::sStaticProperties[] = {
-  { "CAPTURING_PHASE", CAPTURING_PHASE, CONSTANT_FLAGS,
-    JSOP_WRAPPER(GetConstant), JSOP_NULLWRAPPER },
-  { "AT_TARGET", AT_TARGET, CONSTANT_FLAGS, JSOP_WRAPPER(GetConstant), JSOP_NULLWRAPPER },
-  { "BUBBLING_PHASE", BUBBLING_PHASE, CONSTANT_FLAGS,
-    JSOP_WRAPPER(GetConstant), JSOP_NULLWRAPPER },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+const dom::ConstantSpec Event::sStaticConstants[] = {
+  { "CAPTURING_PHASE", JS::Int32Value(CAPTURING_PHASE) },
+  { "AT_TARGET", JS::Int32Value(AT_TARGET) },
+  { "BUBBLING_PHASE", JS::Int32Value(BUBBLING_PHASE) },
+  { nullptr, JS::UndefinedValue() }
 };
 
 class MessageEvent : public Event
 {
   static JSClass sClass;
   static JSClass sMainRuntimeClass;
 
   static const JSPropertySpec sProperties[];
@@ -502,56 +504,75 @@ private:
   Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(IsThisClass(JS_GetClass(aObj)));
     MessageEvent* priv = GetJSPrivateSafeish<MessageEvent>(aObj);
     delete priv;
   }
 
   static bool
-  GetProperty(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-              JS::MutableHandle<JS::Value> aVp)
+  IsMessageEvent(JS::Handle<JS::Value> v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-
-    int32_t slot = JSID_TO_INT(aIdval);
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return IsThisClass(JS_GetClass(obj)) &&
+           GetJSPrivateSafeish<MessageEvent>(obj) != nullptr;
+  }
 
-    JS_ASSERT(slot >= SLOT_data && slot < SLOT_COUNT);
+  template<SLOT Slot>
+  static bool
+  GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
 
-    const char* name = sProperties[slot - SLOT_FIRST].name;
-    MessageEvent* event = GetInstancePrivate(aCx, aObj, name);
-    if (!event) {
-      return false;
-    }
+    const char* name = sProperties[Slot - SLOT_FIRST].name;
+    MessageEvent* event = GetInstancePrivate(aCx, obj, name);
+    MOZ_ASSERT(event);
 
     // Deserialize and save the data value if we can.
-    if (slot == SLOT_data && event->mBuffer.data()) {
+    if (Slot == SLOT_data && event->mBuffer.data()) {
       JSAutoStructuredCloneBuffer buffer;
       buffer.swap(event->mBuffer);
 
       // Release reference to objects that were AddRef'd for
       // cloning into worker when array goes out of scope.
       nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
       clonedObjects.SwapElements(event->mClonedObjects);
 
       JS::Rooted<JS::Value> data(aCx);
       if (!buffer.read(aCx, data.address(),
                        WorkerStructuredCloneCallbacks(event->mMainRuntime))) {
         return false;
       }
-      JS_SetReservedSlot(aObj, slot, data);
+      JS_SetReservedSlot(obj, Slot, data);
 
-      aVp.set(data);
+      aArgs.rval().set(data);
       return true;
     }
 
-    aVp.set(JS_GetReservedSlot(aObj, slot));
+    aArgs.rval().set(JS_GetReservedSlot(obj, Slot));
     return true;
   }
 
+  // This struct (versus just templating the method directly) is needed only for
+  // gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
+  // GetProperty<Slot> and friends in the JSPropertySpec[] below.
+  template<SLOT Slot>
+  struct Property
+  {
+    static bool
+    Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+    {
+      static_assert(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
+      JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+      return JS::CallNonGenericMethod<IsMessageEvent, GetPropertyImpl<Slot> >(aCx, args);
+    }
+  };
+
   static bool
   InitMessageEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
@@ -584,23 +605,23 @@ private:
   };
 
 DECL_MESSAGEEVENT_CLASS(MessageEvent::sClass, "WorkerMessageEvent")
 DECL_MESSAGEEVENT_CLASS(MessageEvent::sMainRuntimeClass, "WorkerMessageEvent")
 
 #undef DECL_MESSAGEEVENT_CLASS
 
 const JSPropertySpec MessageEvent::sProperties[] = {
-  { "data", SLOT_data, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "origin", SLOT_origin, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "source", SLOT_source, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("data", Property<SLOT_data>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("origin", Property<SLOT_origin>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("source", Property<SLOT_source>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 const JSFunctionSpec MessageEvent::sFunctions[] = {
   JS_FN("initMessageEvent", InitMessageEvent, 6, FUNCTION_FLAGS),
   JS_FS_END
 };
 
 class ErrorEvent : public Event
@@ -710,35 +731,48 @@ private:
   static void
   Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(IsThisClass(JS_GetClass(aObj)));
     delete GetJSPrivateSafeish<ErrorEvent>(aObj);
   }
 
   static bool
-  GetProperty(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-              JS::MutableHandle<JS::Value> aVp)
+  IsErrorEvent(JS::Handle<JS::Value> v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-
-    int32_t slot = JSID_TO_INT(aIdval);
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return IsThisClass(JS_GetClass(obj)) &&
+           GetJSPrivateSafeish<ErrorEvent>(obj) != nullptr;
+  }
 
-    JS_ASSERT(slot >= SLOT_message && slot < SLOT_COUNT);
-
-    const char* name = sProperties[slot - SLOT_FIRST].name;
-    ErrorEvent* event = GetInstancePrivate(aCx, aObj, name);
-    if (!event) {
-      return false;
-    }
-
-    aVp.set(JS_GetReservedSlot(aObj, slot));
+  template<SLOT Slot>
+  static bool
+  GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    aArgs.rval().set(JS_GetReservedSlot(&aArgs.thisv().toObject(), Slot));
     return true;
   }
 
+  // This struct (versus just templating the method directly) is needed only for
+  // gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
+  // GetProperty<Slot> and friends in the JSPropertySpec[] below.
+  template<SLOT Slot>
+  struct Property
+  {
+    static bool
+    Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+    {
+      static_assert(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
+      JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+      return JS::CallNonGenericMethod<IsErrorEvent, GetPropertyImpl<Slot> >(aCx, args);
+    }
+  };
+
   static bool
   InitErrorEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
@@ -771,23 +805,23 @@ private:
   };
 
 DECL_ERROREVENT_CLASS(ErrorEvent::sClass, "WorkerErrorEvent")
 DECL_ERROREVENT_CLASS(ErrorEvent::sMainRuntimeClass, "WorkerErrorEvent")
 
 #undef DECL_ERROREVENT_CLASS
 
 const JSPropertySpec ErrorEvent::sProperties[] = {
-  { "message", SLOT_message, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "filename", SLOT_filename, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "lineno", SLOT_lineno, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("message", Property<SLOT_message>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("filename", Property<SLOT_filename>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("lineno", Property<SLOT_lineno>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 const JSFunctionSpec ErrorEvent::sFunctions[] = {
   JS_FN("initErrorEvent", InitErrorEvent, 6, FUNCTION_FLAGS),
   JS_FS_END
 };
 
 class ProgressEvent : public Event
@@ -890,51 +924,64 @@ private:
   static void
   Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     delete GetJSPrivateSafeish<ProgressEvent>(aObj);
   }
 
   static bool
-  GetProperty(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-              JS::MutableHandle<JS::Value> aVp)
+  IsProgressEvent(JS::Handle<JS::Value> v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-
-    int32_t slot = JSID_TO_INT(aIdval);
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return JS_GetClass(obj) == &sClass &&
+           GetJSPrivateSafeish<ProgressEvent>(obj) != nullptr;
+  }
 
-    JS_ASSERT(slot >= SLOT_lengthComputable && slot < SLOT_COUNT);
-
-    const char* name = sProperties[slot - SLOT_FIRST].name;
-    ProgressEvent* event = GetInstancePrivate(aCx, aObj, name);
-    if (!event) {
-      return false;
-    }
-
-    aVp.set(JS_GetReservedSlot(aObj, slot));
+  template<SLOT Slot>
+  static bool
+  GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    aArgs.rval().set(JS_GetReservedSlot(&aArgs.thisv().toObject(), Slot));
     return true;
   }
+
+  // This struct (versus just templating the method directly) is needed only for
+  // gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
+  // GetProperty<Slot> and friends in the JSPropertySpec[] below.
+  template<SLOT Slot>
+  struct Property
+  {
+    static bool
+    Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+    {
+      static_assert(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
+      JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+      return JS::CallNonGenericMethod<IsProgressEvent, GetPropertyImpl<Slot> >(aCx, args);
+    }
+  };
 };
 
 JSClass ProgressEvent::sClass = {
   "WorkerProgressEvent",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
   JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize
 };
 
 const JSPropertySpec ProgressEvent::sProperties[] = {
-  { "lengthComputable", SLOT_lengthComputable, PROPERTY_FLAGS,
-    JSOP_WRAPPER(GetProperty), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "loaded", SLOT_loaded, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "total", SLOT_total, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("lengthComputable", Property<SLOT_lengthComputable>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("loaded", Property<SLOT_loaded>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("total", Property<SLOT_total>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 Event*
 Event::GetPrivate(JSObject* aObj)
 {
   if (aObj) {
     JSClass* classPtr = JS_GetClass(aObj);
     if (IsThisClass(classPtr) ||
--- a/dom/workers/File.cpp
+++ b/dom/workers/File.cpp
@@ -15,19 +15,16 @@
 #include "nsCOMPtr.h"
 #include "nsJSUtils.h"
 #include "nsStringGlue.h"
 
 #include "mozilla/dom/Exceptions.h"
 #include "WorkerInlines.h"
 #include "WorkerPrivate.h"
 
-#define PROPERTY_FLAGS \
-  (JSPROP_ENUMERATE | JSPROP_SHARED)
-
 USING_WORKERS_NAMESPACE
 using mozilla::dom::Throw;
 
 namespace {
 
 class Blob
 {
   // Blob should never be instantiated.
@@ -106,56 +103,70 @@ private:
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
 
     nsIDOMBlob* blob = GetPrivate(aObj);
     NS_IF_RELEASE(blob);
   }
 
   static bool
-  GetSize(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-          JS::MutableHandle<JS::Value> aVp)
+  IsBlob(JS::Handle<JS::Value> v)
   {
-    nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "size");
-    if (!blob) {
-      return false;
-    }
+    return v.isObject() && GetPrivate(&v.toObject()) != nullptr;
+  }
+
+  static bool
+  GetSizeImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "size");
+    MOZ_ASSERT(blob);
 
     uint64_t size;
     if (NS_FAILED(blob->GetSize(&size))) {
       return Throw(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR);
     }
 
-    aVp.set(JS_NumberValue(double(size)));
-
+    aArgs.rval().setNumber(double(size));
     return true;
   }
 
   static bool
-  GetType(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-          JS::MutableHandle<JS::Value> aVp)
+  GetSize(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "type");
-    if (!blob) {
-      return false;
-    }
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsBlob, GetSizeImpl>(aCx, args);
+  }
+
+  static bool
+  GetTypeImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "type");
+    MOZ_ASSERT(blob);
 
     nsString type;
     if (NS_FAILED(blob->GetType(type))) {
       return Throw(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR);
     }
 
     JSString* jsType = JS_NewUCStringCopyN(aCx, type.get(), type.Length());
     if (!jsType) {
       return false;
     }
 
-    aVp.set(STRING_TO_JSVAL(jsType));
+    aArgs.rval().setString(jsType);
+    return true;
+  }
 
-    return true;
+  static bool
+  GetType(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsBlob, GetTypeImpl>(aCx, args);
   }
 
   static bool
   Slice(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
@@ -200,19 +211,19 @@ private:
 JSClass Blob::sClass = {
   "Blob",
   JSCLASS_HAS_PRIVATE,
   JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize
 };
 
 const JSPropertySpec Blob::sProperties[] = {
-  { "size", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetSize), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "type", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetType), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("size", GetSize, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS("type", GetType, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 const JSFunctionSpec Blob::sFunctions[] = {
   JS_FN("slice", Slice, 1, JSPROP_ENUMERATE),
   JS_FS_END
 };
 
 class File : public Blob
@@ -294,123 +305,146 @@ private:
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
 
     nsIDOMFile* file = GetPrivate(aObj);
     NS_IF_RELEASE(file);
   }
 
   static bool
-  GetMozFullPath(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-                 JS::MutableHandle<JS::Value> aVp)
+  IsFile(JS::Handle<JS::Value> v)
   {
-    nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "mozFullPath");
-    if (!file) {
-      return false;
-    }
+    return v.isObject() && GetPrivate(&v.toObject()) != nullptr;
+  }
+
+  static bool
+  GetMozFullPathImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
+    nsIDOMFile* file = GetInstancePrivate(aCx, obj, "mozFullPath");
+    MOZ_ASSERT(file);
 
     nsString fullPath;
 
     if (GetWorkerPrivateFromContext(aCx)->UsesSystemPrincipal() &&
         NS_FAILED(file->GetMozFullPathInternal(fullPath))) {
       return Throw(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR);
     }
 
     JSString* jsFullPath = JS_NewUCStringCopyN(aCx, fullPath.get(),
                                                fullPath.Length());
     if (!jsFullPath) {
       return false;
     }
 
-    aVp.set(STRING_TO_JSVAL(jsFullPath));
+    aArgs.rval().setString(jsFullPath);
     return true;
   }
 
   static bool
-  GetName(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-          JS::MutableHandle<JS::Value> aVp)
+  GetMozFullPath(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "name");
-    if (!file) {
-      return false;
-    }
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsFile, GetMozFullPathImpl>(aCx, args);
+  }
+
+  static bool
+  GetNameImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
+    nsIDOMFile* file = GetInstancePrivate(aCx, obj, "name");
+    MOZ_ASSERT(file);
 
     nsString name;
     if (NS_FAILED(file->GetName(name))) {
       name.Truncate();
     }
 
     JSString* jsName = JS_NewUCStringCopyN(aCx, name.get(), name.Length());
     if (!jsName) {
       return false;
     }
 
-    aVp.set(STRING_TO_JSVAL(jsName));
+    aArgs.rval().setString(jsName);
     return true;
   }
 
   static bool
-  GetPath(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-          JS::MutableHandle<JS::Value> aVp)
+  GetName(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "path");
-    if (!file) {
-      return false;
-    }
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsFile, GetNameImpl>(aCx, args);
+  }
+
+  static bool
+  GetPathImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
+    nsIDOMFile* file = GetInstancePrivate(aCx, obj, "path");
+    MOZ_ASSERT(file);
 
     nsString path;
     if (NS_FAILED(file->GetPath(path))) {
       path.Truncate();
     }
 
     JSString* jsPath = JS_NewUCStringCopyN(aCx, path.get(), path.Length());
     if (!jsPath) {
       return false;
     }
 
-    aVp.set(STRING_TO_JSVAL(jsPath));
+    aArgs.rval().setString(jsPath);
     return true;
   }
 
   static bool
-  GetLastModifiedDate(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-                      JS::MutableHandle<JS::Value> aVp)
+  GetPath(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "lastModifiedDate");
-    if (!file) {
-      return false;
-    }
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsFile, GetPathImpl>(aCx, args);
+  }
+
+  static bool
+  GetLastModifiedDateImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
+    nsIDOMFile* file = GetInstancePrivate(aCx, obj, "lastModifiedDate");
+    MOZ_ASSERT(file);
 
     JS::Rooted<JS::Value> value(aCx);
     if (NS_FAILED(file->GetLastModifiedDate(aCx, value.address()))) {
       return false;
     }
 
-    aVp.set(value);
+    aArgs.rval().set(value);
     return true;
   }
+
+  static bool
+  GetLastModifiedDate(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsFile, GetLastModifiedDateImpl>(aCx, args);
+  }
 };
 
 JSClass File::sClass = {
   "File",
   JSCLASS_HAS_PRIVATE,
   JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize
 };
 
 const JSPropertySpec File::sProperties[] = {
-  { "name", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetName),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "path", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetPath),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "lastModifiedDate", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetLastModifiedDate),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "mozFullPath", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetMozFullPath),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("name", GetName, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS("path", GetPath, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS("lastModifiedDate", GetLastModifiedDate, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("mozFullPath", GetMozFullPath, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 nsIDOMBlob*
 Blob::GetPrivate(JSObject* aObj)
 {
   if (aObj) {
     JSClass* classPtr = JS_GetClass(aObj);
     if (classPtr == &sClass || classPtr == File::Class()) {
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1506,16 +1506,19 @@ RuntimeService::Init()
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
 
   nsresult rv =
     obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   mObserved = true;
 
   if (NS_FAILED(obs->AddObserver(this, GC_REQUEST_OBSERVER_TOPIC, false))) {
     NS_WARNING("Failed to register for GC request notifications!");
   }
 
   if (NS_FAILED(obs->AddObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC,
                                  false))) {
@@ -1593,67 +1596,85 @@ RuntimeService::Init()
   rv = InitOSFileConstants();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   return NS_OK;
 }
 
-// This spins the event loop until all workers are finished and their threads
-// have been joined.
 void
-RuntimeService::Cleanup()
+RuntimeService::Shutdown()
 {
   AssertIsOnMainThread();
 
+  MOZ_ASSERT(!mShuttingDown);
+  // That's it, no more workers.
+  mShuttingDown = true;
+
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_WARN_IF_FALSE(obs, "Failed to get observer service?!");
 
   // Tell anyone that cares that they're about to lose worker support.
   if (obs && NS_FAILED(obs->NotifyObservers(nullptr, WORKERS_SHUTDOWN_TOPIC,
                                             nullptr))) {
     NS_WARNING("NotifyObservers failed!");
   }
 
-  // That's it, no more workers.
-  mShuttingDown = true;
+  {
+    MutexAutoLock lock(mMutex);
+
+    nsAutoTArray<WorkerPrivate*, 100> workers;
+    mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers);
+
+    if (!workers.IsEmpty()) {
+
+      // Cancel all top-level workers.
+      {
+        MutexAutoUnlock unlock(mMutex);
+
+        AutoSafeJSContext cx;
+        JSAutoRequest ar(cx);
+
+        for (uint32_t index = 0; index < workers.Length(); index++) {
+          if (!workers[index]->Kill(cx)) {
+            NS_WARNING("Failed to cancel worker!");
+          }
+        }
+      }
+    }
+  }
+}
+
+// This spins the event loop until all workers are finished and their threads
+// have been joined.
+void
+RuntimeService::Cleanup()
+{
+  AssertIsOnMainThread();
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  NS_WARN_IF_FALSE(obs, "Failed to get observer service?!");
 
   if (mIdleThreadTimer) {
     if (NS_FAILED(mIdleThreadTimer->Cancel())) {
       NS_WARNING("Failed to cancel idle timer!");
     }
     mIdleThreadTimer = nullptr;
   }
 
   {
     MutexAutoLock lock(mMutex);
 
     nsAutoTArray<WorkerPrivate*, 100> workers;
     mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers);
 
     if (!workers.IsEmpty()) {
-      nsIThread* currentThread;
-
-      // Cancel all top-level workers.
-      {
-        MutexAutoUnlock unlock(mMutex);
-
-        currentThread = NS_GetCurrentThread();
-        NS_ASSERTION(currentThread, "This should never be null!");
-
-        AutoSafeJSContext cx;
-        JSAutoRequest ar(cx);
-
-        for (uint32_t index = 0; index < workers.Length(); index++) {
-          if (!workers[index]->Kill(cx)) {
-            NS_WARNING("Failed to cancel worker!");
-          }
-        }
-      }
+      nsIThread* currentThread = NS_GetCurrentThread();
+      NS_ASSERTION(currentThread, "This should never be null!");
 
       // Shut down any idle threads.
       if (!mIdleThreadArray.IsEmpty()) {
         nsAutoTArray<nsCOMPtr<nsIThread>, 20> idleThreads;
 
         uint32_t idleThreadCount = mIdleThreadArray.Length();
         idleThreads.SetLength(idleThreadCount);
 
@@ -1729,19 +1750,19 @@ RuntimeService::Cleanup()
         NS_WARNING("Failed to unregister for GC request notifications!");
       }
 
       if (NS_FAILED(obs->RemoveObserver(this,
                                         MEMORY_PRESSURE_OBSERVER_TOPIC))) {
         NS_WARNING("Failed to unregister for memory pressure notifications!");
       }
 
-      nsresult rv =
-        obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
-      mObserved = NS_FAILED(rv);
+      obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
+      obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+      mObserved = false;
     }
   }
 
   CleanupOSFileConstants();
   nsLayoutStatics::Release();
 }
 
 // static
@@ -1930,16 +1951,20 @@ NS_IMPL_ISUPPORTS1(RuntimeService, nsIOb
 
 // nsIObserver
 NS_IMETHODIMP
 RuntimeService::Observe(nsISupports* aSubject, const char* aTopic,
                         const PRUnichar* aData)
 {
   AssertIsOnMainThread();
 
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    Shutdown();
+    return NS_OK;
+  }
   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
     Cleanup();
     return NS_OK;
   }
   if (!strcmp(aTopic, GC_REQUEST_OBSERVER_TOPIC)) {
     GarbageCollectAllWorkers(false);
     return NS_OK;
   }
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -204,16 +204,19 @@ public:
 private:
   RuntimeService();
   ~RuntimeService();
 
   nsresult
   Init();
 
   void
+  Shutdown();
+
+  void
   Cleanup();
 
   static PLDHashOperator
   AddAllTopLevelWorkersToArray(const nsACString& aKey,
                                WorkerDomainInfo* aData,
                                void* aUserArg);
 
   void
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -3,25 +3,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Worker.h"
 
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/BindingUtils.h"
 
+#include "jsapi.h"
 #include "EventTarget.h"
 #include "RuntimeService.h"
 #include "WorkerPrivate.h"
 
 #include "WorkerInlines.h"
 
-#define PROPERTY_FLAGS \
-  (JSPROP_ENUMERATE | JSPROP_SHARED)
-
 #define FUNCTION_FLAGS \
   JSPROP_ENUMERATE
 
 USING_WORKERS_NAMESPACE
 
 using namespace mozilla::dom;
 using mozilla::ErrorResult;
 
@@ -29,26 +27,16 @@ namespace {
 
 class Worker
 {
   static DOMJSClass sClass;
   static DOMIfaceAndProtoJSClass sProtoClass;
   static const JSPropertySpec sProperties[];
   static const JSFunctionSpec sFunctions[];
 
-  enum
-  {
-    STRING_onerror = 0,
-    STRING_onmessage,
-
-    STRING_COUNT
-  };
-
-  static const char* const sEventStrings[STRING_COUNT];
-
 protected:
   enum {
     // The constructor function holds a WorkerPrivate* in its first reserved
     // slot.
     CONSTRUCTOR_SLOT_PARENT = 0
   };
 
 public:
@@ -171,71 +159,121 @@ protected:
 private:
   // No instance of this class should ever be created so these are explicitly
   // left without an implementation to prevent linking in case someone tries to
   // make one.
   Worker();
   ~Worker();
 
   static bool
-  GetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-                   JS::MutableHandle<JS::Value> aVp)
+  IsWorker(JS::Handle<JS::Value> v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+    return v.isObject() && ClassIsWorker(JS_GetClass(&v.toObject()));
+  }
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
-    if (!worker) {
-      return !JS_IsExceptionPending(aCx);
-    }
+  static bool
+  GetEventListener(JSContext* aCx, const JS::CallArgs aArgs,
+                   const nsAString &aNameStr)
+  {
+    WorkerPrivate* worker =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
+                         NS_ConvertUTF16toUTF8(aNameStr).get());
+    MOZ_ASSERT(worker);
 
-    NS_ConvertASCIItoUTF16 nameStr(name + 2);
     ErrorResult rv;
-    JS::Rooted<JSObject*> listener(aCx, worker->GetEventListener(nameStr, rv));
+    JS::Rooted<JSObject*> listener(aCx, worker->GetEventListener(Substring(aNameStr, 2), rv));
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get listener!");
+      return false;
     }
 
-    aVp.set(listener ? OBJECT_TO_JSVAL(listener) : JSVAL_NULL);
+    aArgs.rval().setObjectOrNull(listener);
     return true;
   }
 
   static bool
-  SetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-                   bool aStrict, JS::MutableHandle<JS::Value> aVp)
+  GetOnerrorImpl(JSContext* aCx, JS::CallArgs aArgs)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+    return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onerror"));
+  }
+
+  static bool
+  GetOnerror(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorker, GetOnerrorImpl>(aCx, args);
+  }
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
-    if (!worker) {
-      return !JS_IsExceptionPending(aCx);
-    }
+  static bool
+  GetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
+  }
+
+  static bool
+  GetOnmessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorker, GetOnmessageImpl>(aCx, args);
+  }
+
+  static bool
+  SetEventListener(JSContext* aCx, JS::CallArgs aArgs,
+                   const nsAString& aNameStr)
+  {
+    WorkerPrivate* worker =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
+                         NS_ConvertUTF16toUTF8(aNameStr).get());
+    MOZ_ASSERT(worker);
 
     JS::Rooted<JSObject*> listener(aCx);
-    if (!JS_ValueToObject(aCx, aVp, listener.address())) {
+    if (!JS_ValueToObject(aCx, aArgs.get(0), listener.address())) {
       return false;
     }
 
-    NS_ConvertASCIItoUTF16 nameStr(name + 2);
     ErrorResult rv;
-    worker->SetEventListener(nameStr, listener, rv);
+    worker->SetEventListener(Substring(aNameStr, 2), listener, rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to set listener!");
       return false;
     }
 
+    aArgs.rval().setUndefined();
     return true;
   }
 
   static bool
+  SetOnerrorImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    return SetEventListener(aCx, aArgs, NS_LITERAL_STRING("onerror"));
+  }
+
+  static bool
+  SetOnerror(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorker, SetOnerrorImpl>(aCx, args);
+  }
+
+  static bool
+  SetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    return SetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
+  }
+
+  static bool
+  SetOnmessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorker, SetOnmessageImpl>(aCx, args);
+  }
+
+  static bool
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     return ConstructInternal(aCx, aArgc, aVp, false, Class());
   }
 
   static void
   Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
@@ -350,34 +388,27 @@ DOMIfaceAndProtoJSClass Worker::sProtoCl
   eInterfacePrototype,
   &sWorkerNativePropertyHooks,
   "[object Worker]",
   prototypes::id::_ID_Count,
   0
 };
 
 const JSPropertySpec Worker::sProperties[] = {
-  { sEventStrings[STRING_onerror], STRING_onerror, PROPERTY_FLAGS,
-    JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
-  { sEventStrings[STRING_onmessage], STRING_onmessage, PROPERTY_FLAGS,
-    JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("onerror", GetOnerror, SetOnerror, JSPROP_ENUMERATE),
+  JS_PSGS("onmessage", GetOnmessage, SetOnmessage, JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 const JSFunctionSpec Worker::sFunctions[] = {
   JS_FN("terminate", Terminate, 0, FUNCTION_FLAGS),
   JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),
   JS_FS_END
 };
 
-const char* const Worker::sEventStrings[STRING_COUNT] = {
-  "onerror",
-  "onmessage"
-};
-
 class ChromeWorker : public Worker
 {
   static DOMJSClass sClass;
   static DOMIfaceAndProtoJSClass sProtoClass;
 
 public:
   static JSClass*
   Class()
@@ -517,17 +548,17 @@ DOMIfaceAndProtoJSClass ChromeWorker::sP
   0
 };
 
 WorkerPrivate*
 Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
                            const char* aFunctionName)
 {
   JSClass* classPtr = JS_GetClass(aObj);
-  if (classPtr == Class() || classPtr == ChromeWorker::Class()) {
+  if (ClassIsWorker(classPtr)) {
     return UnwrapDOMObject<WorkerPrivate>(aObj);
   }
 
   JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
                        Class()->name, aFunctionName, classPtr->name);
   return NULL;
 }
 
@@ -575,9 +606,16 @@ InitClass(JSContext* aCx, JSObject* aGlo
 } // namespace chromeworker
 
 bool
 ClassIsWorker(JSClass* aClass)
 {
   return Worker::Class() == aClass || ChromeWorker::Class() == aClass;
 }
 
+bool
+GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+{
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
+    return false;
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -43,19 +43,16 @@
 #include "Principal.h"
 #include "ScriptLoader.h"
 #include "Worker.h"
 #include "WorkerPrivate.h"
 #include "XMLHttpRequest.h"
 
 #include "WorkerInlines.h"
 
-#define PROPERTY_FLAGS \
-  (JSPROP_ENUMERATE | JSPROP_SHARED)
-
 #define FUNCTION_FLAGS \
   JSPROP_ENUMERATE
 
 using namespace mozilla;
 using namespace mozilla::dom;
 USING_WORKERS_NAMESPACE
 
 namespace {
@@ -150,123 +147,136 @@ protected:
 
   virtual void
   _finalize(JSFreeOp* aFop) MOZ_OVERRIDE
   {
     EventTarget::_finalize(aFop);
   }
 
 private:
+  static bool IsWorkerGlobalScope(JS::Handle<JS::Value> v);
+
   static bool
-  GetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-                   JS::MutableHandle<JS::Value> aVp)
+  GetOnCloseImpl(JSContext* aCx, JS::CallArgs aArgs)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
-
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+    const char* name = sEventStrings[STRING_onclose];
+    WorkerGlobalScope* scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
     ErrorResult rv;
 
     JSObject* listener =
       scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get event listener!");
       return false;
     }
 
-    aVp.set(listener ? OBJECT_TO_JSVAL(listener) : JSVAL_NULL);
+    aArgs.rval().setObjectOrNull(listener);
     return true;
   }
 
   static bool
-  SetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-                   bool aStrict, JS::MutableHandle<JS::Value> aVp)
+  GetOnClose(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetOnCloseImpl>(aCx, args);
+  }
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+  static bool
+  SetOnCloseImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    const char* name = sEventStrings[STRING_onclose];
+    WorkerGlobalScope* scope =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
-    if (JSVAL_IS_PRIMITIVE(aVp)) {
+    if (aArgs.length() == 0 || !aArgs[0].isObject()) {
       JS_ReportError(aCx, "Not an event listener!");
       return false;
     }
 
     ErrorResult rv;
-    JS::Rooted<JSObject*> listenerObj(aCx, JSVAL_TO_OBJECT(aVp));
+    JS::Rooted<JSObject*> listenerObj(aCx, &aArgs[0].toObject());
     scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2),
                             listenerObj, rv);
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to set event listener!");
       return false;
     }
 
+    aArgs.rval().setUndefined();
     return true;
   }
 
+  static bool
+  SetOnClose(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, SetOnCloseImpl>(aCx, args);
+  }
+
   static WorkerGlobalScope*
   GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
 
   static bool
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
   static bool
-  GetSelf(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-          JS::MutableHandle<JS::Value> aVp)
+  GetSelfImpl(JSContext* aCx, JS::CallArgs aArgs)
   {
-    if (!GetInstancePrivate(aCx, aObj, "self")) {
-      return false;
-    }
-
-    aVp.set(OBJECT_TO_JSVAL(aObj));
+    aArgs.rval().setObject(aArgs.thisv().toObject());
     return true;
   }
 
   static bool
-  GetLocation(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-              JS::MutableHandle<JS::Value> aVp)
+  GetSelf(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetSelfImpl>(aCx, args);
+  }
+
+  static bool
+  GetLocationImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
     WorkerGlobalScope* scope =
-      GetInstancePrivate(aCx, aObj, sProperties[SLOT_location].name);
-    if (!scope) {
-      return false;
-    }
+      GetInstancePrivate(aCx, obj, sProperties[SLOT_location].name);
+    MOZ_ASSERT(scope);
 
-    if (JSVAL_IS_VOID(scope->mSlots[SLOT_location])) {
+    if (scope->mSlots[SLOT_location].isUndefined()) {
       WorkerPrivate::LocationInfo& info = scope->mWorker->GetLocationInfo();
 
       nsRefPtr<WorkerLocation> location =
-        WorkerLocation::Create(aCx, aObj, info);
+        WorkerLocation::Create(aCx, obj, info);
       if (!location) {
         return false;
       }
 
       scope->mSlots[SLOT_location] = OBJECT_TO_JSVAL(location->GetJSObject());
     }
 
-    aVp.set(scope->mSlots[SLOT_location]);
+    aArgs.rval().set(scope->mSlots[SLOT_location]);
     return true;
   }
 
   static bool
+  GetLocation(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetLocationImpl>(aCx, args);
+  }
+
+  static bool
   UnwrapErrorEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ASSERT(aArgc == 1);
     JS_ASSERT((JS_ARGV(aCx, aVp)[0]).isObject());
 
     JSObject* wrapper = &JS_CALLEE(aCx, aVp).toObject();
     JS_ASSERT(JS_ObjectIsFunction(aCx, wrapper));
 
@@ -296,58 +306,58 @@ private:
         !JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL, rval.address())) {
       return false;
     }
 
     return true;
   }
 
   static bool
-  GetOnErrorListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-                     JS::MutableHandle<JS::Value> aVp)
+  GetOnErrorListenerImpl(JSContext* aCx, JS::CallArgs aArgs)
   {
     const char* name = sEventStrings[STRING_onerror];
-    WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+    WorkerGlobalScope* scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
     ErrorResult rv;
 
     JSObject* adaptor =
       scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get event listener!");
       return false;
     }
 
     if (!adaptor) {
-      aVp.setNull();
+      aArgs.rval().setNull();
       return true;
     }
 
-    aVp.set(js::GetFunctionNativeReserved(adaptor, SLOT_wrappedFunction));
-
-    JS_ASSERT(!JSVAL_IS_PRIMITIVE(aVp));
-
+    aArgs.rval().set(js::GetFunctionNativeReserved(adaptor, SLOT_wrappedFunction));
+    MOZ_ASSERT(aArgs.rval().isObject());
     return true;
   }
 
   static bool
-  SetOnErrorListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-                     bool aStrict, JS::MutableHandle<JS::Value> aVp)
+  GetOnErrorListener(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetOnErrorListenerImpl>(aCx, args);
+  }
+
+  static bool
+  SetOnErrorListenerImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
     const char* name = sEventStrings[STRING_onerror];
-    WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+    WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, name);
+    MOZ_ASSERT(scope);
 
-    if (JSVAL_IS_PRIMITIVE(aVp)) {
+    if (aArgs.length() == 0 || !aArgs[0].isObject()) {
       JS_ReportError(aCx, "Not an event listener!");
       return false;
     }
 
     JSFunction* adaptor =
       js::NewFunctionWithReserved(aCx, UnwrapErrorEvent, 1, 0,
                                   JS::CurrentGlobalOrNull(aCx), "unwrap");
     if (!adaptor) {
@@ -355,55 +365,68 @@ private:
     }
 
     JS::Rooted<JSObject*> listener(aCx, JS_GetFunctionObject(adaptor));
     if (!listener) {
       return false;
     }
 
     js::SetFunctionNativeReserved(listener, SLOT_wrappedScope,
-                                  OBJECT_TO_JSVAL(aObj));
-    js::SetFunctionNativeReserved(listener, SLOT_wrappedFunction, aVp);
+                                  JS::ObjectValue(*obj));
+    js::SetFunctionNativeReserved(listener, SLOT_wrappedFunction, aArgs[0]);
 
     ErrorResult rv;
 
     scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2), listener, rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to set event listener!");
       return false;
     }
 
+    aArgs.rval().setUndefined();
     return true;
   }
 
   static bool
-  GetNavigator(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-               JS::MutableHandle<JS::Value> aVp)
+  SetOnErrorListener(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, SetOnErrorListenerImpl>(aCx, args);
+  }
+
+  static bool
+  GetNavigatorImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
     WorkerGlobalScope* scope =
-      GetInstancePrivate(aCx, aObj, sProperties[SLOT_navigator].name);
-    if (!scope) {
-      return false;
-    }
+      GetInstancePrivate(aCx, obj, sProperties[SLOT_navigator].name);
+    MOZ_ASSERT(scope);
 
-    if (JSVAL_IS_VOID(scope->mSlots[SLOT_navigator])) {
-      nsRefPtr<WorkerNavigator> navigator = WorkerNavigator::Create(aCx, aObj);
+    if (scope->mSlots[SLOT_navigator].isUndefined()) {
+      nsRefPtr<WorkerNavigator> navigator = WorkerNavigator::Create(aCx, obj);
       if (!navigator) {
         return false;
       }
 
       scope->mSlots[SLOT_navigator] = OBJECT_TO_JSVAL(navigator->GetJSObject());
     }
 
-    aVp.set(scope->mSlots[SLOT_navigator]);
+    aArgs.rval().set(scope->mSlots[SLOT_navigator]);
     return true;
   }
 
   static bool
+  GetNavigator(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetNavigatorImpl>(aCx, args);
+  }
+
+  static bool
   Close(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
     if (!obj) {
       return false;
     }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[0].name);
@@ -632,26 +655,24 @@ NS_INTERFACE_MAP_END
 JSClass WorkerGlobalScope::sClass = {
   "WorkerGlobalScope",
   0,
   JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
 };
 
 const JSPropertySpec WorkerGlobalScope::sProperties[] = {
-  { "location", SLOT_location, PROPERTY_FLAGS, JSOP_WRAPPER(GetLocation),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { sEventStrings[STRING_onerror], STRING_onerror, PROPERTY_FLAGS,
-    JSOP_WRAPPER(GetOnErrorListener), JSOP_WRAPPER(SetOnErrorListener) },
-  { sEventStrings[STRING_onclose], STRING_onclose, PROPERTY_FLAGS,
-    JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
-  { "navigator", SLOT_navigator, PROPERTY_FLAGS, JSOP_WRAPPER(GetNavigator),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "self", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetSelf), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("location", GetLocation, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS(sEventStrings[STRING_onerror], GetOnErrorListener, SetOnErrorListener,
+          JSPROP_ENUMERATE),
+  JS_PSGS(sEventStrings[STRING_onclose], GetOnClose, SetOnClose,
+          JSPROP_ENUMERATE),
+  JS_PSGS("navigator", GetNavigator, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS("self", GetSelf, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 const JSFunctionSpec WorkerGlobalScope::sFunctions[] = {
   JS_FN("close", Close, 0, FUNCTION_FLAGS),
   JS_FN("importScripts", ImportScripts, 1, FUNCTION_FLAGS),
   JS_FN("setTimeout", SetTimeout, 1, FUNCTION_FLAGS),
   JS_FN("clearTimeout", ClearTimeout, 1, FUNCTION_FLAGS),
   JS_FN("setInterval", SetInterval, 1, FUNCTION_FLAGS),
@@ -746,74 +767,85 @@ protected:
     MOZ_COUNT_DTOR(mozilla::dom::workers::DedicatedWorkerGlobalScope);
   }
 
 private:
   using EventTarget::GetEventListener;
   using EventTarget::SetEventListener;
 
   static bool
-  GetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-                   JS::MutableHandle<JS::Value> aVp)
+  IsDedicatedWorkerGlobalScope(JS::Handle<JS::Value> v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+    return v.isObject() && JS_GetClass(&v.toObject()) == Class();
+  }
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+  static bool
+  GetOnMessageImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    const char* name = sEventStrings[STRING_onmessage];
+    DedicatedWorkerGlobalScope* scope =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
     ErrorResult rv;
 
     JSObject* listener =
       scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get event listener!");
       return false;
     }
 
-    aVp.set(listener ? OBJECT_TO_JSVAL(listener) : JSVAL_NULL);
+    aArgs.rval().setObjectOrNull(listener);
     return true;
   }
 
   static bool
-  SetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
-                   bool aStrict, JS::MutableHandle<JS::Value> aVp)
+  GetOnMessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsDedicatedWorkerGlobalScope, GetOnMessageImpl>(aCx, args);
+  }
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+  static bool
+  SetOnMessageImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    const char* name = sEventStrings[STRING_onmessage];
+    DedicatedWorkerGlobalScope* scope =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
-    if (JSVAL_IS_PRIMITIVE(aVp)) {
+    if (aArgs.length() == 0 || !aArgs[0].isObject()) {
       JS_ReportError(aCx, "Not an event listener!");
       return false;
     }
 
     ErrorResult rv;
 
-    JS::Rooted<JSObject*> listenerObj(aCx, JSVAL_TO_OBJECT(aVp));
+    JS::Rooted<JSObject*> listenerObj(aCx, &aArgs[0].toObject());
     scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2),
                             listenerObj, rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to set event listener!");
       return false;
     }
 
+    aArgs.rval().setUndefined();
     return true;
   }
 
+  static bool
+  SetOnMessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsDedicatedWorkerGlobalScope, SetOnMessageImpl>(aCx, args);
+  }
+
   static DedicatedWorkerGlobalScope*
   GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
   {
     JSClass* classPtr = JS_GetClass(aObj);
     if (classPtr == Class()) {
       return UnwrapDOMObject<DedicatedWorkerGlobalScope>(aObj);
     }
 
@@ -942,19 +974,19 @@ DOMIfaceAndProtoJSClass DedicatedWorkerG
   eInterfacePrototype,
   &sWorkerNativePropertyHooks,
   "[object DedicatedWorkerGlobalScope]",
   prototypes::id::_ID_Count,
   0
 };
 
 const JSPropertySpec DedicatedWorkerGlobalScope::sProperties[] = {
-  { sEventStrings[STRING_onmessage], STRING_onmessage, PROPERTY_FLAGS,
-    JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS(sEventStrings[STRING_onmessage], GetOnMessage, SetOnMessage,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 const JSFunctionSpec DedicatedWorkerGlobalScope::sFunctions[] = {
   JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),
   JS_FS_END
 };
 
 const char* const DedicatedWorkerGlobalScope::sEventStrings[STRING_COUNT] = {
@@ -975,16 +1007,22 @@ WorkerGlobalScope::GetInstancePrivate(JS
     return UnwrapDOMObject<DedicatedWorkerGlobalScope>(aObj);
   }
 
   JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
                        sClass.name, aFunctionName, classPtr->name);
   return NULL;
 }
 
+bool
+WorkerGlobalScope::IsWorkerGlobalScope(JS::Handle<JS::Value> v)
+{
+  return v.isObject() && JS_GetClass(&v.toObject()) == DedicatedWorkerGlobalScope::Class();
+}
+
 } /* anonymous namespace */
 
 BEGIN_WORKERS_NAMESPACE
 
 JSObject*
 CreateDedicatedWorkerGlobalScope(JSContext* aCx)
 {
   using namespace mozilla::dom;
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -220,11 +220,19 @@ const uint32_t kJSPrincipalsDebugToken =
 namespace exceptions {
 
 // Implemented in Exceptions.cpp
 void
 ThrowDOMExceptionForNSResult(JSContext* aCx, nsresult aNSResult);
 
 } // namespace exceptions
 
+// Throws the JSMSG_GETTER_ONLY exception.  This shouldn't be used going
+// forward -- getter-only properties should just use JS_PSG for the setter
+// (implying no setter at all), which will not throw when set in non-strict
+// code but will in strict code.  Old code should use this only for temporary
+// compatibility reasons.
+extern bool
+GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
+
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_workers_h__
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -475,21 +475,19 @@ BasicCompositor::EndFrame()
     mCopyTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
     mCopyTarget->SetSource(thebes);
     mCopyTarget->Paint();
     mCopyTarget = nullptr;
   } else {
     // Most platforms require us to buffer drawing to the widget surface.
     // That's why we don't draw to mDrawTarget directly.
     RefPtr<SourceSurface> source = mRenderTarget->mDrawTarget->Snapshot();
-    mDrawTarget->DrawSurface(source,
-                             Rect(0, 0, mWidgetSize.width, mWidgetSize.height),
-                             Rect(0, 0, mWidgetSize.width, mWidgetSize.height),
-                             DrawSurfaceOptions(),
-                             DrawOptions());
+    mDrawTarget->CopySurface(source,
+	                     IntRect(0, 0, mWidgetSize.width, mWidgetSize.height),
+			     IntPoint(0, 0));
     mWidget->EndRemoteDrawing();
   }
   mDrawTarget = nullptr;
   mRenderTarget = nullptr;
 }
 
 void
 BasicCompositor::AbortFrame()
new file mode 100644
--- /dev/null
+++ b/gfx/src/AppUnits.h
@@ -0,0 +1,11 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 _NS_APPUNITS_H_
+#define _NS_APPUNITS_H_
+namespace mozilla {
+static int32_t AppUnitsPerCSSPixel() { return 60; }
+}
+#endif /* _NS_APPUNITS_H_ */
--- a/gfx/src/moz.build
+++ b/gfx/src/moz.build
@@ -29,16 +29,20 @@ EXPORTS += [
     'nsRect.h',
     'nsRegion.h',
     'nsRenderingContext.h',
     'nsSize.h',
     'nsThemeConstants.h',
     'nsTransform2D.h',
 ]
 
+EXPORTS.mozilla += [
+    'AppUnits.h',
+]
+
 if CONFIG['MOZ_X11']:
     EXPORTS.mozilla += ['X11Util.h']
     CPP_SOURCES += [
         'X11Util.cpp',
     ]
 
 CPP_SOURCES += [
     'gfxCrashReporterUtils.cpp',
--- a/gfx/src/nsDeviceContext.h
+++ b/gfx/src/nsDeviceContext.h
@@ -12,16 +12,17 @@
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for nsCOMPtr
 #include "nsCoord.h"                    // for nscoord
 #include "nsError.h"                    // for nsresult
 #include "nsISupports.h"                // for NS_INLINE_DECL_REFCOUNTING
 #include "nsMathUtils.h"                // for NS_round
 #include "nscore.h"                     // for PRUnichar, nsAString
+#include "mozilla/AppUnits.h"           // for AppUnits
 
 class gfxASurface;
 class gfxUserFontSet;
 class nsFont;
 class nsFontCache;
 class nsFontMetrics;
 class nsIAtom;
 class nsIDeviceContextSpec;
@@ -60,17 +61,17 @@ public:
      * @return error status
      */
     nsresult CreateRenderingContext(nsRenderingContext *&aContext);
 
     /**
      * Gets the number of app units in one CSS pixel; this number is global,
      * not unique to each device context.
      */
-    static int32_t AppUnitsPerCSSPixel() { return 60; }
+    static int32_t AppUnitsPerCSSPixel() { return mozilla::AppUnitsPerCSSPixel(); }
 
     /**
      * Gets the number of app units in one device pixel; this number
      * is usually a factor of AppUnitsPerCSSPixel(), although that is
      * not guaranteed.
      */
     int32_t AppUnitsPerDevPixel() const { return mAppUnitsPerDevPixel; }
 
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1269,16 +1269,24 @@ static inline Value
 DoubleValue(double dbl)
 {
     Value v;
     v.setDouble(dbl);
     return v;
 }
 
 static inline Value
+Float32Value(float f)
+{
+    Value v;
+    v.setDouble(f);
+    return v;
+}
+
+static inline Value
 StringValue(JSString *str)
 {
     Value v;
     v.setString(str);
     return v;
 }
 
 static inline Value
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -2175,32 +2175,55 @@ public:
     void addsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("addsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void addss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("addss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void addsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("addsd      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+    void addss_mr(int offset, RegisterID base, XMMRegisterID dst)
+    {
+        spew("addss      %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset);
+    }
+
 #if !WTF_CPU_X86_64
     void addsd_mr(const void* address, XMMRegisterID dst)
     {
         spew("addsd      %p, %s",
              address, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address);
     }
+    void addss_mr(const void* address, XMMRegisterID dst)
+    {
+        spew("addss      %p, %s",
+             address, nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address);
+    }
 #endif
 
     void cvtss2sd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("cvtss2sd   %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_CVTSS2SD_VsdEd, (RegisterID)dst, (RegisterID)src);
@@ -2209,16 +2232,24 @@ public:
     void cvtsd2ss_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("cvtsd2ss   %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSD2SS_VsdEd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void cvtsi2ss_rr(RegisterID src, XMMRegisterID dst)
+    {
+        spew("cvtsi2ss   %s, %s",
+             nameIReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src);
+    }
+
     void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst)
     {
         spew("cvtsi2sd   %s, %s",
              nameIReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src);
     }
 
@@ -2243,16 +2274,32 @@ public:
     void cvtsi2sd_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
     {
         spew("cvtsi2sd   %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, index, scale, offset);
     }
 
+    void cvtsi2ss_mr(int offset, RegisterID base, XMMRegisterID dst)
+    {
+        spew("cvtsi2ss   %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, offset);
+    }
+
+    void cvtsi2ss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
+    {
+        spew("cvtsi2ss   %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), 1<<scale, nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, index, scale, offset);
+    }
+
 #if !WTF_CPU_X86_64
     void cvtsi2sd_mr(const void* address, XMMRegisterID dst)
     {
         spew("cvtsi2sd   %p, %s",
              address, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, address);
     }
@@ -2261,16 +2308,24 @@ public:
     void cvttsd2si_rr(XMMRegisterID src, RegisterID dst)
     {
         spew("cvttsd2si  %s, %s",
              nameFPReg(src), nameIReg(4, dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
     }
 
+    void cvttss2si_rr(XMMRegisterID src, RegisterID dst)
+    {
+        spew("cvttss2si  %s, %s",
+             nameFPReg(src), nameIReg(4, dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
+    }
+
 #if WTF_CPU_X86_64
     void cvttsd2sq_rr(XMMRegisterID src, RegisterID dst)
     {
         spew("cvttsd2sq  %s, %s",
              nameFPReg(src), nameIReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
     }
@@ -2386,52 +2441,32 @@ public:
     void movss_rm_disp32(XMMRegisterID src, int offset, RegisterID base)
     {
         spew("movss      %s, %s0x%x(%s)",
              nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp_disp32(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset);
     }
 
-#if !WTF_CPU_X86_64
-    void movss_rm(XMMRegisterID src, const void* addr)
-    {
-        spew("movss      %s, %p",
-             nameFPReg(src), addr);
-        m_formatter.prefix(PRE_SSE_F3);
-        m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, addr);
-    }
-#endif
-
     void movss_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("movss      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
     void movss_mr_disp32(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("movss      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp_disp32(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
-#if !WTF_CPU_X86_64
-    void movss_mr(const void* addr, XMMRegisterID dst)
-    {
-        spew("movss      %p, %s",
-             addr, nameFPReg(dst));
-        m_formatter.prefix(PRE_SSE_F3);
-        m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, addr);
-    }
-#endif
-
     void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
         spew("movsd      %s, %d(%s,%s,%d)",
              nameFPReg(src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
     }
 
@@ -2478,32 +2513,56 @@ public:
     void movsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("movsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void movss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("movss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
 #if !WTF_CPU_X86_64
     void movsd_mr(const void* address, XMMRegisterID dst)
     {
         spew("movsd      %p, %s",
              address, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
     }
 
+    void movss_mr(const void* address, XMMRegisterID dst)
+    {
+        spew("movss      %p, %s",
+             address, nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
+    }
+
     void movsd_rm(XMMRegisterID src, const void* address)
     {
         spew("movsd      %s, %p",
              nameFPReg(src), address);
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address);
     }
+
+    void movss_rm(XMMRegisterID src, const void* address)
+    {
+        spew("movss      %s, %p",
+             nameFPReg(src), address);
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address);
+    }
 #else
     JmpSrc movsd_ripr(XMMRegisterID dst)
     {
         spew("movsd      \?(%%rip), %s",
              nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteRipOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, 0);
         return JmpSrc(m_formatter.size());
@@ -2553,48 +2612,87 @@ public:
     void mulsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("mulsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void mulss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("mulss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void mulsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("mulsd      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+    void mulss_mr(int offset, RegisterID base, XMMRegisterID dst)
+    {
+        spew("mulss      %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset);
+    }
+
     void pextrw_irr(int whichWord, XMMRegisterID src, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_PEXTRW_GdUdIb, (RegisterID)dst, (RegisterID)src);
         m_formatter.immediate8(whichWord);
     }
 
     void subsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("subsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void subss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("subss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void subsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("subsd      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+    void subss_mr(int offset, RegisterID base, XMMRegisterID dst)
+    {
+        spew("subss      %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset);
+    }
+
+    void ucomiss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("ucomiss    %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void ucomisd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("ucomisd    %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
@@ -2609,32 +2707,55 @@ public:
     void divsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("divsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void divss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("divss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void divsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("divsd      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+    void divss_mr(int offset, RegisterID base, XMMRegisterID dst)
+    {
+        spew("divss      %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset);
+    }
+
     void xorpd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("xorpd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void xorps_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("xorps      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void orpd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("orpd       %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_ORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
     }
 
@@ -2789,16 +2910,21 @@ public:
         m_formatter.jumpTablePointer(ptr);
     }
 
     void doubleConstant(double d)
     {
         spew(".double %.20f", d);
         m_formatter.doubleConstant(d);
     }
+    void floatConstant(float f)
+    {
+        spew(".float %.20f", f);
+        m_formatter.floatConstant(f);
+    }
 
     void int64Constant(int64_t i)
     {
         spew(".quad %lld", (long long)i);
         m_formatter.int64Constant(i);
     }
 
     // Linking & patching:
@@ -3429,16 +3555,27 @@ private:
             union {
                 uint64_t u64;
                 double d;
             } u;
             u.d = d;
             m_buffer.putInt64Unchecked(u.u64);
         }
 
+        void floatConstant(float f)
+        {
+            m_buffer.ensureSpace(sizeof(float));
+            union {
+                uint32_t u32;
+                float f;
+            } u;
+            u.f = f;
+            m_buffer.putIntUnchecked(u.u32);
+        }
+
         void int64Constant(int64_t i)
         {
             m_buffer.ensureSpace(sizeof(int64_t));
             m_buffer.putInt64Unchecked(i);
         }
 
         // Administrative methods:
 
deleted file mode 100644
--- a/js/src/builtin/Iterator-inl.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef builtin_Iterator_inl_h
-#define builtin_Iterator_inl_h
-
-#include "jsiter.h"
-
-#include "vm/ObjectImpl-inl.h"
-
-inline void
-js::PropertyIteratorObject::setNativeIterator(js::NativeIterator *ni)
-{
-    setPrivate(ni);
-}
-
-#endif /* builtin_Iterator_inl_h */
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -8,17 +8,16 @@
 
 #include "jscntxt.h"
 
 #include "vm/RegExpStatics.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
-#include "vm/RegExpObject-inl.h"
 #include "vm/RegExpStatics-inl.h"
 
 using namespace js;
 using namespace js::types;
 
 using mozilla::ArrayLength;
 
 static inline bool
--- a/js/src/gc/Barrier-inl.h
+++ b/js/src/gc/Barrier-inl.h
@@ -13,42 +13,16 @@
 
 #include "gc/Marking.h"
 #include "gc/StoreBuffer.h"
 
 #include "vm/String-inl.h"
 
 namespace js {
 
-template <typename T, typename Unioned>
-void
-EncapsulatedPtr<T, Unioned>::pre()
-{
-    T::writeBarrierPre(value);
-}
-
-template <typename T>
-inline void
-RelocatablePtr<T>::post()
-{
-#ifdef JSGC_GENERATIONAL
-    JS_ASSERT(this->value);
-    T::writeBarrierPostRelocate(this->value, &this->value);
-#endif
-}
-
-template <typename T>
-inline void
-RelocatablePtr<T>::relocate(JSRuntime *rt)
-{
-#ifdef JSGC_GENERATIONAL
-    T::writeBarrierPostRemove(this->value, &this->value);
-#endif
-}
-
 #ifdef JSGC_GENERATIONAL
 class DenseRangeRef : public gc::BufferableRef
 {
     JSObject *owner;
     uint32_t start;
     uint32_t end;
 
   public:
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -44,11 +44,16 @@ HeapSlot::preconditionForSet(Zone *zone,
 void
 HeapSlot::preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target)
 {
     JS_ASSERT_IF(kind == Slot, obj->getSlotAddressUnchecked(slot)->get() == target);
     JS_ASSERT_IF(kind == Element,
                  static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target);
 }
 
+bool
+RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone)
+{
+    return shadowZone->runtimeFromMainThread()->isHeapMajorCollecting();
+}
 #endif // DEBUG
 
 } // namespace js
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -192,17 +192,17 @@ class EncapsulatedPtr
     Unioned *unsafeGetUnioned() { return &other; }
 
     T &operator*() const { return *value; }
     T *operator->() const { return value; }
 
     operator T*() const { return value; }
 
   protected:
-    void pre();
+    void pre() { T::writeBarrierPre(value); }
 };
 
 template <class T, class Unioned = uintptr_t>
 class HeapPtr : public EncapsulatedPtr<T, Unioned>
 {
   public:
     HeapPtr() : EncapsulatedPtr<T>(NULL) {}
     explicit HeapPtr(T *v) : EncapsulatedPtr<T>(v) { post(); }
@@ -315,18 +315,28 @@ class RelocatablePtr : public Encapsulat
             JSRuntime *rt = this->value->runtimeFromMainThread();
             this->value = v;
             relocate(rt);
         }
         return *this;
     }
 
   protected:
-    inline void post();
-    inline void relocate(JSRuntime *rt);
+    void post() {
+#ifdef JSGC_GENERATIONAL
+        JS_ASSERT(this->value);
+        T::writeBarrierPostRelocate(this->value, &this->value);
+#endif
+    }
+
+    void relocate(JSRuntime *rt) {
+#ifdef JSGC_GENERATIONAL
+        T::writeBarrierPostRemove(this->value, &this->value);
+#endif
+    }
 };
 
 /*
  * This is a hack for RegExpStatics::updateFromMatch. It allows us to do two
  * barriers with only one branch to check if we're in an incremental GC.
  */
 template<class T1, class T2>
 static inline void
@@ -917,11 +927,16 @@ class ReadBarrieredValue
 
     inline const Value &get() const;
     Value *unsafeGet() { return &value; }
     inline operator const Value &() const;
 
     inline JSObject &toObject() const;
 };
 
+#ifdef DEBUG
+bool
+RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone);
+#endif
+
 } /* namespace js */
 
 #endif /* gc_Barrier_h */
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -94,16 +94,17 @@ struct Cell
   public:
     inline ArenaHeader *arenaHeader() const;
     inline AllocKind tenuredGetAllocKind() const;
     MOZ_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const;
     MOZ_ALWAYS_INLINE bool markIfUnmarked(uint32_t color = BLACK) const;
     MOZ_ALWAYS_INLINE void unmark(uint32_t color) const;
 
     inline JSRuntime *runtimeFromMainThread() const;
+    inline JS::shadow::Runtime *shadowRuntimeFromMainThread() const;
     inline JS::Zone *tenuredZone() const;
     inline bool tenuredIsInsideZone(JS::Zone *zone) const;
 
     // Note: Unrestricted access to the runtime of a GC thing from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     inline JSRuntime *runtimeFromAnyThread() const;
     inline JS::shadow::Runtime *shadowRuntimeFromAnyThread() const;
 
@@ -958,16 +959,22 @@ Cell::arenaHeader() const
 inline JSRuntime *
 Cell::runtimeFromMainThread() const
 {
     JSRuntime *rt = chunk()->info.runtime;
     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
     return rt;
 }
 
+inline JS::shadow::Runtime *
+Cell::shadowRuntimeFromMainThread() const
+{
+    return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromMainThread());
+}
+
 inline JSRuntime *
 Cell::runtimeFromAnyThread() const
 {
     return chunk()->info.runtime;
 }
 
 inline JS::shadow::Runtime *
 Cell::shadowRuntimeFromAnyThread() const
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -3,17 +3,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_Marking_h
 #define gc_Marking_h
 
 #include "gc/Barrier.h"
-#include "jit/IonCode.h"
 #include "js/TypeDecls.h"
 
 class JSAtom;
 class JSLinearString;
 
 namespace js {
 
 class ArgumentsObject;
@@ -25,16 +24,26 @@ struct GCMarker;
 class GlobalObject;
 class LazyScript;
 class ScopeObject;
 class Shape;
 class UnownedBaseShape;
 
 template<class, typename> class HeapPtr;
 
+namespace jit {
+class IonCode;
+class IonScript;
+class VMFunction;
+}
+
+namespace types {
+class Type;
+}
+
 namespace gc {
 
 /*** Object Marking ***/
 
 /*
  * These functions expose marking functionality for all of the different GC
  * thing kinds. For each GC thing, there are several variants. As an example,
  * these are the variants generated for JSObject. They are listed from most to
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -28,13 +28,13 @@ typedef JS::Handle<js::ScriptSourceObjec
 
 typedef JS::MutableHandle<Shape*>      MutableHandleShape;
 typedef JS::MutableHandle<JSAtom*>     MutableHandleAtom;
 
 typedef JS::Rooted<Shape*>             RootedShape;
 typedef JS::Rooted<types::TypeObject*> RootedTypeObject;
 typedef JS::Rooted<JSAtom*>            RootedAtom;
 typedef JS::Rooted<PropertyName*>      RootedPropertyName;
-typedef Rooted<js::ScriptSourceObject*> RootedScriptSource;
+typedef JS::Rooted<js::ScriptSourceObject*> RootedScriptSource;
 
 } /* namespace js */
 
 #endif /* gc_Rooting_h */
--- a/js/src/jit-test/tests/asm.js/testBasic.js
+++ b/js/src/jit-test/tests/asm.js/testBasic.js
@@ -41,41 +41,36 @@ assertEq(asmLink(asmCompile('x', USE_ASM
 assertEq(asmLink(asmCompile('x','y', USE_ASM + 'function f(){} return f'), 1, 2)(), undefined);
 
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(i) {i=i|0} return f'))(42), undefined);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f() {var i=0;; var j=1;} return f'))(), undefined); // bug 877965 second part
 assertAsmTypeFail(USE_ASM + 'function f(i) {i=i|0;var i} return f');
 assertAsmTypeFail(USE_ASM + 'function f(i) {i=i|0;var i=0} return f');
 
 assertAsmTypeFail('glob', USE_ASM + 'var im=glob.imul; function f() {} return f');
-var code = asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f');
-assertAsmLinkAlwaysFail(code, null);
-assertAsmLinkAlwaysFail(code, {});
-assertAsmLinkAlwaysFail(code, {imul:Math.imul});
-assertEq(asmLink(code, {Math:{imul:Math.imul}})(2,3), 6);
-var code = asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f');
-assertEq(asmLink(code, this)(8,4), 32);
+assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), null);
+assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), {});
+assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), {imul:Math.imul});
+assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), {Math:{imul:Math.imul}})(2,3), 6);
+assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), this)(8,4), 32);
 
-var code = asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f');
-assertAsmLinkAlwaysFail(code, null, null);
-assertAsmLinkAlwaysFail(code, this, null, null);
-assertAsmLinkAlwaysFail(code, this, null, null);
-assertAsmLinkAlwaysFail(code, this, null, new ArrayBuffer(1));
-assertAsmLinkFail(code, this, null, new ArrayBuffer(100));
-assertAsmLinkFail(code, this, null, new ArrayBuffer(4000));
-assertEq(asmLink(code, this, null, new ArrayBuffer(4096))(), undefined);
-var code = asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f');
-assertEq(asmLink(code, this, null, new ArrayBuffer(2*4096))(), undefined);
+assertAsmLinkAlwaysFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), null, null);
+assertAsmLinkAlwaysFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, null);
+assertAsmLinkAlwaysFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, null);
+assertAsmLinkAlwaysFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, new ArrayBuffer(1));
+assertAsmLinkFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, new ArrayBuffer(100));
+assertAsmLinkFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, new ArrayBuffer(4000));
+assertEq(asmLink(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, new ArrayBuffer(4096))(), undefined);
+assertEq(asmLink(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, new ArrayBuffer(2*4096))(), undefined);
 
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i]|0; return i|0}; return f');
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>1]|0; return i|0}; return f');
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>1]|0; return i|0}; return f');
-var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>2]|0; return i|0}; return f');
-assertAsmLinkAlwaysFail(code, this, null, new ArrayBuffer(4095));
-assertEq(code(this, null, new ArrayBuffer(4096))(), 0);
+assertAsmLinkAlwaysFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>2]|0; return i|0}; return f'), this, null, new ArrayBuffer(4095));
+assertEq(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>2]|0; return i|0}; return f')(this, null, new ArrayBuffer(4096))(), 0);
 
 var exp = asmLink(asmCompile(USE_ASM + "return {}"));
 assertEq(Object.keys(exp).length, 0);
 
 var exp = asmLink(asmCompile(USE_ASM + "function f() { return 3 } return {f:f,f:f}"));
 assertEq(exp.f(), 3);
 assertEq(Object.keys(exp).join(), 'f');
 
--- a/js/src/jit-test/tests/asm.js/testGlobals.js
+++ b/js/src/jit-test/tests/asm.js/testGlobals.js
@@ -17,36 +17,32 @@ assertEq(f(42), 0);
 assertEq(f(-1), 42);
 
 assertAsmTypeFail('global', USE_ASM + "var i=global; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + "var i=global|0; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + "var j=0;var i=j.i|0; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + "var i=global.i|0; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + "var i=global.i|0; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + 'var i=global.Infinity; function f() { i = 0.0 } return f');
-var code = asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f');
-assertAsmLinkAlwaysFail(code, undefined);
-assertAsmLinkAlwaysFail(code, null);
-assertAsmLinkFail(code, 3);
-assertAsmLinkFail(code, {});
-assertAsmLinkFail(code, {Infinity:NaN});
-assertAsmLinkFail(code, {Infinity:-Infinity});
-assertEq(asmLink(code, {Infinity:Infinity})(), Infinity);
-var code = asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f');
-assertEq(asmLink(code, this)(), Infinity);
-var code = asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f');
-assertAsmLinkAlwaysFail(code, undefined);
-assertAsmLinkAlwaysFail(code, null);
-assertAsmLinkFail(code, 3);
-assertAsmLinkFail(code, {});
-assertAsmLinkFail(code, {Infinity:Infinity});
-assertAsmLinkFail(code, {Infinity:-Infinity});
-assertEq(asmLink(code, {NaN:NaN})(), NaN);
-var code = asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f');
-assertEq(asmLink(code, this)(), NaN);
+assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), undefined);
+assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), null);
+assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), 3);
+assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {});
+assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:NaN});
+assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:-Infinity});
+assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:Infinity})(), Infinity);
+assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), this)(), Infinity);
+assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), undefined);
+assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), null);
+assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), 3);
+assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {});
+assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {Infinity:Infinity});
+assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {Infinity:-Infinity});
+assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {NaN:NaN})(), NaN);
+assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), this)(), NaN);
 
 assertAsmLinkFail(asmCompile('glob','foreign','buf', USE_ASM + 'var t = new glob.Int32Array(buf); function f() {} return f'), {get Int32Array(){return Int32Array}}, null, new ArrayBuffer(4096))
 assertAsmLinkFail(asmCompile('glob','foreign','buf', USE_ASM + 'var t = new glob.Int32Array(buf); function f() {} return f'), new Proxy({}, {get:function() {return Int32Array}}), null, new ArrayBuffer(4096))
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var t = glob.Math.sin; function f() {} return f'), {get Math(){return Math}});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var t = glob.Math.sin; function f() {} return f'), new Proxy({}, {get:function(){return Math}}));
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var t = glob.Math.sin; function f() {} return f'), {Math:{get sin(){return Math.sin}}});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var t = glob.Math.sin; function f() {} return f'), {Math:new Proxy({}, {get:function(){return Math.sin}})});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var t = glob.Infinity; function f() {} return f'), {get Infinity(){return Infinity}});
@@ -64,8 +60,17 @@ assertAsmLinkAlwaysFail(asmCompile('glob
 assertAsmLinkAlwaysFail(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f"), null, null);
 assertAsmLinkAlwaysFail(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f"), this, null);
 assertAsmLinkFail(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f"), this, 42);
 assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f")(null, {i:42})), 42);
 assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f")(null, {i:1.4})), 1);
 assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=+imp.i; function f() { return +i } return f")(null, {i:42})), 42);
 assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=+imp.i; function f() { return +i } return f")(this, {i:1.4})), 1.4);
 assertEq(asmLink(asmCompile(USE_ASM + "var g=0; function f() { var i=42; while (1) { break; } g = i; return g|0 } return f"))(), 42);
+
+var f1 = asmCompile('global', 'foreign', 'heap', USE_ASM + 'var i32 = new global.Int32Array(heap); function g() { return i32[4]|0 } return g');
+var global = this;
+var ab = new ArrayBuffer(4096);
+var p = new Proxy(global,
+                  {has:function(name) { f1(global, null, ab); return true},
+                   getOwnPropertyDescriptor:function(name) { return {value:Int32Array}}});
+new Int32Array(ab)[4] = 42;
+assertEq(f1(p, null, ab)(), 42);
--- a/js/src/jit-test/tests/asm.js/testHeapAccess.js
+++ b/js/src/jit-test/tests/asm.js/testHeapAccess.js
@@ -437,11 +437,19 @@ assertEq(asmLink(asmCompile('glob', 'imp
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[(8192&8191)>>2] = -1.0; f32[0] = 0.0; return +f32[(8192&8191)>>2]; } return f'), this, null, buf)(),0.0);
 
 var buf = new ArrayBuffer(8192);
 new Float64Array(buf)[1023] = -1.0;
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8184&8191)>>3]; } return f'), this, null, buf)(),-1.0);
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8191&8191)>>3]; } return f'), this, null, buf)(),-1.0);
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[(8192&8191)>>3] = -1.0; f64[0] = 0.0; return +f64[(8192&8191)>>3]; } return f'), this, null, buf)(),0.0);
 
+// Bug 913867
+var buf = new ArrayBuffer(8192);
+new Int32Array(buf)[0] = 0x55aa5a5a;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[(0&0)>>2]|0; } return f'), this, null, buf)(),0x55aa5a5a);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[(4&0)>>2]|0; } return f'), this, null, buf)(),0x55aa5a5a);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f1() { i32[0] = 1; return 8; }; function f() { return i32[((f1()|0)&0)>>2]|0; } return f'), this, null, buf)(),1);
+assertEq(new Int32Array(buf)[0], 1);
+
 
 // Bug 882012
 assertEq(asmLink(asmCompile('stdlib', 'foreign', 'heap', USE_ASM + "var id=foreign.id;var doubles=new stdlib.Float64Array(heap);function g(){doubles[0]=+id(2.0);return +doubles[0];}return g"), this, {id: function(x){return x;}}, BUF_64KB)(), 2.0);
--- a/js/src/jit-test/tests/asm.js/testMathLib.js
+++ b/js/src/jit-test/tests/asm.js/testMathLib.js
@@ -1,96 +1,82 @@
 load(libdir + "asm.js");
 
 function testUnary(f, g) {
     var numbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000];
     for (n of numbers)
         assertEq(f(n), g(n));
 }
 
-var code = asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f');
-assertAsmLinkFail(code, {Math:{sin:Math.sqrt}});
-assertAsmLinkFail(code, {Math:{sin:null}});
-testUnary(asmLink(code, {Math:{sin:Math.sin}}), Math.sin);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sin:Math.sqrt}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sin:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sin:Math.sin}}), Math.sin);
 
-var code = asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f');
-assertAsmLinkFail(code, {Math:{cos:Math.sqrt}});
-assertAsmLinkFail(code, {Math:{cos:null}});
-testUnary(asmLink(code, {Math:{cos:Math.cos}}), Math.cos);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f'), {Math:{cos:Math.sqrt}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f'), {Math:{cos:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f'), {Math:{cos:Math.cos}}), Math.cos);
 
-var code = asmCompile('glob', USE_ASM + 'var ta=glob.Math.tan; function f(d) { d=+d; return +ta(d) } return f');
-assertAsmLinkFail(code, {Math:{tan:Math.sqrt}});
-assertAsmLinkFail(code, {Math:{tan:null}});
-testUnary(asmLink(code, {Math:{tan:Math.tan}}), Math.tan);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ta=glob.Math.tan; function f(d) { d=+d; return +ta(d) } return f'), {Math:{tan:Math.sqrt}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ta=glob.Math.tan; function f(d) { d=+d; return +ta(d) } return f'), {Math:{tan:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var ta=glob.Math.tan; function f(d) { d=+d; return +ta(d) } return f'), {Math:{tan:Math.tan}}), Math.tan);
 
-var code = asmCompile('glob', USE_ASM + 'var as=glob.Math.asin; function f(d) { d=+d; return +as(d) } return f');
-assertAsmLinkFail(code, {Math:{asin:Math.sqrt}});
-assertAsmLinkFail(code, {Math:{asin:null}});
-testUnary(asmLink(code, {Math:{asin:Math.asin}}), Math.asin);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var as=glob.Math.asin; function f(d) { d=+d; return +as(d) } return f'), {Math:{asin:Math.sqrt}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var as=glob.Math.asin; function f(d) { d=+d; return +as(d) } return f'), {Math:{asin:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var as=glob.Math.asin; function f(d) { d=+d; return +as(d) } return f'), {Math:{asin:Math.asin}}), Math.asin);
 
-var code = asmCompile('glob', USE_ASM + 'var ac=glob.Math.acos; function f(d) { d=+d; return +ac(d) } return f');
-assertAsmLinkFail(code, {Math:{acos:Math.sqrt}});
-assertAsmLinkFail(code, {Math:{acos:null}});
-testUnary(asmLink(code, {Math:{acos:Math.acos}}), Math.acos);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ac=glob.Math.acos; function f(d) { d=+d; return +ac(d) } return f'), {Math:{acos:Math.sqrt}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ac=glob.Math.acos; function f(d) { d=+d; return +ac(d) } return f'), {Math:{acos:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var ac=glob.Math.acos; function f(d) { d=+d; return +ac(d) } return f'), {Math:{acos:Math.acos}}), Math.acos);
 
-var code = asmCompile('glob', USE_ASM + 'var at=glob.Math.atan; function f(d) { d=+d; return +at(d) } return f');
-assertAsmLinkFail(code, {Math:{atan:Math.sqrt}});
-assertAsmLinkFail(code, {Math:{atan:null}});
-testUnary(asmLink(code, {Math:{atan:Math.atan}}), Math.atan);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan; function f(d) { d=+d; return +at(d) } return f'), {Math:{atan:Math.sqrt}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan; function f(d) { d=+d; return +at(d) } return f'), {Math:{atan:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan; function f(d) { d=+d; return +at(d) } return f'), {Math:{atan:Math.atan}}), Math.atan);
 
-var code = asmCompile('glob', USE_ASM + 'var ce=glob.Math.ceil; function f(d) { d=+d; return +ce(d) } return f');
-assertAsmLinkFail(code, {Math:{ceil:Math.sqrt}});
-assertAsmLinkFail(code, {Math:{ceil:null}});
-testUnary(asmLink(code, {Math:{ceil:Math.ceil}}), Math.ceil);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ce=glob.Math.ceil; function f(d) { d=+d; return +ce(d) } return f'), {Math:{ceil:Math.sqrt}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ce=glob.Math.ceil; function f(d) { d=+d; return +ce(d) } return f'), {Math:{ceil:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var ce=glob.Math.ceil; function f(d) { d=+d; return +ce(d) } return f'), {Math:{ceil:Math.ceil}}), Math.ceil);
 
-var code = asmCompile('glob', USE_ASM + 'var fl=glob.Math.floor; function f(d) { d=+d; return +fl(d) } return f');
-assertAsmLinkFail(code, {Math:{floor:Math.sqrt}});
-assertAsmLinkFail(code, {Math:{floor:null}});
-testUnary(asmLink(code, {Math:{floor:Math.floor}}), Math.floor);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var fl=glob.Math.floor; function f(d) { d=+d; return +fl(d) } return f'), {Math:{floor:Math.sqrt}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var fl=glob.Math.floor; function f(d) { d=+d; return +fl(d) } return f'), {Math:{floor:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var fl=glob.Math.floor; function f(d) { d=+d; return +fl(d) } return f'), {Math:{floor:Math.floor}}), Math.floor);
 
-var code = asmCompile('glob', USE_ASM + 'var exq=glob.Math.exp; function f(d) { d=+d; return +exq(d) } return f');
-assertAsmLinkFail(code, {Math:{exp:Math.sqrt}});
-assertAsmLinkFail(code, {Math:{exp:null}});
-testUnary(asmLink(code, {Math:{exp:Math.exp}}), Math.exp);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var exq=glob.Math.exp; function f(d) { d=+d; return +exq(d) } return f'), {Math:{exp:Math.sqrt}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var exq=glob.Math.exp; function f(d) { d=+d; return +exq(d) } return f'), {Math:{exp:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var exq=glob.Math.exp; function f(d) { d=+d; return +exq(d) } return f'), {Math:{exp:Math.exp}}), Math.exp);
 
-var code = asmCompile('glob', USE_ASM + 'var lo=glob.Math.log; function f(d) { d=+d; return +lo(d) } return f');
-assertAsmLinkFail(code, {Math:{log:Math.sqrt}});
-assertAsmLinkFail(code, {Math:{log:null}});
-testUnary(asmLink(code, {Math:{log:Math.log}}), Math.log);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var lo=glob.Math.log; function f(d) { d=+d; return +lo(d) } return f'), {Math:{log:Math.sqrt}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var lo=glob.Math.log; function f(d) { d=+d; return +lo(d) } return f'), {Math:{log:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var lo=glob.Math.log; function f(d) { d=+d; return +lo(d) } return f'), {Math:{log:Math.log}}), Math.log);
 
-var code = asmCompile('glob', USE_ASM + 'var sq=glob.Math.sqrt; function f(d) { d=+d; return +sq(d) } return f');
-assertAsmLinkFail(code, {Math:{sqrt:Math.sin}});
-assertAsmLinkFail(code, {Math:{sqrt:null}});
-testUnary(asmLink(code, {Math:{sqrt:Math.sqrt}}), Math.sqrt);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sqrt; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sqrt:Math.sin}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sqrt; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sqrt:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sqrt; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sqrt:Math.sqrt}}), Math.sqrt);
 
-var code = asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; return +abs(d) } return f');
-assertAsmLinkFail(code, {Math:{abs:Math.sin}});
-assertAsmLinkFail(code, {Math:{abs:null}});
-testUnary(asmLink(code, {Math:{abs:Math.abs}}), Math.abs);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; return +abs(d) } return f'), {Math:{abs:Math.sin}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; return +abs(d) } return f'), {Math:{abs:null}});
+testUnary(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; return +abs(d) } return f'), {Math:{abs:Math.abs}}), Math.abs);
 
 var f = asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; return abs(i|0)|0 } return f'), this);
 for (n of [-Math.pow(2,31)-1, -Math.pow(2,31), -Math.pow(2,31)+1, -1, 0, 1, Math.pow(2,31)-2, Math.pow(2,31)-1, Math.pow(2,31)])
     assertEq(f(n), Math.abs(n|0)|0);
 
 function testBinary(f, g) {
     var numbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000];
     for (n of numbers)
         for (o of numbers)
             assertEq(f(n,o), g(n,o));
 }
 
-var code = asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f');
-assertAsmLinkFail(code, {Math:{pow:Math.sin}});
-assertAsmLinkFail(code, {Math:{pow:null}});
-testBinary(asmLink(code, {Math:{pow:Math.pow}}), Math.pow);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.sin}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:null}});
+testBinary(asmLink(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.pow}}), Math.pow);
 
-var code = asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f');
-assertAsmLinkFail(code, {Math:{atan2:Math.sin}});
-assertAsmLinkFail(code, {Math:{atan2:null}});
-testBinary(asmLink(code, {Math:{atan2:Math.atan2}}), Math.atan2);
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.sin}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:null}});
+testBinary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.atan2}}), Math.atan2);
 
 assertAsmTypeFail('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; d = sin(d); } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; var i=0; i = sin(d)|0; } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var pow=glob.Math.pow; function f(d) { d=+d; d = pow(d,d); } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var pow=glob.Math.pow; function f(d) { d=+d; var i=0; i = pow(d,d)|0; } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var atan2=glob.Math.atan2; function f(d) { d=+d; d = atan2(d,d); } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var atan2=glob.Math.atan2; function f(d) { d=+d; var i=0; i = atan2(d,d)|0; } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(d) { d=+d; d = sqrt(d); } return f');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Object-evalInGlobal-09.js
@@ -0,0 +1,9 @@
+// The frame created for evalInGlobal is never marked as a 'FUNCTION' frame.
+
+(function () {
+  var g = newGlobal();
+  var dbg = new Debugger;
+  var gw = dbg.addDebuggee(g);
+  gw.evalInGlobalWithBindings("eval('Math')",{}).return
+})();
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug911707.js
@@ -0,0 +1,5 @@
+// |jit-test| ion-eager
+x = [ "CNY", "TWD", "invalid" ];
+Object.freeze(x).map(function() {
+    x.length = 6
+})
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug911708.js
@@ -0,0 +1,8 @@
+function x() {
+    yield x
+}
+new(x)
+ParallelArray([7247], function() {
+    --x
+    eval("")
+})
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -34,16 +34,17 @@
 #include "gc/Barrier-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 using namespace js::jit;
 
 using mozilla::AddToHash;
 using mozilla::ArrayLength;
+using mozilla::CountLeadingZeroes32;
 using mozilla::DebugOnly;
 using mozilla::HashGeneric;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 using mozilla::Maybe;
 using mozilla::OldMove;
 using mozilla::MoveRef;
 
@@ -3112,18 +3113,20 @@ FoldMaskedArrayIndex(FunctionCompiler &f
 {
     ParseNode *indexNode = BinaryLeft(*indexExpr);
     ParseNode *maskNode = BinaryRight(*indexExpr);
 
     uint32_t mask2;
     if (IsLiteralUint32(maskNode, &mask2)) {
         // Flag the access to skip the bounds check if the mask ensures that an 'out of
         // bounds' access can not occur based on the current heap length constraint.
-        if (mozilla::CountLeadingZeroes32(f.m().minHeapLength() - 1) <= mozilla::CountLeadingZeroes32(mask2))
+        if (mask2 == 0 ||
+            CountLeadingZeroes32(f.m().minHeapLength() - 1) <= CountLeadingZeroes32(mask2)) {
             *needsBoundsCheck = NO_BOUNDS_CHECK;
+        }
         *mask &= mask2;
         *indexExpr = indexNode;
         return true;
     }
 
     return false;
 }
 
@@ -5973,17 +5976,18 @@ GenerateFFIIonExit(ModuleCompiler &m, co
 #else
     masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
 #endif
 
     switch (exit.sig().retType().which()) {
       case RetType::Void:
         break;
       case RetType::Signed:
-        masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert);
+        masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert,
+                                 /* -0 check */ false);
         break;
       case RetType::Double:
         masm.convertValueToDouble(JSReturnOperand, ReturnFloatReg, &oolConvert);
 #if defined(JS_CPU_ARM) && !defined(JS_CPU_ARM_HARDFP)
         masm.boxDouble(ReturnFloatReg, softfpReturnOperand);
 #endif
         break;
     }
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -190,16 +190,17 @@ ValidateGlobalConstant(JSContext *cx, As
 static bool
 DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
 {
     if (module.isLinked())
         return LinkFail(cx, "As a temporary limitation, modules cannot be linked more than "
                             "once. This limitation should be removed in a future release. To "
                             "work around this, compile a second module (e.g., using the "
                             "Function constructor).");
+    module.setIsLinked();
 
     RootedValue globalVal(cx, UndefinedValue());
     if (args.length() > 0)
         globalVal = args[0];
 
     RootedValue importVal(cx, UndefinedValue());
     if (args.length() > 1)
         importVal = args[1];
@@ -222,17 +223,17 @@ DynamicallyLinkModule(JSContext *cx, Cal
         // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
         JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
         if (heap->byteLength() < module.minHeapLength())
             return LinkFail(cx, "ArrayBuffer byteLength less than the largest source code heap length constraint.");
 
         if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
             return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
 
-        module.patchHeapAccesses(heap, cx);
+        module.initHeap(heap, cx);
     }
 
     AutoObjectVector ffis(cx);
     if (!ffis.resize(module.numFFIs()))
         return false;
 
     for (unsigned i = 0; i < module.numGlobals(); i++) {
         AsmJSModule::Global &global = module.global(i);
@@ -258,17 +259,16 @@ DynamicallyLinkModule(JSContext *cx, Cal
                 return false;
             break;
         }
     }
 
     for (unsigned i = 0; i < module.numExits(); i++)
         module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();
 
-    module.setIsLinked(heap);
     return true;
 }
 
 AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
   : cx_(cx),
     module_(module),
     errorRejoinSP_(NULL),
     profiler_(NULL),
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -17,18 +17,23 @@
 
 #include "js/MemoryMetrics.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
 void
-AsmJSModule::patchHeapAccesses(ArrayBufferObject *heap, JSContext *cx)
+AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx)
 {
+    JS_ASSERT(linked_);
+    JS_ASSERT(!maybeHeap_);
+    maybeHeap_ = heap;
+    heapDatum() = heap->dataPointer();
+
     JS_ASSERT(IsPowerOfTwo(heap->byteLength()));
 #if defined(JS_CPU_X86)
     uint8_t *heapOffset = heap->dataPointer();
     void *heapLength = (void*)heap->byteLength();
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
         const jit::AsmJSHeapAccess &access = heapAccesses_[i];
         if (access.hasLengthCheck())
             JSC::X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -625,17 +625,17 @@ class AsmJSModule
         return heapAccesses_.length();
     }
     jit::AsmJSHeapAccess &heapAccess(unsigned i) {
         return heapAccesses_[i];
     }
     const jit::AsmJSHeapAccess &heapAccess(unsigned i) const {
         return heapAccesses_[i];
     }
-    void patchHeapAccesses(ArrayBufferObject *heap, JSContext *cx);
+    void initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx);
 
     void requireHeapLengthToBeAtLeast(uint32_t len) {
         if (len > minHeapLength_)
             minHeapLength_ = len;
     }
     uint32_t minHeapLength() const {
         return minHeapLength_;
     }
@@ -650,21 +650,19 @@ class AsmJSModule
 
     void setOperationCallbackExit(uint8_t *ptr) {
         operationCallbackExit_ = ptr;
     }
     uint8_t *operationCallbackExit() const {
         return operationCallbackExit_;
     }
 
-    void setIsLinked(Handle<ArrayBufferObject*> maybeHeap) {
+    void setIsLinked() {
         JS_ASSERT(!linked_);
         linked_ = true;
-        maybeHeap_ = maybeHeap;
-        heapDatum() = maybeHeap_ ? maybeHeap_->dataPointer() : NULL;
     }
     bool isLinked() const {
         return linked_;
     }
     uint8_t *maybeHeap() const {
         JS_ASSERT(linked_);
         return heapDatum();
     }
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -1163,136 +1163,138 @@ BacktrackingAllocator::populateSafepoint
     }
 
     return true;
 }
 
 void
 BacktrackingAllocator::dumpRegisterGroups()
 {
-    printf("Register groups:\n");
+    fprintf(stderr, "Register groups:\n");
     for (size_t i = 0; i < graph.numVirtualRegisters(); i++) {
         VirtualRegisterGroup *group = vregs[i].group();
         if (group && i == group->canonicalReg()) {
             for (size_t j = 0; j < group->registers.length(); j++)
-                printf(" v%u", group->registers[j]);
-            printf("\n");
+                fprintf(stderr, " v%u", group->registers[j]);
+            fprintf(stderr, "\n");
         }
     }
 }
 
 void
 BacktrackingAllocator::dumpLiveness()
 {
 #ifdef DEBUG
-    printf("Virtual Registers:\n");
+    fprintf(stderr, "Virtual Registers:\n");
 
     for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
         LBlock *block = graph.getBlock(blockIndex);
         MBasicBlock *mir = block->mir();
 
-        printf("\nBlock %lu", static_cast<unsigned long>(blockIndex));
+        fprintf(stderr, "\nBlock %lu", static_cast<unsigned long>(blockIndex));
         for (size_t i = 0; i < mir->numSuccessors(); i++)
-            printf(" [successor %u]", mir->getSuccessor(i)->id());
-        printf("\n");
+            fprintf(stderr, " [successor %u]", mir->getSuccessor(i)->id());
+        fprintf(stderr, "\n");
 
         for (size_t i = 0; i < block->numPhis(); i++) {
             LPhi *phi = block->getPhi(i);
 
-            printf("Phi v%u <-", phi->getDef(0)->virtualRegister());
+            fprintf(stderr, "[%u,%u Phi v%u <-",
+                    inputOf(phi).pos(), outputOf(phi).pos(),
+                    phi->getDef(0)->virtualRegister());
             for (size_t j = 0; j < phi->numOperands(); j++)
-                printf(" v%u", phi->getOperand(j)->toUse()->virtualRegister());
-            printf("\n");
+                fprintf(stderr, " v%u", phi->getOperand(j)->toUse()->virtualRegister());
+            fprintf(stderr, "]\n");
         }
 
         for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
             LInstruction *ins = *iter;
 
-            printf("[%u,%u %s]", inputOf(ins).pos(), outputOf(ins).pos(), ins->opName());
+            fprintf(stderr, "[%u,%u %s]", inputOf(ins).pos(), outputOf(ins).pos(), ins->opName());
 
             for (size_t i = 0; i < ins->numTemps(); i++) {
                 LDefinition *temp = ins->getTemp(i);
                 if (!temp->isBogusTemp())
-                    printf(" [temp v%u]", temp->virtualRegister());
+                    fprintf(stderr, " [temp v%u]", temp->virtualRegister());
             }
 
             for (size_t i = 0; i < ins->numDefs(); i++) {
                 LDefinition *def = ins->getDef(i);
-                printf(" [def v%u]", def->virtualRegister());
+                fprintf(stderr, " [def v%u]", def->virtualRegister());
             }
 
             for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
                 if (alloc->isUse())
-                    printf(" [use v%u]", alloc->toUse()->virtualRegister());
+                    fprintf(stderr, " [use v%u]", alloc->toUse()->virtualRegister());
             }
 
-            printf("\n");
+            fprintf(stderr, "\n");
         }
     }
 
-    printf("\nLive Ranges:\n\n");
+    fprintf(stderr, "\nLive Ranges:\n\n");
 
     for (size_t i = 0; i < AnyRegister::Total; i++)
-        printf("reg %s: %s\n", AnyRegister::FromCode(i).name(), IntervalString(fixedIntervals[i]));
+        fprintf(stderr, "reg %s: %s\n", AnyRegister::FromCode(i).name(), IntervalString(fixedIntervals[i]));
 
     for (size_t i = 0; i < graph.numVirtualRegisters(); i++) {
-        printf("v%lu:", static_cast<unsigned long>(i));
+        fprintf(stderr, "v%lu:", static_cast<unsigned long>(i));
         VirtualRegister &vreg = vregs[i];
         for (size_t j = 0; j < vreg.numIntervals(); j++) {
             if (j)
-                printf(" *");
-            printf("%s", IntervalString(vreg.getInterval(j)));
+                fprintf(stderr, " *");
+            fprintf(stderr, "%s", IntervalString(vreg.getInterval(j)));
         }
-        printf("\n");
+        fprintf(stderr, "\n");
     }
 
-    printf("\n");
+    fprintf(stderr, "\n");
 #endif // DEBUG
 }
 
 #ifdef DEBUG
 struct BacktrackingAllocator::PrintLiveIntervalRange
 {
     void operator()(const AllocatedRange &item)
     {
         if (item.range == item.interval->getRange(0)) {
-            printf("  v%u: %s\n",
+            fprintf(stderr, "  v%u: %s\n",
                    item.interval->hasVreg() ? item.interval->vreg() : 0,
                    IntervalString(item.interval));
         }
     }
 };
 #endif
 
 void
 BacktrackingAllocator::dumpAllocations()
 {
 #ifdef DEBUG
-    printf("Allocations:\n");
+    fprintf(stderr, "Allocations:\n");
 
     for (size_t i = 0; i < graph.numVirtualRegisters(); i++) {
-        printf("v%lu:", static_cast<unsigned long>(i));
+        fprintf(stderr, "v%lu:", static_cast<unsigned long>(i));
         VirtualRegister &vreg = vregs[i];
         for (size_t j = 0; j < vreg.numIntervals(); j++) {
             if (j)
-                printf(" *");
+                fprintf(stderr, " *");
             LiveInterval *interval = vreg.getInterval(j);
-            printf("%s :: %s", IntervalString(interval), interval->getAllocation()->toString());
+            fprintf(stderr, "%s :: %s", IntervalString(interval), interval->getAllocation()->toString());
         }
-        printf("\n");
+        fprintf(stderr, "\n");
     }
 
-    printf("\n");
+    fprintf(stderr, "\n");
 
     for (size_t i = 0; i < AnyRegister::Total; i++) {
-        printf("reg %s:\n", AnyRegister::FromCode(i).name());
+        fprintf(stderr, "reg %s:\n", AnyRegister::FromCode(i).name());
         registers[i].allocations.forEach(PrintLiveIntervalRange());
     }
 
-    printf("\n");
+    fprintf(stderr, "\n");
 #endif // DEBUG
 }
 
 bool
 BacktrackingAllocator::addLiveInterval(LiveIntervalVector &intervals, uint32_t vreg,
                                        CodePosition from, CodePosition to)
 {
     LiveInterval *interval = new LiveInterval(vreg, 0);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -10,16 +10,17 @@
 #include "jslibmath.h"
 
 #include "builtin/Eval.h"
 #include "jit/BaselineCompiler.h"
 #include "jit/BaselineHelpers.h"
 #include "jit/BaselineJIT.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
+#include "jit/Lowering.h"
 #include "jit/PerfSpewer.h"
 #include "jit/VMFunctions.h"
 
 #include "jsboolinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
 #include "vm/ScopeObject-inl.h"
@@ -5163,17 +5164,24 @@ ICSetElem_TypedArray::Compiler::generate
     regs = availableGeneralRegs(0);
     regs.takeUnchecked(obj);
     regs.takeUnchecked(key);
     regs.take(scratchReg);
     Register secondScratch = regs.takeAny();
 
     if (type_ == ScalarTypeRepresentation::TYPE_FLOAT32 || type_ == ScalarTypeRepresentation::TYPE_FLOAT64) {
         masm.ensureDouble(value, FloatReg0, &failure);
-        masm.storeToTypedFloatArray(type_, FloatReg0, dest);
+        if (LIRGenerator::allowFloat32Optimizations() &&
+            type_ == ScalarTypeRepresentation::TYPE_FLOAT32)
+        {
+            masm.convertDoubleToFloat(FloatReg0, ScratchFloatReg);
+            masm.storeToTypedFloatArray(type_, ScratchFloatReg, dest);
+        } else {
+            masm.storeToTypedFloatArray(type_, FloatReg0, dest);
+        }
         EmitReturnFromIC(masm);
     } else if (type_ == ScalarTypeRepresentation::TYPE_UINT8_CLAMPED) {
         Label notInt32;
         masm.branchTestInt32(Assembler::NotEqual, value, &notInt32);
         masm.unboxInt32(value, secondScratch);
         masm.clampIntToUint8(secondScratch, secondScratch);
 
         Label clamped;
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -55,16 +55,30 @@ SetElemICInspector::sawDenseWrite() cons
     for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
         if (stub->isSetElem_DenseAdd() || stub->isSetElem_Dense())
             return true;
     }
     return false;
 }
 
 bool
+SetElemICInspector::sawTypedArrayWrite() const
+{
+    if (!icEntry_)
+        return false;
+
+    // Check for a SetElem_TypedArray stub.
+    for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
+        if (stub->isSetElem_TypedArray())
+            return true;
+    }
+    return false;
+}
+
+bool
 BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, Vector<Shape *> &shapes)
 {
     // Return a list of shapes seen by the baseline IC for the current op.
     // An empty list indicates no shapes are known, or there was an uncacheable
     // access.
     JS_ASSERT(shapes.empty());
 
     if (!hasBaselineScript())
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -35,16 +35,17 @@ class SetElemICInspector : public ICInsp
   public:
     SetElemICInspector(BaselineInspector *inspector, jsbytecode *pc, ICEntry *icEntry)
       : ICInspector(inspector, pc, icEntry)
     { }
 
     bool sawOOBDenseWrite() const;
     bool sawOOBTypedArrayWrite() const;
     bool sawDenseWrite() const;
+    bool sawTypedArrayWrite() const;
 };
 
 class BaselineInspector
 {
   private:
     RootedScript script;
     ICEntry *prevLookedUpEntry;
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -152,99 +152,46 @@ static const VMFunctionsModal StringToNu
     FunctionInfo<StringToNumberFn>(StringToNumber),
     FunctionInfo<StringToNumberParFn>(StringToNumberPar));
 
 bool
 CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
 {
     ValueOperand operand = ToValue(lir, LValueToInt32::Input);
     Register output = ToRegister(lir->output());
-
-    Register tag = masm.splitTagForTest(operand);
-
-    Label done, simple, isInt32, isBool, isString, notDouble;
-    // Type-check switch.
+    FloatRegister temp = ToFloatRegister(lir->tempFloat());
+
     MDefinition *input;
     if (lir->mode() == LValueToInt32::NORMAL)
         input = lir->mirNormal()->input();
     else
         input = lir->mirTruncate()->input();
-    masm.branchEqualTypeIfNeeded(MIRType_Int32, input, tag, &isInt32);
-    masm.branchEqualTypeIfNeeded(MIRType_Boolean, input, tag, &isBool);
-    // Only convert strings to int if we are in a truncation context, like
-    // bitwise operations.
-    if (lir->mode() == LValueToInt32::TRUNCATE)
-        masm.branchEqualTypeIfNeeded(MIRType_String, input, tag, &isString);
-    masm.branchTestDouble(Assembler::NotEqual, tag, &notDouble);
-
-    // If the value is a double, see if it fits in a 32-bit int. We need to ask
-    // the platform-specific codegenerator to do this.
-    FloatRegister temp = ToFloatRegister(lir->tempFloat());
-    masm.unboxDouble(operand, temp);
-
-    Label fails, isDouble;
-    masm.bind(&isDouble);
+
+    Label fails;
     if (lir->mode() == LValueToInt32::TRUNCATE) {
-        if (!emitTruncateDouble(temp, output))
-            return false;
-    } else {
-        masm.convertDoubleToInt32(temp, output, &fails, lir->mirNormal()->canBeNegativeZero());
-    }
-    masm.jump(&done);
-
-    masm.bind(&notDouble);
-
-    if (lir->mode() == LValueToInt32::NORMAL) {
-        // If the value is not null, it's a string, object, or undefined,
-        // which we can't handle here.
-        masm.branchTestNull(Assembler::NotEqual, tag, &fails);
+        // We can only handle strings in truncation contexts, like bitwise
+        // operations.
+        if (input->mightBeType(MIRType_String)) {
+            Register stringReg = ToRegister(lir->temp());
+            OutOfLineCode *ool = oolCallVM(StringToNumberInfo, lir, (ArgList(), stringReg),
+                                           StoreFloatRegisterTo(temp));
+            if (!ool)
+                return false;
+
+            masm.truncateValueToInt32(operand, input, ool->entry(), ool->rejoin(), stringReg,
+                                      temp, output, &fails);
+        } else {
+            masm.truncateValueToInt32(operand, input, temp, output, &fails);
+        }
     } else {
-        // Test for object - then fallthrough to null, which will also handle
-        // undefined.
-        masm.branchEqualTypeIfNeeded(MIRType_Object, input, tag, &fails);
-    }
-
-    if (fails.used() && !bailoutFrom(&fails, lir->snapshot()))
-        return false;
-
-    // The value is null - just emit 0.
-    masm.mov(Imm32(0), output);
-    masm.jump(&done);
-
-    // Unbox a string, call StringToNumber to get a double back, and jump back
-    // to the snippet generated above about dealing with doubles.
-    if (isString.used()) {
-        masm.bind(&isString);
-        Register str = masm.extractString(operand, ToRegister(lir->temp()));
-        OutOfLineCode *ool = oolCallVM(StringToNumberInfo, lir, (ArgList(), str),
-                                       StoreFloatRegisterTo(temp));
-        if (!ool)
-            return false;
-
-        masm.jump(ool->entry());
-        masm.bind(ool->rejoin());
-        masm.jump(&isDouble);
-    }
-
-    // Just unbox a bool, the result is 0 or 1.
-    if (isBool.used()) {
-        masm.bind(&isBool);
-        masm.unboxBoolean(operand, output);
-        masm.jump(&done);
-    }
-
-    // Integers can be unboxed.
-    if (isInt32.used()) {
-        masm.bind(&isInt32);
-        masm.unboxInt32(operand, output);
-    }
-
-    masm.bind(&done);
-
-    return true;
+        masm.convertValueToInt32(operand, input, temp, output, &fails,
+                                 lir->mirNormal()->canBeNegativeZero());
+    }
+
+    return bailoutFrom(&fails, lir->snapshot());
 }
 
 static const double DoubleZero = 0.0;
 
 bool
 CodeGenerator::visitValueToDouble(LValueToDouble *lir)
 {
     MToDouble *mir = lir->mir();
@@ -298,23 +245,105 @@ CodeGenerator::visitValueToDouble(LValue
     masm.bind(&isDouble);
     masm.unboxDouble(operand, output);
     masm.bind(&done);
 
     return true;
 }
 
 bool
+CodeGenerator::visitValueToFloat32(LValueToFloat32 *lir)
+{
+    MToFloat32 *mir = lir->mir();
+    ValueOperand operand = ToValue(lir, LValueToFloat32::Input);
+    FloatRegister output = ToFloatRegister(lir->output());
+
+    Register tag = masm.splitTagForTest(operand);
+
+    Label isDouble, isInt32, isBool, isNull, isUndefined, done;
+    bool hasBoolean = false, hasNull = false, hasUndefined = false;
+
+    masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
+    masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
+
+    if (mir->conversion() != MToFloat32::NumbersOnly) {
+        masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
+        masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
+        hasBoolean = true;
+        hasUndefined = true;
+        if (mir->conversion() != MToFloat32::NonNullNonStringPrimitives) {
+            masm.branchTestNull(Assembler::Equal, tag, &isNull);
+            hasNull = true;
+        }
+    }
+
+    if (!bailout(lir->snapshot()))
+        return false;
+
+    if (hasNull) {
+        static float DoubleZeroFloat = DoubleZero;
+        masm.bind(&isNull);
+        masm.loadStaticFloat32(&DoubleZeroFloat, output);
+        masm.jump(&done);
+    }
+
+    if (hasUndefined) {
+        static float js_NaN_float = js_NaN;
+        masm.bind(&isUndefined);
+        masm.loadStaticFloat32(&js_NaN_float, output);
+        masm.jump(&done);
+    }
+
+    if (hasBoolean) {
+        masm.bind(&isBool);
+        masm.boolValueToFloat32(operand, output);
+        masm.jump(&done);
+    }
+
+    masm.bind(&isInt32);
+    masm.int32ValueToFloat32(operand, output);
+    masm.jump(&done);
+
+    masm.bind(&isDouble);
+    masm.unboxDouble(operand, output);
+    masm.convertDoubleToFloat(output, output);
+    masm.bind(&done);
+
+    return true;
+}
+
+bool
 CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir)
 {
     masm.convertInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
     return true;
 }
 
 bool
+CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble *lir)
+{
+    masm.convertFloatToDouble(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
+    return true;
+}
+
+bool
+CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32 *lir)
+{
+    masm.convertDoubleToFloat(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
+    return true;
+}
+
+bool
+CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32 *lir)
+{
+    masm.convertInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
+    return true;
+}
+
+bool
 CodeGenerator::visitDoubleToInt32(LDoubleToInt32 *lir)
 {
     Label fail;
     FloatRegister input = ToFloatRegister(lir->input());
     Register output = ToRegister(lir->output());
     masm.convertDoubleToInt32(input, output, &fail, lir->mir()->canBeNegativeZero());
     if (!bailoutFrom(&fail, lir->snapshot()))
         return false;
@@ -1307,31 +1336,46 @@ CodeGenerator::visitGuardThreadLocalObje
         return false;
 
     // branch to the OOL failure code if false is returned
     masm.branchIfFalseBool(ReturnReg, bail->entry());
     return true;
 }
 
 bool
-CodeGenerator::visitTypeBarrier(LTypeBarrier *lir)
-{
-    ValueOperand operand = ToValue(lir, LTypeBarrier::Input);
-    Register scratch = ToTempUnboxRegister(lir->temp());
+CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir)
+{
+    ValueOperand operand = ToValue(lir, LTypeBarrierV::Input);
+    Register scratch = ToTempRegisterOrInvalid(lir->temp());
 
     Label matched, miss;
     masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), scratch, &matched, &miss);
     masm.jump(&miss);
     if (!bailoutFrom(&miss, lir->snapshot()))
         return false;
     masm.bind(&matched);
     return true;
 }
 
 bool
+CodeGenerator::visitTypeBarrierO(LTypeBarrierO *lir)
+{
+    Register obj = ToRegister(lir->object());
+    Register scratch = ToTempRegisterOrInvalid(lir->temp());
+
+    Label matched, miss;
+    masm.guardObjectType(obj, lir->mir()->resultTypeSet(), scratch, &matched, &miss);
+    masm.jump(&miss);
+    if (!bailoutFrom(&miss, lir->snapshot()))
+        return false;
+    masm.bind(&matched);
+    return true;
+}
+
+bool
 CodeGenerator::visitMonitorTypes(LMonitorTypes *lir)
 {
     ValueOperand operand = ToValue(lir, LMonitorTypes::Input);
     Register scratch = ToTempUnboxRegister(lir->temp());
 
     Label matched, miss;
     masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &matched, &miss);
     masm.jump(&miss);
@@ -6062,39 +6106,41 @@ CodeGenerator::visitGetElementIC(OutOfLi
 }
 
 bool
 CodeGenerator::visitSetElementCacheV(LSetElementCacheV *ins)
 {
     Register obj = ToRegister(ins->object());
     Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
     Register temp = ToRegister(ins->temp());
+    FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
     ValueOperand index = ToValue(ins, LSetElementCacheV::Index);
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value));
 
-    SetElementIC cache(obj, unboxIndex, temp, index, value, ins->mir()->strict());
+    SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, ins->mir()->strict());
 
     return addCache(ins, allocateCache(cache));
 }
 
 bool
 CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
 {
     Register obj = ToRegister(ins->object());
     Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
     Register temp = ToRegister(ins->temp());
+    FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
     ValueOperand index = ToValue(ins, LSetElementCacheT::Index);
     ConstantOrRegister value;
     const LAllocation *tmp = ins->value();
     if (tmp->isConstant())
         value = *tmp->toConstant();
     else
         value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp));
 
-    SetElementIC cache(obj, unboxIndex, temp, index, value, ins->mir()->strict());
+    SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, ins->mir()->strict());
 
     return addCache(ins, allocateCache(cache));
 }
 
 typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue);
 const VMFunction SetElementIC::UpdateInfo =
     FunctionInfo<SetElementICFn>(SetElementIC::update);
 
@@ -6688,56 +6734,34 @@ CodeGenerator::visitClampDToUint8(LClamp
     Register output = ToRegister(lir->output());
     masm.clampDoubleToUint8(input, output);
     return true;
 }
 
 bool
 CodeGenerator::visitClampVToUint8(LClampVToUint8 *lir)
 {
-    ValueOperand input = ToValue(lir, LClampVToUint8::Input);
+    ValueOperand operand = ToValue(lir, LClampVToUint8::Input);
     FloatRegister tempFloat = ToFloatRegister(lir->tempFloat());
     Register output = ToRegister(lir->output());
-
-    Register tag = masm.splitTagForTest(input);
-
-    Label done;
-    Label isInt32, isDouble, isBoolean;
-    masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
-    masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
-    masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean);
-
-    // Undefined, null and objects are always 0.
-    Label isZero;
-    masm.branchTestUndefined(Assembler::Equal, tag, &isZero);
-    masm.branchTestNull(Assembler::Equal, tag, &isZero);
-    masm.branchTestObject(Assembler::Equal, tag, &isZero);
-
-    // Bailout for everything else (strings).
-    if (!bailout(lir->snapshot()))
+    MDefinition *input = lir->mir()->input();
+
+    Label fails;
+    if (input->mightBeType(MIRType_String)) {
+        OutOfLineCode *ool = oolCallVM(StringToNumberInfo, lir, (ArgList(), output),
+                                       StoreFloatRegisterTo(tempFloat));
+        masm.clampValueToUint8(operand, input, ool->entry(), ool->rejoin(), output,
+                               tempFloat, output, &fails);
+
+    } else {
+        masm.clampValueToUint8(operand, input, tempFloat, output, &fails);
+    }
+    if (!bailoutFrom(&fails, lir->snapshot()))
         return false;
 
-    masm.bind(&isInt32);
-    masm.unboxInt32(input, output);
-    masm.clampIntToUint8(output, output);
-    masm.jump(&done);
-
-    masm.bind(&isDouble);
-    masm.unboxDouble(input, tempFloat);
-    masm.clampDoubleToUint8(tempFloat, output);
-    masm.jump(&done);
-
-    masm.bind(&isBoolean);
-    masm.unboxBoolean(input, output);
-    masm.jump(&done);
-
-    masm.bind(&isZero);
-    masm.move32(Imm32(0), output);
-
-    masm.bind(&done);
     return true;
 }
 
 typedef bool (*OperatorInFn)(JSContext *, HandleValue, HandleObject, bool *);
 static const VMFunction OperatorInInfo = FunctionInfo<OperatorInFn>(OperatorIn);
 
 bool
 CodeGenerator::visitIn(LIn *ins)
@@ -7420,16 +7444,29 @@ CodeGenerator::visitAssertRangeD(LAssert
     FloatRegister input = ToFloatRegister(ins->input());
     FloatRegister temp = ToFloatRegister(ins->temp());
     Range *r = ins->range();
 
     return emitAssertRangeD(r, input, temp);
 }
 
 bool
+CodeGenerator::visitAssertRangeF(LAssertRangeF *ins)
+{
+    FloatRegister input = ToFloatRegister(ins->input());
+    FloatRegister temp = ToFloatRegister(ins->temp());
+    Range *r = ins->range();
+
+    masm.convertFloatToDouble(input, input);
+    bool success = emitAssertRangeD(r, input, temp);
+    masm.convertDoubleToFloat(input, input);
+    return success;
+}
+
+bool
 CodeGenerator::visitAssertRangeV(LAssertRangeV *ins)
 {
     Range *r = ins->range();
     const ValueOperand value = ToValue(ins, LAssertRangeV::Input);
     Register tag = masm.splitTagForTest(value);
     Label done;
 
     {
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -66,16 +66,20 @@ class CodeGenerator : public CodeGenerat
     bool visitDefFun(LDefFun *lir);
     bool visitOsrEntry(LOsrEntry *lir);
     bool visitOsrScopeChain(LOsrScopeChain *lir);
     bool visitStackArgT(LStackArgT *lir);
     bool visitStackArgV(LStackArgV *lir);
     bool visitMoveGroup(LMoveGroup *group);
     bool visitValueToInt32(LValueToInt32 *lir);
     bool visitValueToDouble(LValueToDouble *lir);
+    bool visitValueToFloat32(LValueToFloat32 *lir);
+    bool visitFloat32ToDouble(LFloat32ToDouble *lir);
+    bool visitDoubleToFloat32(LDoubleToFloat32 *lir);
+    bool visitInt32ToFloat32(LInt32ToFloat32 *lir);
     bool visitInt32ToDouble(LInt32ToDouble *lir);
     void emitOOLTestObject(Register objreg, Label *ifTruthy, Label *ifFalsy, Register scratch);
     bool visitTestOAndBranch(LTestOAndBranch *lir);
     bool visitTestVAndBranch(LTestVAndBranch *lir);
     bool visitFunctionDispatch(LFunctionDispatch *lir);
     bool visitTypeObjectDispatch(LTypeObjectDispatch *lir);
     bool visitIntToString(LIntToString *lir);
     bool visitDoubleToString(LDoubleToString *lir);
@@ -86,17 +90,18 @@ class CodeGenerator : public CodeGenerat
     bool visitLambdaForSingleton(LLambdaForSingleton *lir);
     bool visitLambdaPar(LLambdaPar *lir);
     bool visitPointer(LPointer *lir);
     bool visitSlots(LSlots *lir);
     bool visitStoreSlotV(LStoreSlotV *store);
     bool visitElements(LElements *lir);
     bool visitConvertElementsToDoubles(LConvertElementsToDoubles *lir);
     bool visitMaybeToDoubleElement(LMaybeToDoubleElement *lir);
-    bool visitTypeBarrier(LTypeBarrier *lir);
+    bool visitTypeBarrierV(LTypeBarrierV *lir);
+    bool visitTypeBarrierO(LTypeBarrierO *lir);
     bool visitMonitorTypes(LMonitorTypes *lir);
     bool visitPostWriteBarrierO(LPostWriteBarrierO *lir);
     bool visitPostWriteBarrierV(LPostWriteBarrierV *lir);
     bool visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier *ool);
     bool visitCallNative(LCallNative *call);
     bool emitCallInvokeFunction(LInstruction *call, Register callereg,
                                 uint32_t argc, uint32_t unusedStack);
     bool visitCallGeneric(LCallGeneric *call);
@@ -299,16 +304,17 @@ class CodeGenerator : public CodeGenerat
     bool visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic);
     bool visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic);
     bool visitBindNameIC(OutOfLineUpdateCache *ool, DataPtr<BindNameIC> &ic);
     bool visitNameIC(OutOfLineUpdateCache *ool, DataPtr<NameIC> &ic);
     bool visitCallsiteCloneIC(OutOfLineUpdateCache *ool, DataPtr<CallsiteCloneIC> &ic);
 
     bool visitAssertRangeI(LAssertRangeI *ins);
     bool visitAssertRangeD(LAssertRangeD *ins);
+    bool visitAssertRangeF(LAssertRangeF *ins);
     bool visitAssertRangeV(LAssertRangeV *ins);
 
     IonScriptCounts *extractUnassociatedScriptCounts() {
         IonScriptCounts *counts = unassociatedScriptCounts_;
         unassociatedScriptCounts_ = NULL;  // prevent delete in dtor
         return counts;
     }
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2591,17 +2591,17 @@ jit::UsesBeforeIonRecompile(JSScript *sc
     uint32_t loopDepth = GET_UINT8(pc);
     JS_ASSERT(loopDepth > 0);
     return minUses + loopDepth * 100;
 }
 
 void
 AutoFlushCache::updateTop(uintptr_t p, size_t len)
 {
-    IonContext *ictx = GetIonContext();
+    IonContext *ictx = MaybeGetIonContext();
     IonRuntime *irt = (ictx != NULL) ? ictx->runtime->ionRuntime() : NULL;
     if (!irt || !irt->flusher())
         JSC::ExecutableAllocator::cacheFlush((void*)p, len);
     else
         irt->flusher()->update(p, len);
 }
 
 AutoFlushCache::AutoFlushCache(const char *nonce, IonRuntime *rt)
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -7,16 +7,17 @@
 #include "jit/IonAnalysis.h"
 
 #include "jsanalyze.h"
 
 #include "jit/BaselineInspector.h"
 #include "jit/Ion.h"
 #include "jit/IonBuilder.h"
 #include "jit/LIR.h"
+#include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
 
 #include "jsinferinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
@@ -81,17 +82,17 @@ jit::EliminateDeadResumePointOperands(MI
             if (ins->isConstant())
                 continue;
 
             // Scanning uses does not give us sufficient information to tell
             // where instructions that are involved in box/unbox operations or
             // parameter passing might be live. Rewriting uses of these terms
             // in resume points may affect the interpreter's behavior. Rather
             // than doing a more sophisticated analysis, just ignore these.
-            if (ins->isUnbox() || ins->isParameter())
+            if (ins->isUnbox() || ins->isParameter() || ins->isTypeBarrier())
                 continue;
 
             // If the instruction's behavior has been constant folded into a
             // separate instruction, we can't determine precisely where the
             // instruction becomes dead and can't eliminate its uses.
             if (ins->isFolded())
                 continue;
 
@@ -395,57 +396,75 @@ class TypeAnalyzer
     bool respecialize(MPhi *phi, MIRType type);
     bool propagateSpecialization(MPhi *phi);
     bool specializePhis();
     void replaceRedundantPhi(MPhi *phi);
     void adjustPhiInputs(MPhi *phi);
     bool adjustInputs(MDefinition *def);
     bool insertConversions();
 
+    bool graphContainsFloat32();
+    bool markPhiConsumers();
+    bool markPhiProducers();
+    bool specializeValidFloatOps();
+    bool tryEmitFloatOperations();
+
   public:
     TypeAnalyzer(MIRGenerator *mir, MIRGraph &graph)
       : mir(mir), graph(graph)
     { }
 
     bool analyze();
 };
 
 } /* anonymous namespace */
 
 // Try to specialize this phi based on its non-cyclic inputs.
 static MIRType
 GuessPhiType(MPhi *phi)
 {
     MIRType type = MIRType_None;
+    bool convertibleToFloat32 = false;
     for (size_t i = 0, e = phi->numOperands(); i < e; i++) {
         MDefinition *in = phi->getOperand(i);
         if (in->isPhi()) {
             if (!in->toPhi()->triedToSpecialize())
                 continue;
             if (in->type() == MIRType_None) {
                 // The operand is a phi we tried to specialize, but we were
                 // unable to guess its type. propagateSpecialization will
                 // propagate the type to this phi when it becomes known.
                 continue;
             }
         }
         if (type == MIRType_None) {
             type = in->type();
+            if (in->isConstant())
+                convertibleToFloat32 = true;
             continue;
         }
         if (type != in->type()) {
             // Ignore operands which we've never observed.
             if (in->resultTypeSet() && in->resultTypeSet()->empty())
                 continue;
 
-            // Specialize phis with int32 and double operands as double.
-            if (IsNumberType(type) && IsNumberType(in->type()))
+            if (IsFloatType(type) && IsFloatType(in->type())) {
+                // Specialize phis with int32 and float32 operands as float32.
+                type = MIRType_Float32;
+            } else if (convertibleToFloat32 && in->type() == MIRType_Float32) {
+                // If we only saw constants before and encounter a Float32 value, promote previous
+                // constants to Float32
+                type = MIRType_Float32;
+            } else if (IsNumberType(type) && IsNumberType(in->type())) {
+                // Specialize phis with int32 and double operands as double.
                 type = MIRType_Double;
-            else
+                convertibleToFloat32 &= in->isConstant();
+            } else {
                 return MIRType_Value;
+            }
         }
     }
     return type;
 }
 
 bool
 TypeAnalyzer::respecialize(MPhi *phi, MIRType type)
 {
@@ -471,16 +490,23 @@ TypeAnalyzer::propagateSpecialization(MP
             // We tried to specialize this phi, but were unable to guess its
             // type. Now that we know the type of one of its operands, we can
             // specialize it.
             if (!respecialize(use, phi->type()))
                 return false;
             continue;
         }
         if (use->type() != phi->type()) {
+            // Specialize phis with int32 and float operands as floats.
+            if (IsFloatType(use->type()) && IsFloatType(phi->type())) {
+                if (!respecialize(use, MIRType_Float32))
+                    return false;
+                continue;
+            }
+
             // Specialize phis with int32 and double operands as double.
             if (IsNumberType(use->type()) && IsNumberType(phi->type())) {
                 if (!respecialize(use, MIRType_Double))
                     return false;
                 continue;
             }
 
             // This phi in our use chain can now no longer be specialized.
@@ -541,19 +567,34 @@ TypeAnalyzer::adjustPhiInputs(MPhi *phi)
             if (in->type() == phiType)
                 continue;
 
             if (in->isBox() && in->toBox()->input()->type() == phiType) {
                 phi->replaceOperand(i, in->toBox()->input());
             } else {
                 MInstruction *replacement;
 
-                if (phiType == MIRType_Double && in->type() == MIRType_Int32) {
+                if (phiType == MIRType_Double && IsFloatType(in->type())) {
                     // Convert int32 operands to double.
                     replacement = MToDouble::New(in);
+                } else if (phiType == MIRType_Float32) {
+                    if (in->type() == MIRType_Int32 || in->type() == MIRType_Double) {
+                        replacement = MToFloat32::New(in);
+                    } else {
+                        // See comment below
+                        if (in->type() != MIRType_Value) {
+                            MBox *box = MBox::New(in);
+                            in->block()->insertBefore(in->block()->lastIns(), box);
+                            in = box;
+                        }
+
+                        MUnbox *unbox = MUnbox::New(in, MIRType_Double, MUnbox::Fallible);
+                        in->block()->insertBefore(in->block()->lastIns(), unbox);
+                        replacement = MToFloat32::New(in);
+                    }
                 } else {
                     // If we know this branch will fail to convert to phiType,
                     // insert a box that'll immediately fail in the fallible unbox
                     // below.
                     if (in->type() != MIRType_Value) {
                         MBox *box = MBox::New(in);
                         in->block()->insertBefore(in->block()->lastIns(), box);
                         in = box;
@@ -578,18 +619,17 @@ TypeAnalyzer::adjustPhiInputs(MPhi *phi)
         if (in->type() == MIRType_Value)
             continue;
 
         if (in->isUnbox() && phi->typeIncludes(in->toUnbox()->input())) {
             // The input is being explicitly unboxed, so sneak past and grab
             // the original box.
             phi->replaceOperand(i, in->toUnbox()->input());
         } else {
-            MBox *box = MBox::New(in);
-            in->block()->insertBefore(in->block()->lastIns(), box);
+            MDefinition *box = BoxInputsPolicy::alwaysBoxAt(in->block()->lastIns(), in);
             phi->replaceOperand(i, box);
         }
     }
 }
 
 bool
 TypeAnalyzer::adjustInputs(MDefinition *def)
 {
@@ -645,19 +685,232 @@ TypeAnalyzer::insertConversions()
         for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
             if (!adjustInputs(*iter))
                 return false;
         }
     }
     return true;
 }
 
+// This function tries to emit Float32 specialized operations whenever it's possible.
+// MIR nodes are flagged as:
+// - Producers, when they can create Float32 that might need to be coerced into a Double.
+//   Loads in Float32 arrays and conversions to Float32 are producers.
+// - Consumers, when they can have Float32 as inputs and validate a legal use of a Float32.
+//   Stores in Float32 arrays and conversions to Float32 are consumers.
+// - Float32 commutative, when using the Float32 instruction instead of the Double instruction
+//   does not result in a compound loss of precision. This is the case for +, -, /, * with 2
+//   operands, for instance. However, an addition with 3 operands is not commutative anymore,
+//   so an intermediate coercion is needed.
+// Except for phis, all these flags are known after Ion building, so they cannot change during
+// the process.
+//
+// The idea behind the algorithm is easy: whenever we can prove that a commutative operation
+// has only producers as inputs and consumers as uses, we can specialize the operation as a
+// float32 operation. Otherwise, we have to convert all float32 inputs to doubles. Even
+// if a lot of conversions are produced, GVN will take care of eliminating the redundant ones.
+//
+// Phis have a special status. Phis need to be flagged as producers or consumers as they can
+// be inputs or outputs of commutative instructions. Fortunately, producers and consumers
+// properties are such that we can deduce the property using all non phis inputs first (which form
+// an initial phi graph) and then propagate all properties from one phi to another using a
+// fixed point algorithm. The algorithm is ensured to terminate as each iteration has less or as
+// many flagged phis as the previous iteration (so the worst steady state case is all phis being
+// flagged as false).
+//
+// In a nutshell, the algorithm applies three passes:
+// 1 - Determine which phis are consumers. Each phi gets an initial value by making a global AND on
+// all its non-phi inputs. Then each phi propagates its value to other phis. If after propagation,
+// the flag value changed, we have to reapply the algorithm on all phi operands, as a phi is a
+// consumer if all of its uses are consumers.
+// 2 - Determine which phis are producers. It's the same algorithm, except that we have to reapply
+// the algorithm on all phi uses, as a phi is a producer if all of its operands are producers.
+// 3 - Go through all commutative operations and ensure their inputs are all producers and their
+// uses are all consumers.
+bool
+TypeAnalyzer::markPhiConsumers()
+{
+    JS_ASSERT(phiWorklist_.empty());
+
+    // Iterate in postorder so worklist is initialized to RPO.
+    for (PostorderIterator block(graph.poBegin()); block != graph.poEnd(); ++block) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Consumer Phis - Initial state"))
+            return false;
+
+        for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); ++phi) {
+            JS_ASSERT(!phi->isInWorklist());
+            bool canConsumeFloat32 = true;
+            for (MUseDefIterator use(*phi); canConsumeFloat32 && use; use++) {
+                MDefinition *usedef = use.def();
+                canConsumeFloat32 &= usedef->isPhi() || usedef->canConsumeFloat32();
+            }
+            phi->setCanConsumeFloat32(canConsumeFloat32);
+            if (canConsumeFloat32 && !addPhiToWorklist(*phi))
+                return false;
+        }
+    }
+
+    while (!phiWorklist_.empty()) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Consumer Phis - Fixed point"))
+            return false;
+
+        MPhi *phi = popPhi();
+        JS_ASSERT(phi->canConsumeFloat32());
+
+        bool validConsumer = true;
+        for (MUseDefIterator use(phi); use; use++) {
+            MDefinition *def = use.def();
+            if (def->isPhi() && !def->canConsumeFloat32()) {
+                validConsumer = false;
+                break;
+            }
+        }
+
+        if (validConsumer)
+            continue;
+
+        // Propagate invalidated phis
+        phi->setCanConsumeFloat32(false);
+        for (size_t i = 0, e = phi->numOperands(); i < e; ++i) {
+            MDefinition *input = phi->getOperand(i);
+            if (input->isPhi() && !input->isInWorklist() && input->canConsumeFloat32())
+            {
+                if (!addPhiToWorklist(input->toPhi()))
+                    return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool
+TypeAnalyzer::markPhiProducers()
+{
+    JS_ASSERT(phiWorklist_.empty());
+
+    // Iterate in reverse postorder so worklist is initialized to PO.
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Producer Phis - initial state"))
+            return false;
+
+        for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); ++phi) {
+            JS_ASSERT(!phi->isInWorklist());
+            bool canProduceFloat32 = true;
+            for (size_t i = 0, e = phi->numOperands(); canProduceFloat32 && i < e; ++i) {
+                MDefinition *input = phi->getOperand(i);
+                canProduceFloat32 &= input->isPhi() || input->canProduceFloat32();
+            }
+            phi->setCanProduceFloat32(canProduceFloat32);
+            if (canProduceFloat32 && !addPhiToWorklist(*phi))
+                return false;
+        }
+    }
+
+    while (!phiWorklist_.empty()) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Producer Phis - Fixed point"))
+            return false;
+
+        MPhi *phi = popPhi();
+        JS_ASSERT(phi->canProduceFloat32());
+
+        bool validProducer = true;
+        for (size_t i = 0, e = phi->numOperands(); i < e; ++i) {
+            MDefinition *input = phi->getOperand(i);
+            if (input->isPhi() && !input->canProduceFloat32()) {
+                validProducer = false;
+                break;
+            }
+        }
+
+        if (validProducer)
+            continue;
+
+        // Propagate invalidated phis
+        phi->setCanProduceFloat32(false);
+        for (MUseDefIterator use(phi); use; use++) {
+            MDefinition *def = use.def();
+            if (def->isPhi() && !def->isInWorklist() && def->canProduceFloat32())
+            {
+                if (!addPhiToWorklist(def->toPhi()))
+                    return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool
+TypeAnalyzer::specializeValidFloatOps()
+{
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Instructions"))
+            return false;
+
+        for (MInstructionIterator ins(block->begin()); ins != block->end(); ++ins) {
+            if (!ins->isFloat32Commutative())
+                continue;
+
+            if (ins->type() == MIRType_Float32)
+                continue;
+
+            // This call will try to specialize the instruction iff all uses are consumers and
+            // all inputs are producers.
+            ins->trySpecializeFloat32();
+        }
+    }
+    return true;
+}
+
+bool
+TypeAnalyzer::graphContainsFloat32()
+{
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Graph contains Float32"))
+            return false;
+
+        for (MDefinitionIterator def(*block); def; def++) {
+            if (def->type() == MIRType_Float32)
+                return true;
+        }
+    }
+    return false;
+}
+
+bool
+TypeAnalyzer::tryEmitFloatOperations()
+{
+    // Backends that currently don't know how to generate Float32 specialized instructions
+    // shouldn't run this pass and just let all instructions as specialized for Double.
+    if (!LIRGenerator::allowFloat32Optimizations())
+        return true;
+
+    // Asm.js uses the ahead of time type checks to specialize operations, no need to check
+    // them again at this point.
+    if (mir->compilingAsmJS())
+        return true;
+
+    // Check ahead of time that there is at least one definition typed as Float32, otherwise we
+    // don't need this pass.
+    if (!graphContainsFloat32())
+        return true;
+
+    if (!markPhiConsumers())
+       return false;
+    if (!markPhiProducers())
+       return false;
+    if (!specializeValidFloatOps())
+       return false;
+    return true;
+}
+
 bool
 TypeAnalyzer::analyze()
 {
+    if (!tryEmitFloatOperations())
+        return false;
     if (!specializePhis())
         return false;
     if (!insertConversions())
         return false;
     return true;
 }
 
 bool
@@ -1281,31 +1534,37 @@ TryEliminateTypeBarrierFromTest(MTypeBar
     // Watch for code patterns similar to 'if (x.f) { ... = x.f }'.  If x.f
     // is either an object or null/undefined, there will be a type barrier on
     // the latter read as the null/undefined value is never realized there.
     // The type barrier can be eliminated, however, by looking at tests
     // performed on the result of the first operation that filter out all
     // types that have been seen in the first access but not the second.
 
     // A test 'if (x.f)' filters both null and undefined.
-    if (test->getOperand(0) == barrier->input() && direction == TRUE_BRANCH) {
+
+    // Disregard the possible unbox added before the Typebarrier for checking.
+    MDefinition *input = barrier->input();
+    if (input->isUnbox() && input->toUnbox()->mode() == MUnbox::TypeBarrier)
+        input = input->toUnbox()->input();
+
+    if (test->getOperand(0) == input && direction == TRUE_BRANCH) {
         *eliminated = true;
         barrier->replaceAllUsesWith(barrier->input());
         return;
     }
 
     if (!test->getOperand(0)->isCompare())
         return;
 
     MCompare *compare = test->getOperand(0)->toCompare();
     MCompare::CompareType compareType = compare->compareType();
 
     if (compareType != MCompare::Compare_Undefined && compareType != MCompare::Compare_Null)
         return;
-    if (compare->getOperand(0) != barrier->input())
+    if (compare->getOperand(0) != input)
         return;
 
     JSOp op = compare->jsop();
     JS_ASSERT(op == JSOP_EQ || op == JSOP_STRICTEQ ||
               op == JSOP_NE || op == JSOP_STRICTNE);
 
     if ((direction == TRUE_BRANCH) != (op == JSOP_NE || op == JSOP_STRICTNE))
         return;
@@ -1327,16 +1586,23 @@ TryEliminateTypeBarrierFromTest(MTypeBar
 static bool
 TryEliminateTypeBarrier(MTypeBarrier *barrier, bool *eliminated)
 {
     JS_ASSERT(!*eliminated);
 
     const types::StackTypeSet *barrierTypes = barrier->resultTypeSet();
     const types::StackTypeSet *inputTypes = barrier->input()->resultTypeSet();
 
+    // Disregard the possible unbox added before the Typebarrier.
+    if (barrier->input()->isUnbox() &&
+        barrier->input()->toUnbox()->mode() == MUnbox::TypeBarrier)
+    {
+        inputTypes = barrier->input()->toUnbox()->input()->resultTypeSet();
+    }
+
     if (!barrierTypes || !inputTypes)
         return true;
 
     bool filtersNull = barrierTypes->filtersType(inputTypes, types::Type::NullType());
     bool filtersUndefined = barrierTypes->filtersType(inputTypes, types::Type::UndefinedType());
 
     if (!filtersNull && !filtersUndefined)
         return true;
@@ -1759,16 +2025,20 @@ bool
 jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
                                 types::TypeObject *type, HandleObject baseobj,
                                 Vector<types::TypeNewScript::Initializer> *initializerList)
 {
     // When invoking 'new' on the specified script, try to find some properties
     // which will definitely be added to the created object before it has a
     // chance to escape and be accessed elsewhere.
 
+    if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx)) {
+        return false;
+    }
+
     if (!fun->nonLazyScript()->compileAndGo)
         return true;
 
     if (!fun->nonLazyScript()->ensureHasTypes(cx))
         return false;
 
     types::TypeScript::SetThis(cx, fun->nonLazyScript(), types::Type::ObjectType(type));
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -925,56 +925,58 @@ IonBuilder::addOsrValueTypeBarrier(uint3
         if (!typeSet)
             return false;
         MInstruction *barrier = MTypeBarrier::New(def, typeSet);
         osrBlock->insertBefore(osrBlock->lastIns(), barrier);
         osrBlock->rewriteSlot(slot, barrier);
         def = barrier;
     }
 
-    switch (type) {
-      case MIRType_Boolean:
-      case MIRType_Int32:
-      case MIRType_Double:
-      case MIRType_String:
-      case MIRType_Object:
-      {
-        MUnbox *unbox = MUnbox::New(def, type, MUnbox::Fallible);
-        osrBlock->insertBefore(osrBlock->lastIns(), unbox);
-        osrBlock->rewriteSlot(slot, unbox);
-        def = unbox;
-        break;
-      }
-
-      case MIRType_Null:
-      {
-        MConstant *c = MConstant::New(NullValue());
-        osrBlock->insertBefore(osrBlock->lastIns(), c);
-        osrBlock->rewriteSlot(slot, c);
-        def = c;
-        break;
-      }
-
-      case MIRType_Undefined:
-      {
-        MConstant *c = MConstant::New(UndefinedValue());
-        osrBlock->insertBefore(osrBlock->lastIns(), c);
-        osrBlock->rewriteSlot(slot, c);
-        def = c;
-        break;
-      }
-
-      case MIRType_Magic:
-        JS_ASSERT(lazyArguments_);
-        osrBlock->rewriteSlot(slot, lazyArguments_);
-        def = lazyArguments_;
-        break;
-
-      default:
-        break;
+    if (type != def->type()) {
+        switch (type) {
+          case MIRType_Boolean:
+          case MIRType_Int32:
+          case MIRType_Double:
+          case MIRType_String:
+          case MIRType_Object:
+          {
+            MUnbox *unbox = MUnbox::New(def, type, MUnbox::Fallible);
+            osrBlock->insertBefore(osrBlock->lastIns(), unbox);
+            osrBlock->rewriteSlot(slot, unbox);
+            def = unbox;
+            break;
+          }
+
+          case MIRType_Null:
+          {
+            MConstant *c = MConstant::New(NullValue());
+            osrBlock->insertBefore(osrBlock->lastIns(), c);
+            osrBlock->rewriteSlot(slot, c);
+            def = c;
+            break;
+          }
+
+          case MIRType_Undefined:
+          {
+            MConstant *c = MConstant::New(UndefinedValue());
+            osrBlock->insertBefore(osrBlock->lastIns(), c);
+            osrBlock->rewriteSlot(slot, c);
+            def = c;
+            break;
+          }
+
+          case MIRType_Magic:
+            JS_ASSERT(lazyArguments_);
+            osrBlock->rewriteSlot(slot, lazyArguments_);
+            def = lazyArguments_;
+            break;
+
+          default:
+            break;
+        }
     }
 
     JS_ASSERT(def == osrBlock->getSlot(slot));
     return true;
 }
 
 bool
 IonBuilder::maybeAddOsrTypeBarriers()
@@ -3749,19 +3751,19 @@ IonBuilder::inlineScriptedCall(CallInfo 
     BaselineInspector inspector(cx, target->nonLazyScript());
 
     // Improve type information of |this| when not set.
     if (callInfo.constructing() && !callInfo.thisArg()->resultTypeSet()) {
         types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript);
         if (!types->unknown()) {
             MTypeBarrier *barrier = MTypeBarrier::New(callInfo.thisArg(), cloneTypeSet(types), Bailout_Normal);
             current->add(barrier);
-            MUnbox *unbox = MUnbox::New(barrier, MIRType_Object, MUnbox::Infallible);
-            current->add(unbox);
-            callInfo.setThis(unbox);
+            callInfo.setThis(barrier);
+            // object or missing
+            JS_ASSERT(barrier->type() == MIRType_Object || barrier->type() == MIRType_Value);
         }
     }
 
     // Start inlining.
     LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
     CompileInfo *info = alloc->new_<CompileInfo>(calleeScript.get(), target,
                                                  (jsbytecode *)NULL, callInfo.constructing(),
                                                  this->info().executionMode());
@@ -4046,29 +4048,22 @@ IonBuilder::getInlineableGetPropertyCach
         if (cache->hasUses())
             return NULL;
         if (!CanInlineGetPropertyCache(cache, thisDef))
             return NULL;
         return cache;
     }
 
     // Optimize away the following common pattern:
-    // MUnbox[MIRType_Object, Infallible] <- MTypeBarrier <- MGetPropertyCache
-    if (funcDef->isUnbox()) {
-        MUnbox *unbox = funcDef->toUnbox();
-        if (unbox->mode() != MUnbox::Infallible)
-            return NULL;
-        if (unbox->hasUses())
+    // MTypeBarrier[MIRType_Object] <- MGetPropertyCache
+    if (funcDef->isTypeBarrier()) {
+        MTypeBarrier *barrier = funcDef->toTypeBarrier();
+        if (barrier->hasUses())
             return NULL;
-        if (!unbox->input()->isTypeBarrier())
-            return NULL;
-
-        MTypeBarrier *barrier = unbox->input()->toTypeBarrier();
-        // Test if usecount() > 1
-        if (!barrier->hasOneUse())
+        if (barrier->type() != MIRType_Object)
             return NULL;
         if (!barrier->input()->isGetPropertyCache())
             return NULL;
 
         MGetPropertyCache *cache = barrier->input()->toGetPropertyCache();
         if (cache->hasUses() && !cache->hasOneUse())
             return NULL;
         if (!CanInlineGetPropertyCache(cache, thisDef))
@@ -4172,26 +4167,26 @@ IonBuilder::inlineGenericFallback(JSFunc
 
 bool
 IonBuilder::inlineTypeObjectFallback(CallInfo &callInfo, MBasicBlock *dispatchBlock,
                                      MTypeObjectDispatch *dispatch, MGetPropertyCache *cache,
                                      MBasicBlock **fallbackTarget)
 {
     // Getting here implies the following:
     // 1. The call function is an MGetPropertyCache, or an MGetPropertyCache
-    //    followed by an MTypeBarrier, followed by an MUnbox.
-    JS_ASSERT(callInfo.fun()->isGetPropertyCache() || callInfo.fun()->isUnbox());
+    //    followed by an MTypeBarrier.
+    JS_ASSERT(callInfo.fun()->isGetPropertyCache() || callInfo.fun()->isTypeBarrier());
 
     // 2. The MGetPropertyCache has inlineable cases by guarding on the TypeObject.
     JS_ASSERT(dispatch->numCases() > 0);
 
-    // 3. The MGetPropertyCache (and, if applicable, MTypeBarrier and MUnbox) only
+    // 3. The MGetPropertyCache (and, if applicable, MTypeBarrier) only
     //    have at most a single use.
     JS_ASSERT_IF(callInfo.fun()->isGetPropertyCache(), !cache->hasUses());
-    JS_ASSERT_IF(callInfo.fun()->isUnbox(), cache->hasOneUse());
+    JS_ASSERT_IF(callInfo.fun()->isTypeBarrier(), cache->hasOneUse());
 
     // This means that no resume points yet capture the MGetPropertyCache,
     // so everything from the MGetPropertyCache up until the call is movable.
     // We now move the MGetPropertyCache and friends into a fallback path.
 
     // Create a new CallInfo to track modified state within the fallback path.
     CallInfo fallbackInfo(cx, callInfo.constructing());
     if (!fallbackInfo.init(callInfo))
@@ -4235,29 +4230,24 @@ IonBuilder::inlineTypeObjectFallback(Cal
     JS_ASSERT(checkObject == cache->object());
 
     // Move the MGetPropertyCache and friends into the getPropBlock.
     if (fallbackInfo.fun()->isGetPropertyCache()) {
         JS_ASSERT(fallbackInfo.fun()->toGetPropertyCache() == cache);
         getPropBlock->addFromElsewhere(cache);
         getPropBlock->push(cache);
     } else {
-        MUnbox *unbox = callInfo.fun()->toUnbox();
-        JS_ASSERT(unbox->input()->isTypeBarrier());
-        JS_ASSERT(unbox->type() == MIRType_Object);
-        JS_ASSERT(unbox->mode() == MUnbox::Infallible);
-
-        MTypeBarrier *typeBarrier = unbox->input()->toTypeBarrier();
-        JS_ASSERT(typeBarrier->input()->isGetPropertyCache());
-        JS_ASSERT(typeBarrier->input()->toGetPropertyCache() == cache);
+        MTypeBarrier *barrier = callInfo.fun()->toTypeBarrier();
+        JS_ASSERT(barrier->type() == MIRType_Object);
+        JS_ASSERT(barrier->input()->isGetPropertyCache());
+        JS_ASSERT(barrier->input()->toGetPropertyCache() == cache);
 
         getPropBlock->addFromElsewhere(cache);
-        getPropBlock->addFromElsewhere(typeBarrier);
-        getPropBlock->addFromElsewhere(unbox);
-        getPropBlock->push(unbox);
+        getPropBlock->addFromElsewhere(barrier);
+        getPropBlock->push(barrier);
     }
 
     // Construct an end block with the correct resume point.
     MBasicBlock *preCallBlock = newBlock(getPropBlock, pc, preCallResumePoint);
     if (!preCallBlock)
         return false;
     getPropBlock->end(MGoto::New(preCallBlock));
 
@@ -6202,48 +6192,24 @@ IonBuilder::pushTypeBarrier(MInstruction
         return true;
     }
 
     if (observed->unknown())
         return true;
 
     current->pop();
 
-    MInstruction *barrier;
-    JSValueType type = observed->getKnownTypeTag();
-
-    // An unbox instruction isn't enough to capture JSVAL_TYPE_OBJECT. Use a type
-    // barrier followed by an infallible unbox.
-    bool isObject = false;
-    if (type == JSVAL_TYPE_OBJECT && !observed->hasType(types::Type::AnyObjectType())) {
-        type = JSVAL_TYPE_UNKNOWN;
-        isObject = true;
-    }
-
-    switch (type) {
-      case JSVAL_TYPE_UNKNOWN:
-      case JSVAL_TYPE_UNDEFINED:
-      case JSVAL_TYPE_NULL:
-        barrier = MTypeBarrier::New(ins, cloneTypeSet(observed));
-        current->add(barrier);
-
-        if (type == JSVAL_TYPE_UNDEFINED)
-            return pushConstant(UndefinedValue());
-        if (type == JSVAL_TYPE_NULL)
-            return pushConstant(NullValue());
-        if (isObject) {
-            barrier = MUnbox::New(barrier, MIRType_Object, MUnbox::Infallible);
-            current->add(barrier);
-        }
-        break;
-      default:
-        MUnbox::Mode mode = ins->isEffectful() ? MUnbox::TypeBarrier : MUnbox::TypeGuard;
-        barrier = MUnbox::New(ins, MIRTypeFromValueType(type), mode);
-        current->add(barrier);
-    }
+    MInstruction *barrier = MTypeBarrier::New(ins, cloneTypeSet(observed));
+    current->add(barrier);
+
+    if (barrier->type() == MIRType_Undefined)
+        return pushConstant(UndefinedValue());
+    if (barrier->type() == MIRType_Null)
+        return pushConstant(NullValue());
+
     current->push(barrier);
     return true;
 }
 
 bool
 IonBuilder::getStaticName(HandleObject staticObject, HandlePropertyName name, bool *psucceeded)
 {
     JS_ASSERT(staticObject->is<GlobalObject>() || staticObject->is<CallObject>());
@@ -6334,16 +6300,17 @@ bool
 jit::TypeSetIncludes(types::TypeSet *types, MIRType input, types::TypeSet *inputTypes)
 {
     switch (input) {
       case MIRType_Undefined:
       case MIRType_Null:
       case MIRType_Boolean:
       case MIRType_Int32:
       case MIRType_Double:
+      case MIRType_Float32:
       case MIRType_String:
       case MIRType_Magic:
         return types->hasType(types::Type::PrimitiveType(ValueTypeFromMIRType(input)));
 
       case MIRType_Object:
         return types->unknownObject() || (inputTypes && inputTypes->isSubset(types));
 
       case MIRType_Value:
@@ -7052,16 +7019,18 @@ IonBuilder::jsop_getelem_typed(MDefiniti
           case ScalarTypeRepresentation::TYPE_UINT16:
           case ScalarTypeRepresentation::TYPE_INT32:
             knownType = MIRType_Int32;
             break;
           case ScalarTypeRepresentation::TYPE_UINT32:
             knownType = allowDouble ? MIRType_Double : MIRType_Int32;
             break;
           case ScalarTypeRepresentation::TYPE_FLOAT32:
+            knownType = (LIRGenerator::allowFloat32Optimizations()) ? MIRType_Float32 : MIRType_Double;
+            break;
           case ScalarTypeRepresentation::TYPE_FLOAT64:
             knownType = MIRType_Double;
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Unknown typed array type");
         }
 
         // Get the length.
@@ -7290,19 +7259,19 @@ IonBuilder::setElemTryCache(bool *emitte
     if (!object->mightBeType(MIRType_Object))
         return true;
 
     if (!index->mightBeType(MIRType_Int32) && !index->mightBeType(MIRType_String))
         return true;
 
     // TODO: Bug 876650: remove this check:
     // Temporary disable the cache if non dense native,
-    // untill the cache supports more ics
+    // until the cache supports more ics
     SetElemICInspector icInspect(inspector->setElemICInspector(pc));
-    if (!icInspect.sawDenseWrite())
+    if (!icInspect.sawDenseWrite() && !icInspect.sawTypedArrayWrite())
         return true;
 
     bool needsBarrier;
     if (PropertyWriteNeedsTypeBarrier(cx, current, &object, NULL, &value, /* canModify = */ true,
                                       &needsBarrier))
     {
         return false;
     }
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1112,26 +1112,23 @@ template <class GetPropCache>
 static GetPropertyIC::NativeGetPropCacheability
 CanAttachNativeGetProp(typename GetPropCache::Context cx, const GetPropCache &cache,
                        HandleObject obj, HandlePropertyName name,
                        MutableHandleObject holder, MutableHandleShape shape)
 {
     if (!obj || !obj->isNative())
         return GetPropertyIC::CanAttachNone;
 
-    // If the cache is idempotent or parallel, watch out for resolve hooks or
-    // non-native objects on the proto chain. We check this before calling
-    // lookupProperty, to make sure no effectful lookup hooks or resolve hooks
-    // are called.
-    if (cache.lookupNeedsIdempotentChain() && !obj->hasIdempotentProtoChain())
+    // The lookup needs to be universally pure, otherwise we risk calling hooks out
+    // of turn. We don't mind doing this even when purity isn't required, because we
+    // only miss out on shape hashification, which is only a temporary perf cost.
+    // The limits were arbitrarily set, anyways.
+    if (!LookupPropertyPure(obj, NameToId(name), holder.address(), shape.address()))
         return GetPropertyIC::CanAttachNone;
 
-    if (!GetPropCache::doPropertyLookup(cx, obj, name, holder, shape))
-        return GetPropertyIC::CanAttachError;
-
     RootedScript script(cx);
     jsbytecode *pc;
     cache.getScriptedLocation(&script, &pc);
     if (IsCacheableGetPropReadSlot(obj, holder, shape) ||
         IsCacheableNoProperty(obj, holder, shape, pc, cache.output()))
     {
         // TI infers the possible types of native object properties. There's one
         // edge case though: for singleton objects it does not add the initial
@@ -1204,18 +1201,16 @@ GetPropertyIC::tryAttachNative(JSContext
     JS_ASSERT(canAttachStub());
     JS_ASSERT(!*emitted);
 
     RootedShape shape(cx);
     RootedObject holder(cx);
 
     NativeGetPropCacheability type =
         CanAttachNativeGetProp(cx, *this, obj, name, &holder, &shape);
-    if (type == CanAttachError)
-        return false;
     if (type == CanAttachNone)
         return true;
 
     *emitted = true;
 
     MacroAssembler masm(cx);
     RepatchStubAppender attacher(*this);
     const char *attachKind;
@@ -1419,18 +1414,16 @@ GetPropertyIC::tryAttachDOMProxyUnshadow
 
     RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull());
     RootedObject holder(cx);
     RootedShape shape(cx);
 
     NativeGetPropCacheability canCache =
         CanAttachNativeGetProp(cx, *this, checkObj, name, &holder, &shape);
 
-    if (canCache == CanAttachError)
-        return false;
     if (canCache == CanAttachNone)
         return true;
 
     *emitted = true;
 
     if (resetNeeded) {
         // If we know that we have a DoesntShadowUnique object, then
         // we reset the cache to clear out an existing IC for the object
@@ -1893,17 +1886,16 @@ GetPropertyParIC::update(ForkJoinSlice *
 
             {
                 RootedShape shape(cx);
                 RootedObject holder(cx);
                 RootedPropertyName name(cx, cache.name());
 
                 GetPropertyIC::NativeGetPropCacheability canCache =
                     CanAttachNativeGetProp(cx, cache, obj, name, &holder, &shape);
-                JS_ASSERT(canCache != GetPropertyIC::CanAttachError);
 
                 if (canCache == GetPropertyIC::CanAttachReadSlot) {
                     if (!cache.attachReadSlot(cx, ion, obj, holder, shape))
                         return TP_FATAL;
                     attachedStub = true;
                 }
 
                 if (!attachedStub && canCache == GetPropertyIC::CanAttachArrayLength) {
@@ -1941,64 +1933,63 @@ IonCache::reset()
 }
 
 void
 IonCache::destroy()
 {
 }
 
 bool
-SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion,
-                                    HandleObject obj, HandleShape shape)
+SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj,
+                                    HandleShape shape, bool checkTypeset)
 {
     JS_ASSERT(obj->isNative());
 
     MacroAssembler masm(cx);
     RepatchStubAppender attacher(*this);
 
     Label failures;
     masm.branchPtr(Assembler::NotEqual,
                    Address(object(), JSObject::offsetOfShape()),
                    ImmGCPtr(obj->lastProperty()), &failures);
 
     // Guard that the incoming value is in the type set for the property
     // if a type barrier is required.
-    if (needsTypeBarrier() && !value().constant()) {
+    if (needsTypeBarrier()) {
         // We can't do anything that would change the HeapTypeSet, so
         // just guard that it's already there.
 
         // Obtain and guard on the TypeObject of the object.
         types::TypeObject *type = obj->getType(cx);
         masm.branchPtr(Assembler::NotEqual,
                        Address(object(), JSObject::offsetOfType()),
                        ImmGCPtr(type), &failures);
 
-        if (!type->unknownProperties()) {
+        if (checkTypeset) {
             TypedOrValueRegister valReg = value().reg();
             RootedId id(cx, types::IdToTypeId(AtomToId(name())));
             types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, id);
             JS_ASSERT(propTypes);
-
-            if (!propTypes->unknown()) {
-                Label barrierSuccess;
-                Label barrierFailure;
-
-                Register scratchReg = object();
-                masm.push(scratchReg);
-
-                masm.guardTypeSet(valReg, propTypes, scratchReg,
-                                  &barrierSuccess, &barrierFailure);
-
-                masm.bind(&barrierFailure);
-                masm.pop(object());
-                masm.jump(&failures);
-
-                masm.bind(&barrierSuccess);
-                masm.pop(object());
-            }
+            JS_ASSERT(!propTypes->unknown());
+
+            Label barrierSuccess;
+            Label barrierFailure;
+
+            Register scratchReg = object();
+            masm.push(scratchReg);
+
+            masm.guardTypeSet(valReg, propTypes, scratchReg,
+                                &barrierSuccess, &barrierFailure);
+
+            masm.bind(&barrierFailure);
+            masm.pop(object());
+            masm.jump(&failures);
+
+            masm.bind(&barrierSuccess);
+            masm.pop(object());
         }
     }
 
     if (obj->isFixedSlot(shape->slot())) {
         Address addr(object(), JSObject::getFixedSlotOffset(shape->slot()));
 
         if (cx->zone()->needsBarrier())
             masm.callPreBarrier(addr, MIRType_Value);
@@ -2054,16 +2045,23 @@ IsCacheableSetPropCallPropertyOp(HandleO
         return false;
 
     if (shape->hasDefaultSetter())
         return false;
 
     if (shape->hasSetterValue())
         return false;
 
+    // Despite the vehement claims of Shape.h that writable() is only
+    // relevant for data descriptors, some PropertyOp setters care
+    // desperately about its value. The flag should be always true, apart
+    // from these rare instances.
+    if (!shape->writable())
+        return false;
+
     return true;
 }
 
 static bool
 EmitCallProxySet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
                  HandleId propId, RegisterSet liveRegs, Register object,
                  ConstantOrRegister value, void *returnAddr, bool strict)
 {
@@ -2593,17 +2591,17 @@ SetPropertyIC::attachNativeAdding(JSCont
 
     attacher.jumpNextStub(masm);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "adding");
 }
 
 static bool
 IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject obj,
-                        HandleId id, MutableHandleShape pshape)
+                        HandleId id, MutableHandleShape pshape, bool *checkTypeset)
 {
     if (!obj->isNative())
         return false;
 
     Shape *shape = obj->nativeLookup(cx, id);
 
     if (!shape)
         return false;
@@ -2612,30 +2610,49 @@ IsPropertySetInlineable(JSContext *cx, c
         return false;
 
     if (!shape->hasDefaultSetter())
         return false;
 
     if (!shape->writable())
         return false;
 
+    bool shouldCheck = false;
     types::TypeObject *type = obj->getType(cx);
     if (cache.needsTypeBarrier() && !type->unknownProperties()) {
         RootedId typeId(cx, types::IdToTypeId(id));
         types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, typeId);
         if (!propTypes)
             return false;
-        if (cache.value().constant() && !propTypes->unknown()) {
-            // If the input is a constant, then don't bother if the barrier will always fail.
-            if (!propTypes->hasType(types::GetValueType(cache.value().value())))
-                return false;
+        if (!propTypes->unknown()) {
+            shouldCheck = true;
+            ConstantOrRegister val = cache.value();
+            if (val.constant()) {
+                // If the input is a constant, then don't bother if the barrier will always fail.
+                if (!propTypes->hasType(types::GetValueType(cache.value().value())))
+                    return false;
+                shouldCheck = false;
+            } else {
+                TypedOrValueRegister reg = val.reg();
+                // We can do the same trick as above for primitive types of specialized registers.
+                // TIs handling of objects is complicated enough to warrant a runtime
+                // check, as we can't statically handle the case where the typeset
+                // contains the specific object, but doesn't have ANYOBJECT set.
+                if (reg.hasTyped() && reg.type() != MIRType_Object) {
+                    JSValueType valType = ValueTypeFromMIRType(reg.type());
+                    if (!propTypes->hasType(types::Type::PrimitiveType(valType)))
+                        return false;
+                    shouldCheck = false;
+                }
+            }
         }
     }
 
     pshape.set(shape);
+    *checkTypeset = shouldCheck;
 
     return true;
 }
 
 static bool
 IsPropertyAddInlineable(JSContext *cx, HandleObject obj, HandleId id, uint32_t oldSlots,
                         MutableHandleShape pShape)
 {
@@ -2732,18 +2749,19 @@ SetPropertyIC::update(JSContext *cx, siz
 
             if (!addedSetterStub && !cache.hasGenericProxyStub()) {
                 if (!cache.attachGenericProxy(cx, ion, returnAddr))
                     return false;
                 addedSetterStub = true;
             }
         }
         RootedShape shape(cx);
-        if (!addedSetterStub && IsPropertySetInlineable(cx, cache, obj, id, &shape)) {
-            if (!cache.attachNativeExisting(cx, ion, obj, shape))
+        bool checkTypeset;
+        if (!addedSetterStub && IsPropertySetInlineable(cx, cache, obj, id, &shape, &checkTypeset)) {
+            if (!cache.attachNativeExisting(cx, ion, obj, shape, checkTypeset))
                 return false;
             addedSetterStub = true;
         } else if (!addedSetterStub) {
             RootedObject holder(cx);
             if (!JSObject::lookupProperty(cx, obj, name, &holder, &shape))
                 return false;
 
             if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) ||
@@ -2802,19 +2820,16 @@ GetElementIC::attachGetProp(JSContext *c
     JS_ASSERT(index().reg().hasValue());
 
     RootedObject holder(cx);
     RootedShape shape(cx);
 
     GetPropertyIC::NativeGetPropCacheability canCache =
         CanAttachNativeGetProp(cx, *this, obj, name, &holder, &shape);
 
-    if (canCache == GetPropertyIC::CanAttachError)
-        return false;
-
     if (canCache != GetPropertyIC::CanAttachReadSlot) {
         IonSpew(IonSpew_InlineCaches, "GETELEM uncacheable property");
         return true;
     }
 
     JS_ASSERT(idval.isString());
 
     Label failures;
@@ -2923,19 +2938,19 @@ GetElementIC::canAttachTypedArrayElement
     // way to accept float typed arrays for now is to return a Value type.
     int arrayType = obj->as<TypedArrayObject>().type();
     bool floatOutput = arrayType == ScalarTypeRepresentation::TYPE_FLOAT32 ||
                        arrayType == ScalarTypeRepresentation::TYPE_FLOAT64;
     return !floatOutput || output.hasValue();
 }
 
 static void
-GenerateTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                          TypedArrayObject *tarr, const Value &idval, Register object,
-                          ConstantOrRegister index, TypedOrValueRegister output)
+GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
+                             TypedArrayObject *tarr, const Value &idval, Register object,
+                             ConstantOrRegister index, TypedOrValueRegister output)
 {
     JS_ASSERT(GetElementIC::canAttachTypedArrayElement(tarr, idval, output));
 
     Label failures;
 
     // The array type is the object within the table of typed array classes.
     int arrayType = tarr->type();
 
@@ -3031,17 +3046,17 @@ GenerateTypedArrayElement(JSContext *cx,
 }
 
 bool
 GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr,
                                       const Value &idval)
 {
     MacroAssembler masm(cx);
     RepatchStubAppender attacher(*this);
-    GenerateTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
+    GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
     return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
 }
 
 bool
 GetElementIC::attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj)
 {
     JS_ASSERT(obj->is<ArgumentsObject>());
 
@@ -3345,26 +3360,134 @@ SetElementIC::attachDenseElement(JSConte
     masm.bind(&outOfBounds);
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 
     setHasDenseStub();
     return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
 }
 
+static bool
+GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
+                             TypedArrayObject *tarr, Register object,
+                             ValueOperand indexVal, ConstantOrRegister value,
+                             Register tempUnbox, Register temp, FloatRegister tempFloat)
+{
+    Label failures, done, popObjectAndFail;
+
+    // Guard on the shape.
+    Shape *shape = tarr->lastProperty();
+    if (!shape)
+        return false;
+    masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
+
+    // Ensure the index is an int32.
+    masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures);
+    Register index = masm.extractInt32(indexVal, tempUnbox);
+
+    // Guard on the length.
+    Address length(object, TypedArrayObject::lengthOffset());
+    masm.unboxInt32(length, temp);
+    masm.branch32(Assembler::BelowOrEqual, temp, index, &done);
+
+    // Load the elements vector.
+    Register elements = temp;
+    masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elements);
+
+    // Set the value.
+    int arrayType = tarr->type();
+    int width = TypedArrayObject::slotWidth(arrayType);
+    BaseIndex target(elements, index, ScaleFromElemWidth(width));
+
+    if (arrayType == ScalarTypeRepresentation::TYPE_FLOAT32 ||
+        arrayType == ScalarTypeRepresentation::TYPE_FLOAT64)
+    {
+        if (!masm.convertConstantOrRegisterToDouble(cx, value, tempFloat, &failures))
+            return false;
+        masm.storeToTypedFloatArray(arrayType, tempFloat, target);
+    } else {
+        // On x86 we only have 6 registers available to use, so reuse the object
+        // register to compute the intermediate value to store and restore it
+        // afterwards.
+        masm.push(object);
+
+        if (arrayType == ScalarTypeRepresentation::TYPE_UINT8_CLAMPED) {
+            if (!masm.clampConstantOrRegisterToUint8(cx, value, tempFloat, object,
+                                                     &popObjectAndFail))
+            {
+                return false;
+            }
+        } else {
+            if (!masm.truncateConstantOrRegisterToInt32(cx, value, tempFloat, object,
+                                                        &popObjectAndFail))
+            {
+                return false;
+            }
+        }
+        masm.storeToTypedIntArray(arrayType, object, target);
+
+        masm.pop(object);
+    }
+
+    // Out-of-bound writes jump here as they are no-ops.
+    masm.bind(&done);
+    attacher.jumpRejoin(masm);
+
+    if (popObjectAndFail.used()) {
+        masm.bind(&popObjectAndFail);
+        masm.pop(object);
+    }
+
+    masm.bind(&failures);
+    attacher.jumpNextStub(masm);
+    return true;
+}
+
+/* static */ bool
+SetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval, const Value &value)
+{
+    // Don't bother attaching stubs for assigning strings and objects.
+    return (obj->is<TypedArrayObject>() && idval.isInt32() &&
+            !value.isString() && !value.isObject());
+}
+
+bool
+SetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr)
+{
+    MacroAssembler masm(cx);
+    RepatchStubAppender attacher(*this);
+    if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr,
+                                      object(), index(), value(),
+                                      tempToUnboxIndex(), temp(), tempFloat()))
+    {
+        return false;
+    }
+
+    return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
+}
+
 bool
 SetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
                      HandleValue idval, HandleValue value)
 {
     IonScript *ion = GetTopIonJSScript(cx)->ionScript();
     SetElementIC &cache = ion->getCache(cacheIndex).toSetElement();
 
-    if (cache.canAttachStub() && !cache.hasDenseStub() && IsElementSetInlineable(obj, idval)) {
-        if (!cache.attachDenseElement(cx, ion, obj, idval))
-            return false;
+    bool attachedStub = false;
+    if (cache.canAttachStub()) {
+        if (!cache.hasDenseStub() && IsElementSetInlineable(obj, idval)) {
+            if (!cache.attachDenseElement(cx, ion, obj, idval))
+                return false;
+            attachedStub = true;
+        }
+        if (!attachedStub && canAttachTypedArrayElement(obj, idval, value)) {
+            TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
+            if (!cache.attachTypedArrayElement(cx, ion, tarr))
+                return false;
+        }
     }
 
     if (!SetObjectElement(cx, obj, idval, value, cache.strict()))
         return false;
     return true;
 }
 
 void
@@ -3406,17 +3529,17 @@ GetElementParIC::attachDenseElement(Lock
 }
 
 bool
 GetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
                                          TypedArrayObject *tarr, const Value &idval)
 {
     MacroAssembler masm(cx);
     DispatchStubPrepender attacher(*this);
-    GenerateTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
+    GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
 }
 
 ParallelResult
 GetElementParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
                         HandleValue idval, MutableHandleValue vp)
 {
     AutoFlushCache afc("GetElementParCache", slice->runtime()->ionRuntime());
@@ -3453,17 +3576,16 @@ GetElementParIC::update(ForkJoinSlice *s
                 GetElementIC::canAttachGetProp(obj, idval, id))
             {
                 RootedShape shape(cx);
                 RootedObject holder(cx);
                 RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
 
                 GetPropertyIC::NativeGetPropCacheability canCache =
                     CanAttachNativeGetProp(cx, cache, obj, name, &holder, &shape);
-                JS_ASSERT(canCache != GetPropertyIC::CanAttachError);
 
                 if (canCache == GetPropertyIC::CanAttachReadSlot)
                 {
                     if (!cache.attachReadSlot(cx, ion, obj, idval, name, holder, shape))
                         return TP_FATAL;
                     attachedStub = true;
                 }
             }
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -589,32 +589,24 @@ class GetPropertyIC : public RepatchIonC
     }
     void getLocationInfo(uint32_t *index, uint32_t *num) const {
         JS_ASSERT(idempotent());
         *index = locationsIndex_;
         *num = numLocations_;
     }
 
     enum NativeGetPropCacheability {
-        CanAttachError,
         CanAttachNone,
         CanAttachReadSlot,
         CanAttachArrayLength,
         CanAttachCallGetter
     };
 
     // Helpers for CanAttachNativeGetProp
     typedef JSContext * Context;
-    static bool doPropertyLookup(Context cx, HandleObject obj, HandlePropertyName name,
-                                 MutableHandleObject holder, MutableHandleShape shape) {
-        return JSObject::lookupProperty(cx, obj, name, holder, shape);
-    }
-    bool lookupNeedsIdempotentChain() const {
-        return idempotent();
-    }
     bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const;
     bool allowArrayLength(Context cx, HandleObject obj) const;
 
     // Attach the proper stub, if possible
     bool tryAttachStub(JSContext *cx, IonScript *ion, HandleObject obj,
                        HandlePropertyName name, void *returnAddr, bool *emitted);
     bool tryAttachProxy(JSContext *cx, IonScript *ion, HandleObject obj,
                         HandlePropertyName name, void *returnAddr, bool *emitted);
@@ -682,17 +674,18 @@ class SetPropertyIC : public RepatchIonC
     }
     bool needsTypeBarrier() const {
         return needsTypeBarrier_;
     }
     bool hasGenericProxyStub() const {
         return hasGenericProxyStub_;
     }
 
-    bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape);
+    bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj,
+                              HandleShape shape, bool checkTypeset);
     bool attachSetterCall(JSContext *cx, IonScript *ion, HandleObject obj,
                           HandleObject holder, HandleShape shape, void *returnAddr);
     bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldshape,
                             HandleShape newshape, HandleShape propshape);
     bool attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAddr);
     bool attachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj,
                                 void *returnAddr);
     bool attachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj,
@@ -756,23 +749,18 @@ class GetElementIC : public RepatchIonCa
     }
     void setHasDenseStub() {
         JS_ASSERT(!hasDenseStub());
         hasDenseStub_ = true;
     }
 
     // Helpers for CanAttachNativeGetProp
     typedef JSContext * Context;
-    static bool doPropertyLookup(Context cx, HandleObject obj, HandlePropertyName name,
-                                 MutableHandleObject holder, MutableHandleShape shape) {
-        return JSObject::lookupProperty(cx, obj, name, holder, shape);
-    }
     bool allowGetters() const { return false; }
     bool allowArrayLength(Context, HandleObject) const { return false; }
-    bool lookupNeedsIdempotentChain() const { return false; }
     bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const {
         return monitoredResult();
     }
 
     static bool canAttachGetProp(JSObject *obj, const Value &idval, jsid id);
     static bool canAttachDenseElement(JSObject *obj, const Value &idval);
     static bool canAttachTypedArrayElement(JSObject *obj, const Value &idval,
                                            TypedOrValueRegister output);
@@ -800,29 +788,31 @@ class GetElementIC : public RepatchIonCa
 };
 
 class SetElementIC : public RepatchIonCache
 {
   protected:
     Register object_;
     Register tempToUnboxIndex_;
     Register temp_;
+    FloatRegister tempFloat_;
     ValueOperand index_;
     ConstantOrRegister value_;
     bool strict_;
 
     bool hasDenseStub_ : 1;
 
   public:
     SetElementIC(Register object, Register tempToUnboxIndex, Register temp,
-                 ValueOperand index, ConstantOrRegister value,
+                 FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
                  bool strict)
       : object_(object),
         tempToUnboxIndex_(tempToUnboxIndex),
         temp_(temp),
+        tempFloat_(tempFloat),
         index_(index),
         value_(value),
         strict_(strict),
         hasDenseStub_(false)
     {
     }
 
     CACHE_HEADER(SetElement)
@@ -833,16 +823,19 @@ class SetElementIC : public RepatchIonCa
         return object_;
     }
     Register tempToUnboxIndex() const {
         return tempToUnboxIndex_;
     }
     Register temp() const {
         return temp_;
     }
+    FloatRegister tempFloat() const {
+        return tempFloat_;
+    }
     ValueOperand index() const {
         return index_;
     }
     ConstantOrRegister value() const {
         return value_;
     }
     bool strict() const {
         return strict_;
@@ -851,21 +844,24 @@ class SetElementIC : public RepatchIonCa
     bool hasDenseStub() const {
         return hasDenseStub_;
     }
     void setHasDenseStub() {
         JS_ASSERT(!hasDenseStub());
         hasDenseStub_ = true;
     }
 
+    static bool canAttachTypedArrayElement(JSObject *obj, const Value &idval, const Value &value);
+
     bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval);
+    bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr);
 
     static bool
     update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
-                HandleValue value);
+           HandleValue value);
 };
 
 class BindNameIC : public RepatchIonCache
 {
   protected:
     Register scopeChain_;
     PropertyName *name_;
     Register output_;
@@ -1040,21 +1036,16 @@ class GetPropertyParIC : public Parallel
         return output_;
     }
     bool hasTypedArrayLengthStub() const {
         return hasTypedArrayLengthStub_;
     }
 
     // CanAttachNativeGetProp Helpers
     typedef LockedJSContext & Context;
-    static bool doPropertyLookup(Context cx, HandleObject obj, HandlePropertyName name,
-                                 MutableHandleObject holder, MutableHandleShape shape) {
-        return LookupPropertyPure(obj, NameToId(name), holder.address(), shape.address());
-    }
-    bool lookupNeedsIdempotentChain() const { return true; }
     bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; }
     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);
@@ -1100,21 +1091,16 @@ class GetElementParIC : public ParallelI
         return output_;
     }
     bool monitoredResult() const {
         return monitoredResult_;
     }
 
     // CanAttachNativeGetProp Helpers
     typedef LockedJSContext & Context;
-    static bool doPropertyLookup(Context cx, HandleObject obj, HandlePropertyName name,
-                                 MutableHandleObject holder, MutableHandleShape shape) {
-        return LookupPropertyPure(obj, NameToId(name), holder.address(), shape.address());
-    }
-    bool lookupNeedsIdempotentChain() const { return true; }
     bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; }
     bool allowGetters() const { return false; }
     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,
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -1236,16 +1236,32 @@ SnapshotIterator::slotReadable(const Slo
 
 Value
 SnapshotIterator::slotValue(const Slot &slot)
 {
     switch (slot.mode()) {
       case SnapshotReader::DOUBLE_REG:
         return DoubleValue(machine_.read(slot.floatReg()));
 
+      case SnapshotReader::FLOAT32_REG:
+      {
+        double asDouble = machine_.read(slot.floatReg());
+        // The register contains the encoding of a float32. We just read
+        // the bits without making any conversion.
+        float asFloat = *(float*) &asDouble;
+        return DoubleValue(asFloat);
+      }
+
+      case SnapshotReader::FLOAT32_STACK:
+      {
+        double asDouble = ReadFrameDoubleSlot(fp_, slot.stackSlot());
+        float asFloat = *(float*) &asDouble; // no conversion, see comment above.
+        return DoubleValue(asFloat);
+      }
+
       case SnapshotReader::TYPED_REG:
         return FromTypedPayload(slot.knownType(), machine_.read(slot.reg()));
 
       case SnapshotReader::TYPED_STACK:
       {
         JSValueType type = slot.knownType();
         if (type == JSVAL_TYPE_DOUBLE)
             return DoubleValue(ReadFrameDoubleSlot(fp_, slot.stackSlot()));
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -10,16 +10,17 @@
 #include "jsprf.h"
 
 #include "builtin/TypeRepresentation.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/BaselineRegisters.h"
+#include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "vm/ForkJoin.h"
 
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 
 #include "vm/Shape-inl.h"
 
@@ -91,23 +92,42 @@ MacroAssembler::guardTypeSet(const Sourc
         branchTestMagic(Equal, tag, matched);
 
     if (types->hasType(types::Type::AnyObjectType())) {
         branchTestObject(Equal, tag, matched);
     } else if (types->getObjectCount()) {
         JS_ASSERT(scratch != InvalidReg);
         branchTestObject(NotEqual, tag, miss);
         Register obj = extractObject(address, scratch);
+        guardObjectType(obj, types, scratch, matched, miss);
+    }
+}
 
-        unsigned count = types->getObjectCount();
-        for (unsigned i = 0; i < count; i++) {
-            if (JSObject *object = types->getSingleObject(i))
-                branchPtr(Equal, obj, ImmGCPtr(object), matched);
-        }
+template <typename TypeSet> void
+MacroAssembler::guardObjectType(Register obj, const TypeSet *types,
+                                Register scratch, Label *matched, Label *miss)
+{
+    JS_ASSERT(!types->unknown());
+    JS_ASSERT(!types->hasType(types::Type::AnyObjectType()));
+    JS_ASSERT(types->getObjectCount());
+    JS_ASSERT(scratch != InvalidReg);
 
+    // Note: Some platforms give the same register for obj and scratch.
+    // Make sure when writing to scratch, the obj register isn't used anymore!
+
+    bool hasTypeObjects = false;
+    unsigned count = types->getObjectCount();
+    for (unsigned i = 0; i < count; i++) {
+        if (JSObject *object = types->getSingleObject(i))
+            branchPtr(Equal, obj, ImmGCPtr(object), matched);
+        else if (types->getTypeObject(i))
+            hasTypeObjects = true;
+    }
+
+    if (hasTypeObjects) {
         loadPtr(Address(obj, JSObject::offsetOfType()), scratch);
 
         for (unsigned i = 0; i < count; i++) {
             if (types::TypeObject *object = types->getTypeObject(i))
                 branchPtr(Equal, scratch, ImmGCPtr(object), matched);
         }
     }
 }
@@ -137,16 +157,23 @@ template void MacroAssembler::guardTypeS
 template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::TypeSet *types,
                                            Register scratch, Label *matched, Label *miss);
 
 template void MacroAssembler::guardTypeSet(const Address &address, const TypeWrapper *types,
                                            Register scratch, Label *matched, Label *miss);
 template void MacroAssembler::guardTypeSet(const ValueOperand &value, const TypeWrapper *types,
                                            Register scratch, Label *matched, Label *miss);
 
+template void MacroAssembler::guardObjectType(Register obj, const types::StackTypeSet *types,
+                                              Register scratch, Label *matched, Label *miss);
+template void MacroAssembler::guardObjectType(Register obj, const types::TypeSet *types,
+                                              Register scratch, Label *matched, Label *miss);
+template void MacroAssembler::guardObjectType(Register obj, const TypeWrapper *types,
+                                              Register scratch, Label *matched, Label *miss);
+
 template void MacroAssembler::guardType(const Address &address, types::Type type,
                                         Register scratch, Label *matched, Label *miss);
 template void MacroAssembler::guardType(const ValueOperand &value, types::Type type,
                                         Register scratch, Label *matched, Label *miss);
 
 void
 MacroAssembler::PushRegsInMask(RegisterSet set)
 {
@@ -273,16 +300,51 @@ MacroAssembler::moveNurseryPtr(const Imm
 {
 #ifdef JSGC_GENERATIONAL
     if (ptr.value && gc::IsInsideNursery(GetIonContext()->cx->runtime(), (void *)ptr.value))
         embedsNurseryPointers_ = true;
 #endif
     movePtr(ptr, reg);
 }
 
+template<typename S, typename T>
+static void
+StoreToTypedFloatArray(MacroAssembler &masm, int arrayType, const S &value, const T &dest) {
+    switch (arrayType) {
+      case ScalarTypeRepresentation::TYPE_FLOAT32:
+        if (LIRGenerator::allowFloat32Optimizations()) {
+            masm.storeFloat(value, dest);
+        } else {
+#ifdef JS_MORE_DETERMINISTIC
+            // See the comment in ToDoubleForTypedArray.
+            masm.canonicalizeDouble(value);
+#endif
+            masm.convertDoubleToFloat(value, ScratchFloatReg);
+            masm.storeFloat(ScratchFloatReg, dest);
+        }
+        break;
+      case ScalarTypeRepresentation::TYPE_FLOAT64:
+#ifdef JS_MORE_DETERMINISTIC
+        // See the comment in ToDoubleForTypedArray.
+        masm.canonicalizeDouble(value);
+#endif
+        masm.storeDouble(value, dest);
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
+    }
+}
+
+void MacroAssembler::storeToTypedFloatArray(int arrayType, const FloatRegister &value, const BaseIndex &dest) {
+    StoreToTypedFloatArray(*this, arrayType, value, dest);
+}
+void MacroAssembler::storeToTypedFloatArray(int arrayType, const FloatRegister &value, const Address &dest) {
+    StoreToTypedFloatArray(*this, arrayType, value, dest);
+}
+
 template<typename T>
 void
 MacroAssembler::loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp,
                                    Label *fail)
 {
     switch (arrayType) {
       case ScalarTypeRepresentation::TYPE_INT8:
         load8SignExtend(src, dest.gpr());
@@ -306,21 +368,26 @@ MacroAssembler::loadFromTypedArray(int a
             convertUInt32ToDouble(temp, dest.fpu());
         } else {
             load32(src, dest.gpr());
             test32(dest.gpr(), dest.gpr());
             j(Assembler::Signed, fail);
         }
         break;
       case ScalarTypeRepresentation::TYPE_FLOAT32:
-      case ScalarTypeRepresentation::TYPE_FLOAT64:
-        if (arrayType == ScalarTypeRepresentation::TYPE_FLOAT32)
+        if (LIRGenerator::allowFloat32Optimizations()) {
+            loadFloat(src, dest.fpu());
+            canonicalizeFloat(dest.fpu());
+        } else {
             loadFloatAsDouble(src, dest.fpu());
-        else
-            loadDouble(src, dest.fpu());
+            canonicalizeDouble(dest.fpu());
+        }
+        break;
+      case ScalarTypeRepresentation::TYPE_FLOAT64:
+        loadDouble(src, dest.fpu());
         canonicalizeDouble(dest.fpu());
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
     }
 }
 
 template void MacroAssembler::loadFromTypedArray(int arrayType, const Address &src, AnyRegister dest,
@@ -364,16 +431,21 @@ MacroAssembler::loadFromTypedArray(int a
             bind(&done);
         } else {
             // Bailout if the value does not fit in an int32.
             j(Assembler::Signed, fail);
             tagValue(JSVAL_TYPE_INT32, temp, dest);
         }
         break;
       case ScalarTypeRepresentation::TYPE_FLOAT32:
+        loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(), NULL);
+        if (LIRGenerator::allowFloat32Optimizations())
+            convertFloatToDouble(ScratchFloatReg, ScratchFloatReg);
+        boxDouble(ScratchFloatReg, dest);
+        break;
       case ScalarTypeRepresentation::TYPE_FLOAT64:
         loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(), NULL);
         boxDouble(ScratchFloatReg, dest);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
     }
 }
@@ -1299,16 +1371,17 @@ MacroAssembler::convertInt32ValueToDoubl
 {
     branchTestInt32(Assembler::NotEqual, address, done);
     unboxInt32(address, scratch);
     convertInt32ToDouble(scratch, ScratchFloatReg);
     storeDouble(ScratchFloatReg, address);
 }
 
 static const double DoubleZero = 0.0;
+static const double DoubleOne  = 1.0;
 
 void
 MacroAssembler::convertValueToDouble(ValueOperand value, FloatRegister output, Label *fail)
 {
     Register tag = splitTagForTest(value);
 
     Label isDouble, isInt32, isBool, isNull, done;
 
@@ -1334,49 +1407,55 @@ MacroAssembler::convertValueToDouble(Val
     int32ValueToDouble(value, output);
     jump(&done);
 
     bind(&isDouble);
     unboxDouble(value, output);
     bind(&done);
 }
 
-void
-MacroAssembler::convertValueToInt32(ValueOperand value, FloatRegister temp,
-                                    Register output, Label *fail)
+bool
+MacroAssembler::convertValueToDouble(JSContext *cx, const Value &v, FloatRegister output, Label *fail)
 {
-    Register tag = splitTagForTest(value);
-
-    Label done, simple, isInt32, isBool, isDouble;
+    if (v.isNumber() || v.isString()) {
+        double d;
+        if (v.isNumber())
+            d = v.toNumber();
+        else if (!StringToNumber(cx, v.toString(), &d))
+            return false;
 
-    branchTestInt32(Assembler::Equal, tag, &isInt32);
-    branchTestBoolean(Assembler::Equal, tag, &isBool);
-    branchTestDouble(Assembler::Equal, tag, &isDouble);
-    branchTestNull(Assembler::NotEqual, tag, fail);
+        if (d == js_NaN)
+            loadStaticDouble(&js_NaN, output);
+        else
+            loadConstantDouble(d, output);
 
-    // The value is null - just emit 0.
-    mov(Imm32(0), output);
-    jump(&done);
+        return true;
+    }
 
-    // Try converting double into integer
-    bind(&isDouble);
-    unboxDouble(value, temp);
-    convertDoubleToInt32(temp, output, fail, /* -0 check */ false);
-    jump(&done);
+    if (v.isBoolean()) {
+        if (v.toBoolean())
+            loadStaticDouble(&DoubleOne, output);
+        else
+            loadStaticDouble(&DoubleZero, output);
+        return true;
+    }
 
-    // Just unbox a bool, the result is 0 or 1.
-    bind(&isBool);
-    unboxBoolean(value, output);
-    jump(&done);
+    if (v.isNull()) {
+        loadStaticDouble(&DoubleZero, output);
+        return true;
+    }
 
-    // Integers can be unboxed.
-    bind(&isInt32);
-    unboxInt32(value, output);
+    if (v.isUndefined()) {
+        loadStaticDouble(&js_NaN, output);
+        return true;
+    }
 
-    bind(&done);
+    JS_ASSERT(v.isObject());
+    jump(fail);
+    return true;
 }
 
 void
 MacroAssembler::PushEmptyRooted(VMFunction::RootType rootType)
 {
     switch (rootType) {
       case VMFunction::RootNone:
         MOZ_ASSUME_UNREACHABLE("Handle must have root type");
@@ -1408,16 +1487,261 @@ MacroAssembler::popRooted(VMFunction::Ro
         Pop(cellReg);
         break;
       case VMFunction::RootValue:
         Pop(valueReg);
         break;
     }
 }
 
+bool
+MacroAssembler::convertConstantOrRegisterToDouble(JSContext *cx, ConstantOrRegister src,
+                                                  FloatRegister output, Label *fail)
+{
+    if (src.constant())
+        return convertValueToDouble(cx, src.value(), output, fail);
+
+    convertTypedOrValueToDouble(src.reg(), output, fail);
+    return true;
+}
+
+void
+MacroAssembler::convertTypedOrValueToDouble(TypedOrValueRegister src, FloatRegister output,
+                                            Label *fail)
+{
+    if (src.hasValue()) {
+        convertValueToDouble(src.valueReg(), output, fail);
+        return;
+    }
+
+    switch (src.type()) {
+      case MIRType_Null:
+        loadStaticDouble(&DoubleZero, output);
+        break;
+      case MIRType_Boolean:
+      case MIRType_Int32:
+        convertInt32ToDouble(src.typedReg().gpr(), output);
+        break;
+      case MIRType_Double:
+        if (src.typedReg().fpu() != output)
+            moveDouble(src.typedReg().fpu(), output);
+        break;
+      case MIRType_Object:
+      case MIRType_String:
+        jump(fail);
+        break;
+      case MIRType_Undefined:
+        loadStaticDouble(&js_NaN, output);
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Bad MIRType");
+    }
+}
+
+void
+MacroAssembler::convertDoubleToInt(FloatRegister src, Register output, Label *fail,
+                                   IntConversionBehavior behavior)
+{
+    switch (behavior) {
+      case IntConversion_Normal:
+      case IntConversion_NegativeZeroCheck:
+        convertDoubleToInt32(src, output, fail, behavior == IntConversion_NegativeZeroCheck);
+        break;
+      case IntConversion_Truncate:
+        branchTruncateDouble(src, output, fail);
+        break;
+      case IntConversion_ClampToUint8:
+        clampDoubleToUint8(src, output);
+        break;
+    }
+}
+
+void
+MacroAssembler::convertValueToInt(ValueOperand value, MDefinition *maybeInput,
+                                  Label *handleStringEntry, Label *handleStringRejoin,
+                                  Register stringReg, FloatRegister temp, Register output,
+                                  Label *fail, IntConversionBehavior behavior)
+{
+    Register tag = splitTagForTest(value);
+    bool handleStrings = (behavior == IntConversion_Truncate ||
+                          behavior == IntConversion_ClampToUint8) &&
+                         handleStringEntry &&
+                         handleStringRejoin;
+    bool zeroObjects = behavior == IntConversion_ClampToUint8;
+
+    Label done, isInt32, isBool, isDouble, isNull, isString;
+
+    branchEqualTypeIfNeeded(MIRType_Int32, maybeInput, tag, &isInt32);
+    branchEqualTypeIfNeeded(MIRType_Boolean, maybeInput, tag, &isBool);
+    branchEqualTypeIfNeeded(MIRType_Double, maybeInput, tag, &isDouble);
+
+    // If we are not truncating, we fail for anything that's not
+    // null. Otherwise we might be able to handle strings and objects.
+    switch (behavior) {
+      case IntConversion_Normal:
+      case IntConversion_NegativeZeroCheck:
+        branchTestNull(Assembler::NotEqual, tag, fail);
+        break;
+
+      case IntConversion_Truncate:
+      case IntConversion_ClampToUint8:
+        branchEqualTypeIfNeeded(MIRType_Null, maybeInput, tag, &isNull);
+        if (handleStrings)
+            branchEqualTypeIfNeeded(MIRType_String, maybeInput, tag, &isString);
+        if (zeroObjects)
+            branchEqualTypeIfNeeded(MIRType_Object, maybeInput, tag, &isNull);
+        branchTestUndefined(Assembler::NotEqual, tag, fail);
+        break;
+    }
+
+    // The value is null or undefined in truncation contexts - just emit 0.
+    if (isNull.used())
+        bind(&isNull);
+    mov(Imm32(0), output);
+    jump(&done);
+
+    // Try converting a string into a double, then jump to the double case.
+    if (handleStrings) {
+        bind(&isString);
+        unboxString(value, stringReg);
+        jump(handleStringEntry);
+    }
+
+    // Try converting double into integer.
+    if (isDouble.used() || handleStrings) {
+        if (isDouble.used()) {
+            bind(&isDouble);
+            unboxDouble(value, temp);
+        }
+
+        if (handleStrings)
+            bind(handleStringRejoin);
+
+        convertDoubleToInt(temp, output, fail, behavior);
+        jump(&done);
+    }
+
+    // Just unbox a bool, the result is 0 or 1.
+    if (isBool.used()) {
+        bind(&isBool);
+        unboxBoolean(value, output);
+        jump(&done);
+    }
+
+    // Integers can be unboxed.
+    if (isInt32.used()) {
+        bind(&isInt32);
+        unboxInt32(value, output);
+        if (behavior == IntConversion_ClampToUint8)
+            clampIntToUint8(output, output);
+    }
+
+    bind(&done);
+}
+
+bool
+MacroAssembler::convertValueToInt(JSContext *cx, const Value &v, Register output, Label *fail,
+                                  IntConversionBehavior behavior)
+{
+    bool handleStrings = (behavior == IntConversion_Truncate ||
+                          behavior == IntConversion_ClampToUint8);
+    bool zeroObjects = behavior == IntConversion_ClampToUint8;
+
+    if (v.isNumber() || (handleStrings && v.isString())) {
+        double d;
+        if (v.isNumber())
+            d = v.toNumber();
+        else if (!StringToNumber(cx, v.toString(), &d))
+            return false;
+
+        switch (behavior) {
+          case IntConversion_Normal:
+          case IntConversion_NegativeZeroCheck: {
+            // -0 is checked anyways if we have a constant value.
+            int i;
+            if (mozilla::DoubleIsInt32(d, &i))
+                move32(Imm32(i), output);
+            else
+                jump(fail);
+            break;
+          }
+          case IntConversion_Truncate:
+            move32(Imm32(ToInt32(d)), output);
+            break;
+          case IntConversion_ClampToUint8:
+            move32(Imm32(ClampDoubleToUint8(d)), output);
+            break;
+        }
+
+        return true;
+    }
+
+    if (v.isBoolean()) {
+        move32(Imm32(v.toBoolean() ? 1 : 0), output);
+        return true;
+    }
+
+    if (v.isNull() || v.isUndefined()) {
+        move32(Imm32(0), output);
+        return true;
+    }
+
+    JS_ASSERT(v.isObject());
+
+    if (zeroObjects)
+        move32(Imm32(0), output);
+    else
+        jump(fail);
+    return true;
+}
+
+bool
+MacroAssembler::convertConstantOrRegisterToInt(JSContext *cx, ConstantOrRegister src,
+                                               FloatRegister temp, Register output,
+                                               Label *fail, IntConversionBehavior behavior)
+{
+    if (src.constant())
+        return convertValueToInt(cx, src.value(), output, fail, behavior);
+
+    convertTypedOrValueToInt(src.reg(), temp, output, fail, behavior);
+    return true;
+}
+
+void
+MacroAssembler::convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister temp,
+                                         Register output, Label *fail,
+                                         IntConversionBehavior behavior)
+{
+    if (src.hasValue()) {
+        convertValueToInt(src.valueReg(), temp, output, fail, behavior);
+        return;
+    }
+
+    switch (src.type()) {
+      case MIRType_Undefined:
+      case MIRType_Null:
+        move32(Imm32(0), output);
+        break;
+      case MIRType_Boolean:
+      case MIRType_Int32:
+        if (src.typedReg().gpr() != output)
+            move32(src.typedReg().gpr(), output);
+        break;
+      case MIRType_Double:
+        convertDoubleToInt(src.typedReg().fpu(), output, fail, behavior);
+        break;
+      case MIRType_String:
+      case MIRType_Object:
+        jump(fail);
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Bad MIRType");
+    }
+}
+
 void
 MacroAssembler::finish()
 {
     if (sequentialFailureLabel_.used()) {
         bind(&sequentialFailureLabel_);
         handleFailure(SequentialExecution);
     }
     if (parallelFailureLabel_.used()) {
@@ -1465,20 +1789,20 @@ MacroAssembler::branchIfNotInterpretedCo
         branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::IS_FUN_PROTO << 16), &done);
         breakpoint();
 #endif
     }
     bind(&done);
 }
 
 void
-MacroAssembler::branchEqualTypeIfNeeded(MIRType type, MDefinition *def, const Register &tag,
+MacroAssembler::branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, const Register &tag,
                                         Label *label)
 {
-    if (def->mightBeType(type)) {
+    if (!maybeDef || maybeDef->mightBeType(type)) {
         switch (type) {
           case MIRType_Null:
             branchTestNull(Equal, tag, label);
             break;
           case MIRType_Boolean:
             branchTestBoolean(Equal, tag, label);
             break;
           case MIRType_Int32:
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -157,16 +157,19 @@ class MacroAssembler : public MacroAssem
         return embedsNurseryPointers_;
     }
 
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source, typename TypeSet>
     void guardTypeSet(const Source &address, const TypeSet *types, Register scratch,
                       Label *matched, Label *miss);
+    template <typename TypeSet>
+    void guardObjectType(Register obj, const TypeSet *types,
+                         Register scratch, Label *matched, Label *miss);
     template <typename Source>
     void guardType(const Source &address, types::Type type, Register scratch,
                    Label *matched, Label *miss);
 
     void loadObjShape(Register objReg, Register dest) {
         loadPtr(Address(objReg, JSObject::offsetOfShape()), dest);
     }
     void loadBaseShape(Register objReg, Register dest) {
@@ -422,17 +425,17 @@ class MacroAssembler : public MacroAssem
             size_t idbits = JSID_BITS(id);
             Push(ImmWord(idbits));
         }
     }
 
     void Push(TypedOrValueRegister v) {
         if (v.hasValue())
             Push(v.valueReg());
-        else if (v.type() == MIRType_Double)
+        else if (IsFloatingPointType(v.type()))
             Push(v.typedReg().fpu());
         else
             Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr());
     }
 
     void Push(ConstantOrRegister v) {
         if (v.constant())
             Push(v.value());
@@ -553,16 +556,24 @@ class MacroAssembler : public MacroAssem
 
     void canonicalizeDouble(FloatRegister reg) {
         Label notNaN;
         branchDouble(DoubleOrdered, reg, reg, &notNaN);
         loadStaticDouble(&js_NaN, reg);
         bind(&notNaN);
     }
 
+    void canonicalizeFloat(FloatRegister reg) {
+        static float js_NaN_float = js_NaN;
+        Label notNaN;
+        branchFloat(DoubleOrdered, reg, reg, &notNaN);
+        loadStaticFloat32(&js_NaN_float, reg);
+        bind(&notNaN);
+    }
+
     template<typename T>
     void loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp, Label *fail);
 
     template<typename T>
     void loadFromTypedArray(int arrayType, const T &src, const ValueOperand &dest, bool allowDouble,
                             Register temp, Label *fail);
 
     template<typename S, typename T>
@@ -581,34 +592,18 @@ class MacroAssembler : public MacroAssem
           case ScalarTypeRepresentation::TYPE_UINT32:
             store32(value, dest);
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
         }
     }
 
-    template<typename T>
-    void storeToTypedFloatArray(int arrayType, FloatRegister value, const T &dest) {
-#ifdef JS_MORE_DETERMINISTIC
-        // See the comment in ToDoubleForTypedArray.
-        canonicalizeDouble(value);
-#endif
-        switch (arrayType) {
-          case ScalarTypeRepresentation::TYPE_FLOAT32:
-            convertDoubleToFloat(value, ScratchFloatReg);
-            storeFloat(ScratchFloatReg, dest);
-            break;
-          case ScalarTypeRepresentation::TYPE_FLOAT64:
-            storeDouble(value, dest);
-            break;
-          default:
-            MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
-        }
-    }
+    void storeToTypedFloatArray(int arrayType, const FloatRegister &value, const BaseIndex &dest);
+    void storeToTypedFloatArray(int arrayType, const FloatRegister &value, const Address &dest);
 
     Register extractString(const Address &address, Register scratch) {
         return extractObject(address, scratch);
     }
     Register extractString(const ValueOperand &value, Register scratch) {
         return extractObject(value, scratch);
     }
 
@@ -620,42 +615,44 @@ class MacroAssembler : public MacroAssem
         mov(ImmWord(ValueTypeFromMIRType(reg.type())), scratch);
         return scratch;
     }
 
     using MacroAssemblerSpecific::extractObject;
     Register extractObject(const TypedOrValueRegister &reg, Register scratch) {
         if (reg.hasValue())
             return extractObject(reg.valueReg(), scratch);
+        JS_ASSERT(reg.type() == MIRType_Object);
         return reg.typedReg().gpr();
     }
 
     // Inline version of js_TypedArray_uint8_clamp_double.
     // This function clobbers the input register.
     void clampDoubleToUint8(FloatRegister input, Register output);
 
     using MacroAssemblerSpecific::ensureDouble;
 
-    void ensureDouble(const Address &source, FloatRegister dest, Label *failure) {
+    template <typename S>
+    void ensureDouble(const S &source, FloatRegister dest, Label *failure) {
         Label isDouble, done;
         branchTestDouble(Assembler::Equal, source, &isDouble);
         branchTestInt32(Assembler::NotEqual, source, failure);
 
         convertInt32ToDouble(source, dest);
         jump(&done);
 
         bind(&isDouble);
         unboxDouble(source, dest);
 
         bind(&done);
     }
 
     // Emit type case branch on tag matching if the type tag in the definition
     // might actually be that type.
-    void branchEqualTypeIfNeeded(MIRType type, MDefinition *def, const Register &tag,
+    void branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, const Register &tag,
                                  Label *label);
 
     // Inline allocation.
     void newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail);
     void newGCThing(const Register &result, JSObject *templateObject, Label *fail);
     void newGCString(const Register &result, Label *fail);
     void newGCShortString(const Register &result, Label *fail);
 
@@ -996,17 +993,161 @@ class MacroAssembler : public MacroAssem
 #if JS_TRACE_LOGGING
     void tracelogStart(JSScript *script);
     void tracelogStop();
     void tracelogLog(TraceLogging::Type type);
 #endif
 
     void convertInt32ValueToDouble(const Address &address, Register scratch, Label *done);
     void convertValueToDouble(ValueOperand value, FloatRegister output, Label *fail);
-    void convertValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label *fail);
+    bool convertValueToDouble(JSContext *cx, const Value &v, FloatRegister output, Label *fail);
+    bool convertConstantOrRegisterToDouble(JSContext *cx, ConstantOrRegister src,
+                                           FloatRegister output, Label *fail);
+    void convertTypedOrValueToDouble(TypedOrValueRegister src, FloatRegister output, Label *fail);
+
+    enum IntConversionBehavior {
+        IntConversion_Normal,
+        IntConversion_NegativeZeroCheck,
+        IntConversion_Truncate,
+        IntConversion_ClampToUint8,
+    };
+
+    //
+    // Functions for converting values to int.
+    //
+    void convertDoubleToInt(FloatRegister src, Register output, Label *fail,
+                            IntConversionBehavior behavior);
+
+    // Strings may be handled by providing labels to jump to when the behavior
+    // is truncation or clamping. The subroutine, usually an OOL call, is
+    // passed the unboxed string in |stringReg| and should convert it to a
+    // double store into |temp|.
+    void convertValueToInt(ValueOperand value, MDefinition *input,
+                           Label *handleStringEntry, Label *handleStringRejoin,
+                           Register stringReg, FloatRegister temp, Register output,
+                           Label *fail, IntConversionBehavior behavior);
+    void convertValueToInt(ValueOperand value, FloatRegister temp, Register output, Label *fail,
+                           IntConversionBehavior behavior)
+    {
+        convertValueToInt(value, NULL, NULL, NULL, InvalidReg, temp, output, fail, behavior);
+    }
+    bool convertValueToInt(JSContext *cx, const Value &v, Register output, Label *fail,
+                           IntConversionBehavior behavior);
+    bool convertConstantOrRegisterToInt(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
+                                        Register output, Label *fail, IntConversionBehavior behavior);
+    void convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister temp, Register output,
+                                  Label *fail, IntConversionBehavior behavior);
+
+    //
+    // Convenience functions for converting values to int32.
+    //
+    void convertValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label *fail,
+                             bool negativeZeroCheck)
+    {
+        convertValueToInt(value, temp, output, fail, negativeZeroCheck
+                          ? IntConversion_NegativeZeroCheck
+                          : IntConversion_Normal);
+    }
+    void convertValueToInt32(ValueOperand value, MDefinition *input,
+                             FloatRegister temp, Register output, Label *fail,
+                             bool negativeZeroCheck)
+    {
+        convertValueToInt(value, input, NULL, NULL, InvalidReg, temp, output, fail,
+                          negativeZeroCheck
+                          ? IntConversion_NegativeZeroCheck
+                          : IntConversion_Normal);
+    }
+    bool convertValueToInt32(JSContext *cx, const Value &v, Register output, Label *fail,
+                             bool negativeZeroCheck)
+    {
+        return convertValueToInt(cx, v, output, fail, negativeZeroCheck
+                                 ? IntConversion_NegativeZeroCheck
+                                 : IntConversion_Normal);
+    }
+    bool convertConstantOrRegisterToInt32(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
+                                          Register output, Label *fail, bool negativeZeroCheck)
+    {
+        return convertConstantOrRegisterToInt(cx, src, temp, output, fail, negativeZeroCheck
+                                              ? IntConversion_NegativeZeroCheck
+                                              : IntConversion_Normal);
+    }
+    void convertTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
+                                    Label *fail, bool negativeZeroCheck)
+    {
+        convertTypedOrValueToInt(src, temp, output, fail, negativeZeroCheck
+                                 ? IntConversion_NegativeZeroCheck
+                                 : IntConversion_Normal);
+    }
+
+    //
+    // Convenience functions for truncating values to int32.
+    //
+    void truncateValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label *fail) {
+        convertValueToInt(value, temp, output, fail, IntConversion_Truncate);
+    }
+    void truncateValueToInt32(ValueOperand value, MDefinition *input,
+                              Label *handleStringEntry, Label *handleStringRejoin,
+                              Register stringReg, FloatRegister temp, Register output,
+                              Label *fail)
+    {
+        convertValueToInt(value, input, handleStringEntry, handleStringRejoin,
+                          stringReg, temp, output, fail, IntConversion_Truncate);
+    }
+    void truncateValueToInt32(ValueOperand value, MDefinition *input,
+                              FloatRegister temp, Register output, Label *fail)
+    {
+        convertValueToInt(value, input, NULL, NULL, InvalidReg, temp, output, fail,
+                          IntConversion_Truncate);
+    }
+    bool truncateValueToInt32(JSContext *cx, const Value &v, Register output, Label *fail) {
+        return convertValueToInt(cx, v, output, fail, IntConversion_Truncate);
+    }
+    bool truncateConstantOrRegisterToInt32(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
+                                           Register output, Label *fail)
+    {
+        return convertConstantOrRegisterToInt(cx, src, temp, output, fail, IntConversion_Truncate);
+    }
+    void truncateTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
+                                     Label *fail)
+    {
+        convertTypedOrValueToInt(src, temp, output, fail, IntConversion_Truncate);
+    }
+
+    // Convenience functions for clamping values to uint8.
+    void clampValueToUint8(ValueOperand value, FloatRegister temp, Register output, Label *fail) {
+        convertValueToInt(value, temp, output, fail, IntConversion_ClampToUint8);
+    }
+    void clampValueToUint8(ValueOperand value, MDefinition *input,
+                           Label *handleStringEntry, Label *handleStringRejoin,
+                           Register stringReg, FloatRegister temp, Register output,
+                           Label *fail)
+    {
+        convertValueToInt(value, input, handleStringEntry, handleStringRejoin,
+                          stringReg, temp, output, fail, IntConversion_ClampToUint8);
+    }
+    void clampValueToUint8(ValueOperand value, MDefinition *input,
+                           FloatRegister temp, Register output, Label *fail)
+    {
+        convertValueToInt(value, input, NULL, NULL, InvalidReg, temp, output, fail,
+                          IntConversion_ClampToUint8);
+    }
+    bool clampValueToUint8(JSContext *cx, const Value &v, Register output, Label *fail) {
+        return convertValueToInt(cx, v, output, fail, IntConversion_ClampToUint8);
+    }
+    bool clampConstantOrRegisterToUint8(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
+                                        Register output, Label *fail)
+    {
+        return convertConstantOrRegisterToInt(cx, src, temp, output, fail,
+                                              IntConversion_ClampToUint8);
+    }
+    void clampTypedOrValueToUint8(TypedOrValueRegister src, FloatRegister temp, Register output,
+                                  Label *fail)
+    {
+        convertTypedOrValueToInt(src, temp, output, fail, IntConversion_ClampToUint8);
+    }
 };
 
 static inline Assembler::DoubleCondition
 JSOpToDoubleCondition(JSOp op)
 {
     switch (op) {
       case JSOP_EQ:
       case JSOP_STRICTEQ:
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -78,16 +78,17 @@ BailoutKindString(BailoutKind kind)
 // a number.
 enum MIRType
 {
     MIRType_Undefined,
     MIRType_Null,
     MIRType_Boolean,
     MIRType_Int32,
     MIRType_Double,
+    MIRType_Float32,
     MIRType_String,
     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
@@ -129,16 +130,17 @@ ValueTypeFromMIRType(MIRType type)
     case MIRType_Undefined:
       return JSVAL_TYPE_UNDEFINED;
     case MIRType_Null:
       return JSVAL_TYPE_NULL;
     case MIRType_Boolean:
       return JSVAL_TYPE_BOOLEAN;
     case MIRType_Int32:
       return JSVAL_TYPE_INT32;
+    case MIRType_Float32: // Fall through, there's no JSVAL for Float32
     case MIRType_Double:
       return JSVAL_TYPE_DOUBLE;
     case MIRType_String:
       return JSVAL_TYPE_STRING;
     case MIRType_Magic:
       return JSVAL_TYPE_MAGIC;
     default:
       JS_ASSERT(type == MIRType_Object);
@@ -161,16 +163,18 @@ StringFromMIRType(MIRType type)
     case MIRType_Null:
       return "Null";
     case MIRType_Boolean:
       return "Bool";
     case MIRType_Int32:
       return "Int32";
     case MIRType_Double:
       return "Double";
+    case MIRType_Float32:
+      return "Float32";
     case MIRType_String:
       return "String";
     case MIRType_Object:
       return "Object";
     case MIRType_Magic:
       return "Magic";
     case MIRType_Value:
       return "Value";
@@ -187,17 +191,29 @@ StringFromMIRType(MIRType type)
     default:
       MOZ_ASSUME_UNREACHABLE("Unknown MIRType.");
   }
 }
 
 static inline bool
 IsNumberType(MIRType type)
 {
-    return type == MIRType_Int32 || type == MIRType_Double;
+    return type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32;
+}
+
+static inline bool
+IsFloatType(MIRType type)
+{
+    return type == MIRType_Int32 || type == MIRType_Float32;
+}
+
+static inline bool
+IsFloatingPointType(MIRType type)
+{
+    return type == MIRType_Double || type == MIRType_Float32;
 }
 
 static inline bool
 IsNullOrUndefined(MIRType type)
 {
     return type == MIRType_Null || type == MIRType_Undefined;
 }
 
--- a/js/src/jit/LICM.cpp
+++ b/js/src/jit/LICM.cpp
@@ -243,17 +243,17 @@ Loop::requiresHoistedUse(const MDefiniti
 {
     if (ins->isConstantElements() || ins->isBox())
         return true;
 
     // Integer constants can often be folded as immediates and aren't worth
     // hoisting on their own, in general. Floating-point constants typically
     // are worth hoisting, unless they'll end up being spilled (eg. due to a
     // call).
-    if (ins->isConstant() && (ins->type() != MIRType_Double || containsPossibleCall_))
+    if (ins->isConstant() && (IsFloatingPointType(ins->type()) || containsPossibleCall_))
         return true;
 
     return false;
 }
 
 bool
 Loop::hoistInstructions(InstructionQueue &toHoist)
 {
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -183,16 +183,32 @@ class LDouble : public LInstructionHelpe
 
     LDouble(double d) : d_(d)
     { }
     double getDouble() const {
         return d_;
     }
 };
 
+// Constant float32.
+class LFloat32 : public LInstructionHelper<1, 0, 0>
+{
+    float f_;
+  public:
+    LIR_HEADER(Float32);
+
+    LFloat32(float f)
+      : f_(f)
+    { }
+
+    float getFloat() const {
+        return f_;
+    }
+};
+
 // A constant Value.
 class LValue : public LInstructionHelper<BOX_PIECES, 0, 0>
 {
     Value v_;
 
   public:
     LIR_HEADER(Value)
 
@@ -2097,16 +2113,26 @@ class LNegD : public LInstructionHelper<
 {
   public:
     LIR_HEADER(NegD)
     LNegD(const LAllocation &num) {
         setOperand(0, num);
     }
 };
 
+// Negative of a float32.
+class LNegF : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(NegF)
+    LNegF(const LAllocation &num) {
+        setOperand(0, num);
+    }
+};
+
 // Absolute value of an integer.
 class LAbsI : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(AbsI)
     LAbsI(const LAllocation &num) {
         setOperand(0, num);
     }
@@ -2297,16 +2323,33 @@ class LMathD : public LBinaryMath<0>
       : jsop_(jsop)
     { }
 
     JSOp jsop() const {
         return jsop_;
     }
 };
 
+// Performs an add, sub, mul, or div on two double values.
+class LMathF: public LBinaryMath<0>
+{
+    JSOp jsop_;
+
+  public:
+    LIR_HEADER(MathF)
+
+    LMathF(JSOp jsop)
+      : jsop_(jsop)
+    { }
+
+    JSOp jsop() const {
+        return jsop_;
+    }
+};
+
 class LModD : public LBinaryMath<1>
 {
   public:
     LIR_HEADER(ModD)
 
     LModD(const LAllocation &lhs, const LAllocation &rhs, const LDefinition &temp) {
         setOperand(0, lhs);
         setOperand(1, rhs);
@@ -2453,28 +2496,73 @@ class LInt32ToDouble : public LInstructi
   public:
     LIR_HEADER(Int32ToDouble)
 
     LInt32ToDouble(const LAllocation &input) {
         setOperand(0, input);
     }
 };
 
+// Convert a 32-bit float to a double.
+class LFloat32ToDouble : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(Float32ToDouble)
+
+    LFloat32ToDouble(const LAllocation &input) {
+        setOperand(0, input);
+    }
+};
+
+// Convert a double to a 32-bit float.
+class LDoubleToFloat32 : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(DoubleToFloat32)
+
+    LDoubleToFloat32(const LAllocation &input) {
+        setOperand(0, input);
+    }
+};
+
+// Convert a 32-bit integer to a float32.
+class LInt32ToFloat32 : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(Int32ToFloat32)
+
+    LInt32ToFloat32(const LAllocation &input) {
+        setOperand(0, input);
+    }
+};
+
 // Convert a value to a double.
 class LValueToDouble : public LInstructionHelper<1, BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(ValueToDouble)
     static const size_t Input = 0;
 
     MToDouble *mir() {
         return mir_->toToDouble();
     }
 };
 
+// Convert a value to a float32.
+class LValueToFloat32 : public LInstructionHelper<1, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(ValueToFloat32)
+    static const size_t Input = 0;
+
+    MToFloat32 *mir() {
+        return mir_->toToFloat32();
+    }
+};
+
 // Convert a value to an int32.
 //   Input: components of a Value
 //   Output: 32-bit integer
 //   Bailout: undefined, string, object, or non-int32 double
 //   Temps: one float register, one GP register
 //
 // This instruction requires a temporary float register.
 class LValueToInt32 : public LInstructionHelper<1, BOX_PIECES, 2>
@@ -3554,16 +3642,19 @@ class LClampVToUint8 : public LInstructi
         setTemp(0, tempFloat);
     }
 
     static const size_t Input = 0;
 
     const LDefinition *tempFloat() {
         return getTemp(0);
     }
+    const MClampToUint8 *mir() const {
+        return mir_->toClampToUint8();
+    }
 };
 
 // Load a boxed value from an object's fixed slot.
 class LLoadFixedSlotV : public LInstructionHelper<BOX_PIECES, 1, 0>
 {
   public:
     LIR_HEADER(LoadFixedSlotV)
     BOX_OUTPUT_ACCESSORS()
@@ -4163,59 +4254,65 @@ class LSetPropertyCacheT : public LInstr
     const MSetPropertyCache *mir() const {
         return mir_->toSetPropertyCache();
     }
     MIRType valueType() {
         return valueType_;
     }
 };
 
-class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 2>
+class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 3>
 {
   public:
     LIR_HEADER(SetElementCacheV);
 
     static const size_t Index = 1;
     static const size_t Value = 1 + BOX_PIECES;
 
     LSetElementCacheV(const LAllocation &object, const LDefinition &tempToUnboxIndex,
-                      const LDefinition &temp)
+                      const LDefinition &temp, const LDefinition &tempFloat)
     {
         setOperand(0, object);
         setTemp(0, tempToUnboxIndex);
         setTemp(1, temp);
+        setTemp(2, tempFloat);
     }
     const MSetElementCache *mir() const {
         return mir_->toSetElementCache();
     }
 
     const LAllocation *object() {
         return getOperand(0);
     }
     const LDefinition *tempToUnboxIndex() {
         return getTemp(0);
     }
     const LDefinition *temp() {
         return getTemp(1);
     }
-};
-
-class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 2>
+    const LDefinition *tempFloat() {
+        return getTemp(2);
+    }
+};
+
+class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 3>
 {
   public:
     LIR_HEADER(SetElementCacheT);
 
     static const size_t Index = 2;
 
     LSetElementCacheT(const LAllocation &object, const LAllocation &value,
-                      const LDefinition &tempToUnboxIndex, const LDefinition &temp) {
+                      const LDefinition &tempToUnboxIndex,
+                      const LDefinition &temp, const LDefinition &tempFloat) {
         setOperand(0, object);
         setOperand(1, value);
         setTemp(0, tempToUnboxIndex);
         setTemp(1, temp);
+        setTemp(2, tempFloat);
     }
     const MSetElementCache *mir() const {
         return mir_->toSetElementCache();
     }
 
     const LAllocation *object() {
         return getOperand(0);
     }
@@ -4223,16 +4320,19 @@ class LSetElementCacheT : public LInstru
         return getOperand(1);
     }
     const LDefinition *tempToUnboxIndex() {
         return getTemp(0);
     }
     const LDefinition *temp() {
         return getTemp(1);
     }
+    const LDefinition *tempFloat() {
+        return getTemp(2);
+    }
 };
 
 class LCallIteratorStart : public LCallInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(CallIteratorStart)
 
     LCallIteratorStart(const LAllocation &object) {
@@ -4444,35 +4544,56 @@ class LGuardThreadLocalObject : public L
     }
 
     const LAllocation *getTempReg() {
         return getTemp(0)->output();
     }
 };
 
 // Guard that a value is in a TypeSet.
-class LTypeBarrier : public LInstructionHelper<0, BOX_PIECES, 1>
-{
-  public:
-    LIR_HEADER(TypeBarrier)
-
-    LTypeBarrier(const LDefinition &temp) {
+class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1>
+{
+  public:
+    LIR_HEADER(TypeBarrierV)
+
+    LTypeBarrierV(const LDefinition &temp) {
         setTemp(0, temp);
     }
 
     static const size_t Input = 0;
 
     const MTypeBarrier *mir() const {
         return mir_->toTypeBarrier();
     }
     const LDefinition *temp() {
         return getTemp(0);
     }
 };
 
+// Guard that a object is in a TypeSet.
+class LTypeBarrierO : public LInstructionHelper<0, 1, 1>
+{
+  public:
+    LIR_HEADER(TypeBarrierO)
+
+    LTypeBarrierO(const LAllocation &obj, const LDefinition &temp) {
+        setOperand(0, obj);
+        setTemp(0, temp);
+    }
+    const MTypeBarrier *mir() const {
+        return mir_->toTypeBarrier();
+    }
+    const LAllocation *object() {
+        return getOperand(0);
+    }
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
+};
+
 // Guard that a value is in a TypeSet.
 class LMonitorTypes : public LInstructionHelper<0, BOX_PIECES, 1>
 {
   public:
     LIR_HEADER(MonitorTypes)
 
     LMonitorTypes(const LDefinition &temp) {
         setTemp(0, temp);
@@ -4978,16 +5099,42 @@ class LAssertRangeD : public LInstructio
     MAssertRange *mir() {
         return mir_->toAssertRange();
     }
     Range *range() {
         return mir()->range();
     }
 };
 
+class LAssertRangeF : public LInstructionHelper<0, 1, 1>
+{
+  public:
+    LIR_HEADER(AssertRangeF)
+
+    LAssertRangeF(const LAllocation &input, const LDefinition &temp) {
+        setOperand(0, input);
+        setTemp(0, temp);
+    }
+
+    const LAllocation *input() {
+        return getOperand(0);
+    }
+
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
+
+    MAssertRange *mir() {
+        return mir_->toAssertRange();
+    }
+    Range *range() {
+        return mir()->range();
+    }
+};
+
 class LAssertRangeV : public LInstructionHelper<0, BOX_PIECES, 3>
 {
   public:
     LIR_HEADER(AssertRangeV)
 
     LAssertRangeV(const LDefinition &temp, const LDefinition &floatTemp1,
                   const LDefinition &floatTemp2)
     {
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -535,16 +535,17 @@ class LDefinition
         switch (type) {
           case MIRType_Boolean:
           case MIRType_Int32:
             return LDefinition::GENERAL;
           case MIRType_String:
           case MIRType_Object:
             return LDefinition::OBJECT;
           case MIRType_Double:
+          case MIRType_Float32:
             return LDefinition::DOUBLE;
 #if defined(JS_PUNBOX64)
           case MIRType_Value:
             return LDefinition::BOX;
 #endif
           case MIRType_Slots:
           case MIRType_Elements:
             return LDefinition::SLOTS;
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -10,16 +10,17 @@
 #define LIR_COMMON_OPCODE_LIST(_)   \
     _(Label)                        \
     _(Nop)                          \
     _(OsiPoint)                     \
     _(MoveGroup)                    \
     _(Integer)                      \
     _(Pointer)                      \
     _(Double)                       \
+    _(Float32)                      \
     _(Value)                        \
     _(Parameter)                    \
     _(Callee)                       \
     _(TableSwitch)                  \
     _(TableSwitchV)                 \
     _(Goto)                         \
     _(NewParallelArray)             \
     _(NewArray)                     \
@@ -87,41 +88,47 @@
     _(IsNullOrLikeUndefined)        \
     _(IsNullOrLikeUndefinedAndBranch)\
     _(EmulatesUndefined)            \
     _(EmulatesUndefinedAndBranch)   \
     _(MinMaxI)                      \
     _(MinMaxD)                      \
     _(NegI)                         \
     _(NegD)                         \
+    _(NegF)                         \
     _(AbsI)                         \
     _(AbsD)                         \
     _(SqrtD)                        \
     _(Atan2D)                       \
     _(PowI)                         \
     _(PowD)                         \
     _(Random)                       \
     _(MathFunctionD)                \
     _(NotI)                         \
     _(NotD)                         \
     _(NotO)                         \
     _(NotV)                         \
     _(AddI)                         \
     _(SubI)                         \
     _(MulI)                         \
     _(MathD)                        \
+    _(MathF)                        \
     _(ModD)                         \
     _(BinaryV)                      \
     _(Concat)                       \
     _(ConcatPar)                    \
     _(CharCodeAt)                   \
     _(FromCharCode)                 \
     _(Int32ToDouble)                \
+    _(Float32ToDouble)              \
+    _(DoubleToFloat32)              \
+    _(Int32ToFloat32)               \
     _(ValueToDouble)                \
     _(ValueToInt32)                 \
+    _(ValueToFloat32)               \
     _(DoubleToInt32)                \
     _(TruncateDToInt32)             \
     _(IntToString)                  \
     _(DoubleToString)               \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
@@ -138,17 +145,18 @@
     _(LoadSlotV)                    \
     _(LoadSlotT)                    \
     _(StoreSlotV)                   \
     _(StoreSlotT)                   \
     _(GuardShape)                   \
     _(GuardObjectType)              \
     _(GuardClass)                   \
     _(GuardThreadLocalObject)       \
-    _(TypeBarrier)                  \
+    _(TypeBarrierV)                 \
+    _(TypeBarrierO)                 \
     _(MonitorTypes)                 \
     _(PostWriteBarrierO)            \
     _(PostWriteBarrierV)            \
     _(InitializedLength)            \
     _(SetInitializedLength)         \
     _(BoundsCheck)                  \
     _(BoundsCheckRange)             \
     _(BoundsCheckLower)             \
@@ -243,16 +251,17 @@
     _(AsmJSReturn)                  \
     _(AsmJSVoidReturn)              \
     _(AsmJSPassStackArg)            \
     _(AsmJSCall)                    \
     _(AsmJSCheckOverRecursed)       \
     _(CheckInterruptPar)            \
     _(AssertRangeI)                 \
     _(AssertRangeD)                 \
+    _(AssertRangeF)                 \
     _(AssertRangeV)
 
 #if defined(JS_CPU_X86)
 # include "jit/x86/LOpcodes-x86.h"
 #elif defined(JS_CPU_X64)
 # include "jit/x64/LOpcodes-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "jit/arm/LOpcodes-arm.h"
--- a/js/src/jit/LiveRangeAllocator.h
+++ b/js/src/jit/LiveRangeAllocator.h
@@ -31,24 +31,27 @@ class Requirement
 
     Requirement()
       : kind_(NONE)
     { }
 
     Requirement(Kind kind)
       : kind_(kind)
     {
-        // These have dedicated constructors;
+        // These have dedicated constructors.
         JS_ASSERT(kind != FIXED && kind != SAME_AS_OTHER);
     }
 
     Requirement(Kind kind, CodePosition at)
       : kind_(kind),
         position_(at)
-    { }
+    {
+        // These have dedicated constructors.
+        JS_ASSERT(kind != FIXED && kind != SAME_AS_OTHER);
+    }
 
     Requirement(LAllocation fixed)
       : kind_(FIXED),
         allocation_(fixed)
     { }
 
     // Only useful as a hint, encodes where the fixed requirement is used to
     // avoid allocating a fixed register too early.
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1236,16 +1236,22 @@ LIRGenerator::visitAdd(MAdd *ins)
     }
 
     if (ins->specialization() == MIRType_Double) {
         JS_ASSERT(lhs->type() == MIRType_Double);
         ReorderCommutative(&lhs, &rhs);
         return lowerForFPU(new LMathD(JSOP_ADD), ins, lhs, rhs);
     }
 
+    if (ins->specialization() == MIRType_Float32) {
+        JS_ASSERT(lhs->type() == MIRType_Float32);
+        ReorderCommutative(&lhs, &rhs);
+        return lowerForFPU(new LMathF(JSOP_ADD), ins, lhs, rhs);
+    }
+
     return lowerBinaryV(JSOP_ADD, ins);
 }
 
 bool
 LIRGenerator::visitSub(MSub *ins)
 {
     MDefinition *lhs = ins->lhs();
     MDefinition *rhs = ins->rhs();
@@ -1264,16 +1270,20 @@ LIRGenerator::visitSub(MSub *ins)
 
         MaybeSetRecoversInput(ins, lir);
         return true;
     }
     if (ins->specialization() == MIRType_Double) {
         JS_ASSERT(lhs->type() == MIRType_Double);
         return lowerForFPU(new LMathD(JSOP_SUB), ins, lhs, rhs);
     }
+    if (ins->specialization() == MIRType_Float32) {
+        JS_ASSERT(lhs->type() == MIRType_Float32);
+        return lowerForFPU(new LMathF(JSOP_SUB), ins, lhs, rhs);
+    }
 
     return lowerBinaryV(JSOP_SUB, ins);
 }
 
 bool
 LIRGenerator::visitMul(MMul *ins)
 {
     MDefinition *lhs = ins->lhs();
@@ -1290,16 +1300,28 @@ LIRGenerator::visitMul(MMul *ins)
         ReorderCommutative(&lhs, &rhs);
 
         // If our RHS is a constant -1.0, we can optimize to an LNegD.
         if (rhs->isConstant() && rhs->toConstant()->value() == DoubleValue(-1.0))
             return defineReuseInput(new LNegD(useRegisterAtStart(lhs)), ins, 0);
 
         return lowerForFPU(new LMathD(JSOP_MUL), ins, lhs, rhs);
     }
+    if (ins->specialization() == MIRType_Float32) {
+        JS_ASSERT(lhs->type() == MIRType_Float32);
+        ReorderCommutative(&lhs, &rhs);
+
+        // We apply the same optimizations as for doubles
+        if (lhs->isConstant() && lhs->toConstant()->value() == JS::Float32Value(-1.0f))
+            return defineReuseInput(new LNegF(useRegisterAtStart(rhs)), ins, 0);
+        if (rhs->isConstant() && rhs->toConstant()->value() == JS::Float32Value(-1.0f))
+            return defineReuseInput(new LNegF(useRegisterAtStart(lhs)), ins, 0);
+
+        return lowerForFPU(new LMathF(JSOP_MUL), ins, lhs, rhs);
+    }
 
     return lowerBinaryV(JSOP_MUL, ins);
 }
 
 bool
 LIRGenerator::visitDiv(MDiv *ins)
 {
     MDefinition *lhs = ins->lhs();
@@ -1309,16 +1331,20 @@ LIRGenerator::visitDiv(MDiv *ins)
     if (ins->specialization() == MIRType_Int32) {
         JS_ASSERT(lhs->type() == MIRType_Int32);
         return lowerDivI(ins);
     }
     if (ins->specialization() == MIRType_Double) {
         JS_ASSERT(lhs->type() == MIRType_Double);
         return lowerForFPU(new LMathD(JSOP_DIV), ins, lhs, rhs);
     }
+    if (ins->specialization() == MIRType_Float32) {
+        JS_ASSERT(lhs->type() == MIRType_Float32);
+        return lowerForFPU(new LMathF(JSOP_DIV), ins, lhs, rhs);
+    }
 
     return lowerBinaryV(JSOP_DIV, ins);
 }
 
 bool
 LIRGenerator::visitMod(MMod *ins)
 {
     JS_ASSERT(ins->lhs()->type() == ins->rhs()->type());
@@ -1501,27 +1527,83 @@ LIRGenerator::visitToDouble(MToDouble *c
         /* FALLTHROUGH */
 
       case MIRType_Int32:
       {
         LInt32ToDouble *lir = new LInt32ToDouble(useRegister(opd));
         return define(lir, convert);
       }
 
+      case MIRType_Float32:
+      {
+        LFloat32ToDouble *lir = new LFloat32ToDouble(useRegister(opd));
+        return define(lir, convert);
+      }
+
       case MIRType_Double:
         return redefine(convert, opd);
 
       default:
         // Objects might be effectful.
         // Strings are complicated - we don't handle them yet.
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
 bool
+LIRGenerator::visitToFloat32(MToFloat32 *convert)
+{
+    MDefinition *opd = convert->input();
+    mozilla::DebugOnly<MToFloat32::ConversionKind> conversion = convert->conversion();
+
+    switch (opd->type()) {
+      case MIRType_Value:
+      {
+        LValueToFloat32 *lir = new LValueToFloat32();
+        if (!useBox(lir, LValueToFloat32::Input, opd))
+            return false;
+        return assignSnapshot(lir) && define(lir, convert);
+      }
+
+      case MIRType_Null:
+        JS_ASSERT(conversion != MToFloat32::NonStringPrimitives);
+        return lowerConstantFloat32(0, convert);
+
+      case MIRType_Undefined:
+        JS_ASSERT(conversion != MToFloat32::NumbersOnly);
+        return lowerConstantFloat32(js_NaN, convert);
+
+      case MIRType_Boolean:
+        JS_ASSERT(conversion != MToFloat32::NumbersOnly);
+        /* FALLTHROUGH */
+
+      case MIRType_Int32:
+      {
+        LInt32ToFloat32 *lir = new LInt32ToFloat32(useRegister(opd));
+        return define(lir, convert);
+      }
+
+      case MIRType_Double:
+      {
+        LDoubleToFloat32 *lir = new LDoubleToFloat32(useRegister(opd));
+        return define(lir, convert);
+      }
+
+      case MIRType_Float32:
+        return redefine(convert, opd);
+
+      default:
+        // Objects might be effectful.
+        // Strings are complicated - we don't handle them yet.
+        MOZ_ASSUME_UNREACHABLE("unexpected type");
+        return false;
+    }
+}
+
+bool
 LIRGenerator::visitToInt32(MToInt32 *convert)
 {
     MDefinition *opd = convert->input();
 
     switch (opd->type()) {
       case MIRType_Value:
       {
         LValueToInt32 *lir = new LValueToInt32(tempFloat(), temp(), LValueToInt32::NORMAL);
@@ -1817,40 +1899,76 @@ LIRGenerator::visitStoreSlot(MStoreSlot 
         lir = new LStoreSlotV(useRegister(ins->slots()));
         if (!useBox(lir, LStoreSlotV::Value, ins->value()))
             return false;
         return add(lir, ins);
 
       case MIRType_Double:
         return add(new LStoreSlotT(useRegister(ins->slots()), useRegister(ins->value())), ins);
 
+      case MIRType_Float32:
+        MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be stored in a slot.");
+
       default:
         return add(new LStoreSlotT(useRegister(ins->slots()), useRegisterOrConstant(ins->value())),
                    ins);
     }
 
     return true;
 }
 
 bool
 LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
 {
     // Requesting a non-GC pointer is safe here since we never re-enter C++
     // from inside a type barrier test.
 
     const types::StackTypeSet *types = ins->resultTypeSet();
     bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
-    LDefinition tmp = needTemp ? temp() : tempToUnbox();
-
-    LTypeBarrier *barrier = new LTypeBarrier(tmp);
-    if (!useBox(barrier, LTypeBarrier::Input, ins->input()))
-        return false;
-    if (!assignSnapshot(barrier, ins->bailoutKind()))
-        return false;
-    return redefine(ins, ins->input()) && add(barrier, ins);
+
+    MIRType inputType = ins->getOperand(0)->type();
+    MIRType outputType = ins->type();
+
+    JS_ASSERT(inputType == outputType);
+    JS_ASSERT_IF(ins->alwaysBails(), outputType == MIRType_Value);
+
+    // Handle typebarrier that will always bail.
+    // (Emit LBail for visibility).
+    if (ins->alwaysBails()) {
+        JS_ASSERT(outputType == MIRType_Value);
+
+        LBail *bail = new LBail();
+        if (!assignSnapshot(bail, ins->bailoutKind()))
+            return false;
+        return redefine(ins, ins->input()) && add(bail, ins);
+    }
+
+    // Handle typebarrier with Value as input.
+    if (inputType == MIRType_Value) {
+        LDefinition tmp = needTemp ? temp() : tempToUnbox();
+        LTypeBarrierV *barrier = new LTypeBarrierV(tmp);
+        if (!useBox(barrier, LTypeBarrierV::Input, ins->input()))
+            return false;
+        if (!assignSnapshot(barrier, ins->bailoutKind()))
+            return false;
+        return redefine(ins, ins->input()) && add(barrier, ins);
+    }
+
+    // Handle typebarrier with specific TypeObject/SingleObjects.
+    if (inputType == MIRType_Object && !types->hasType(types::Type::AnyObjectType()))
+    {
+        LDefinition tmp = needTemp ? temp() : LDefinition::BogusTemp();
+        LTypeBarrierO *barrier = new LTypeBarrierO(useRegister(ins->getOperand(0)), tmp);
+        if (!assignSnapshot(barrier, ins->bailoutKind()))
+            return false;
+        return redefine(ins, ins->getOperand(0)) && add(barrier, ins);
+    }
+
+    // Handle remaining cases: No-op, unbox did everything.
+    return redefine(ins, ins->getOperand(0));
 }
 
 bool
 LIRGenerator::visitMonitorTypes(MMonitorTypes *ins)
 {
     // Requesting a non-GC pointer is safe here since we never re-enter C++
     // from inside a type check.
 
@@ -2214,17 +2332,17 @@ LIRGenerator::visitLoadTypedArrayElement
 
     const LUse elements = useRegister(ins->elements());
     const LAllocation index = useRegisterOrConstant(ins->index());
 
     JS_ASSERT(IsNumberType(ins->type()));
 
     // We need a temp register for Uint32Array with known double result.
     LDefinition tempDef = LDefinition::BogusTemp();
-    if (ins->arrayType() == ScalarTypeRepresentation::TYPE_UINT32 && ins->type() == MIRType_Double)
+    if (ins->arrayType() == ScalarTypeRepresentation::TYPE_UINT32 && IsFloatingPointType(ins->type()))
         tempDef = temp();
 
     LLoadTypedArrayElement *lir = new LLoadTypedArrayElement(elements, index, tempDef);
     if (ins->fallible() && !assignSnapshot(lir))
         return false;
     return define(lir, ins);
 }
 
@@ -2243,17 +2361,17 @@ LIRGenerator::visitClampToUint8(MClampTo
       case MIRType_Double:
         return define(new LClampDToUint8(useRegisterAtStart(in), tempCopy(in, 0)), ins);
 
       case MIRType_Value:
       {
         LClampVToUint8 *lir = new LClampVToUint8(tempFloat());
         if (!useBox(lir, LClampVToUint8::Input, in))
             return false;
-        return assignSnapshot(lir) && define(lir, ins);
+        return assignSnapshot(lir) && define(lir, ins) && assignSafepoint(lir, ins);
       }
 
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
 bool
@@ -2280,16 +2398,68 @@ LIRGenerator::visitLoadTypedArrayElement
         new LLoadTypedArrayElementStatic(useRegisterAtStart(ins->ptr()));
 
     if (ins->fallible() && !assignSnapshot(lir))
         return false;
     return define(lir, ins);
 }
 
 bool
+LIRGenerator::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins)
+{
+    JS_ASSERT(ins->elements()->type() == MIRType_Elements);
+    JS_ASSERT(ins->index()->type() == MIRType_Int32);
+
+    if (ins->isFloatArray()) {
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
+    } else {
+        JS_ASSERT(ins->value()->type() == MIRType_Int32);
+    }
+
+    LUse elements = useRegister(ins->elements());
+    LAllocation index = useRegisterOrConstant(ins->index());
+    LAllocation value;
+
+    // For byte arrays, the value has to be in a byte register on x86.
+    if (ins->isByteArray())
+        value = useByteOpRegisterOrNonDoubleConstant(ins->value());
+    else
+        value = useRegisterOrNonDoubleConstant(ins->value());
+    return add(new LStoreTypedArrayElement(elements, index, value), ins);
+}
+
+bool
+LIRGenerator::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins)
+{
+    JS_ASSERT(ins->elements()->type() == MIRType_Elements);
+    JS_ASSERT(ins->index()->type() == MIRType_Int32);
+    JS_ASSERT(ins->length()->type() == MIRType_Int32);
+
+    if (ins->isFloatArray()) {
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
+    } else {
+        JS_ASSERT(ins->value()->type() == MIRType_Int32);
+    }
+
+    LUse elements = useRegister(ins->elements());
+    LAllocation length = useAnyOrConstant(ins->length());
+    LAllocation index = useRegisterOrConstant(ins->index());
+    LAllocation value;
+
+    // For byte arrays, the value has to be in a byte register on x86.
+    if (ins->isByteArray())
+        value = useByteOpRegisterOrNonDoubleConstant(ins->value());
+    else
+        value = useRegisterOrNonDoubleConstant(ins->value());
+    return add(new LStoreTypedArrayElementHole(elements, length, index, value), ins);
+}
+
+bool
 LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot *ins)
 {
     JS_ASSERT(ins->object()->type() == MIRType_Object);
 
     if (ins->type() == MIRType_Value) {
         LLoadFixedSlotV *lir = new LLoadFixedSlotV(useRegister(ins->object()));
         return defineBox(lir, ins);
     }
@@ -2462,16 +2632,20 @@ LIRGenerator::visitAssertRange(MAssertRa
       case MIRType_Int32:
           lir = new LAssertRangeI(useRegisterAtStart(input));
         break;
 
       case MIRType_Double:
         lir = new LAssertRangeD(useRegister(input), tempFloat());
         break;
 
+      case MIRType_Float32:
+        lir = new LAssertRangeF(useRegister(input), tempFloat());
+        break;
+
       case MIRType_Value:
         lir = new LAssertRangeV(tempToUnbox(), tempFloat(), tempFloat());
         if (!useBox(lir, LAssertRangeV::Input, input))
             return false;
         break;
 
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected Range for MIRType");
@@ -2561,29 +2735,34 @@ LIRGenerator::visitSetPropertyCache(MSet
 }
 
 bool
 LIRGenerator::visitSetElementCache(MSetElementCache *ins)
 {
     JS_ASSERT(ins->object()->type() == MIRType_Object);
     JS_ASSERT(ins->index()->type() == MIRType_Value);
 
+    // Due to lack of registers on x86, we reuse the object register as a
+    // temporary. This register may be used in a 1-byte store, which on x86
+    // again has constraints; thus the use of |useByteOpRegister| over
+    // |useRegister| below.
     LInstruction *lir;
     if (ins->value()->type() == MIRType_Value) {
-        lir = new LSetElementCacheV(useRegister(ins->object()), tempToUnbox(), temp());
+        lir = new LSetElementCacheV(useByteOpRegister(ins->object()), tempToUnbox(),
+                                    temp(), tempFloat());
 
         if (!useBox(lir, LSetElementCacheV::Index, ins->index()))
             return false;
         if (!useBox(lir, LSetElementCacheV::Value, ins->value()))
             return false;
     } else {
         lir = new LSetElementCacheT(
-            useRegister(ins->object()),
+            useByteOpRegister(ins->object()),
             useRegisterOrConstant(ins->value()),
-            tempToUnbox(), temp());
+            tempToUnbox(), temp(), tempFloat());
 
         if (!useBox(lir, LSetElementCacheT::Index, ins->index()))
             return false;
     }
 
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -148,16 +148,17 @@ class LIRGenerator : public LIRGenerator
     bool visitCharCodeAt(MCharCodeAt *ins);
     bool visitFromCharCode(MFromCharCode *ins);
     bool visitStart(MStart *start);
     bool visitOsrEntry(MOsrEntry *entry);
     bool visitNop(MNop *nop);
     bool visitOsrValue(MOsrValue *value);
     bool visitOsrScopeChain(MOsrScopeChain *object);
     bool visitToDouble(MToDouble *convert);
+    bool visitToFloat32(MToFloat32 *convert);
     bool visitToInt32(MToInt32 *convert);
     bool visitTruncateToInt32(MTruncateToInt32 *truncate);
     bool visitToString(MToString *convert);
     bool visitRegExp(MRegExp *ins);
     bool visitRegExpTest(MRegExpTest *ins);
     bool visitLambda(MLambda *ins);
     bool visitLambdaPar(MLambdaPar *ins);
     bool visitImplicitThis(MImplicitThis *ins);
@@ -190,16 +191,18 @@ class LIRGenerator : public LIRGenerator
     bool visitStoreElementHole(MStoreElementHole *ins);
     bool visitEffectiveAddress(MEffectiveAddress *ins);
     bool visitArrayPopShift(MArrayPopShift *ins);
     bool visitArrayPush(MArrayPush *ins);
     bool visitArrayConcat(MArrayConcat *ins);
     bool visitLoadTypedArrayElement(MLoadTypedArrayElement *ins);
     bool visitLoadTypedArrayElementHole(MLoadTypedArrayElementHole *ins);
     bool visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic *ins);
+    bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
+    bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
     bool visitClampToUint8(MClampToUint8 *ins);
     bool visitLoadFixedSlot(MLoadFixedSlot *ins);
     bool visitStoreFixedSlot(MStoreFixedSlot *ins);
     bool visitGetPropertyCache(MGetPropertyCache *ins);
     bool visitGetPropertyPolymorphic(MGetPropertyPolymorphic *ins);
     bool visitSetPropertyPolymorphic(MSetPropertyPolymorphic *ins);
     bool visitGetElementCache(MGetElementCache *ins);
     bool visitBindNameCache(MBindNameCache *ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -787,19 +787,19 @@ IonBuilder::inlineMathImul(CallInfo &cal
 {
     if (callInfo.argc() != 2 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     MIRType returnType = getInlineReturnType();
     if (returnType != MIRType_Int32)
         return InliningStatus_NotInlined;
 
-    if (!IsNumberType(callInfo.getArg(0)->type()))
+    if (!IsNumberType(callInfo.getArg(0)->type()) || callInfo.getArg(0)->type() == MIRType_Float32)
         return InliningStatus_NotInlined;
-    if (!IsNumberType(callInfo.getArg(1)->type()))
+    if (!IsNumberType(callInfo.getArg(1)->type()) || callInfo.getArg(1)->type() == MIRType_Float32)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     MInstruction *first = MTruncateToInt32::New(callInfo.getArg(0));
     current->add(first);
 
     MInstruction *second = MTruncateToInt32::New(callInfo.getArg(1));
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -447,16 +447,22 @@ MConstant::printOpcode(FILE *fp) const
         fprintf(fp, value().toBoolean() ? "true" : "false");
         break;
       case MIRType_Int32:
         fprintf(fp, "0x%x", value().toInt32());
         break;
       case MIRType_Double:
         fprintf(fp, "%f", value().toDouble());
         break;
+      case MIRType_Float32:
+      {
+        float val = value().toDouble();
+        fprintf(fp, "%f", val);
+        break;
+      }
       case MIRType_Object:
         if (value().toObject().is<JSFunction>()) {
             JSFunction *fun = &value().toObject().as<JSFunction>();
             if (fun->displayAtom()) {
                 fputs("function ", fp);
                 FileEscapedString(fp, fun->displayAtom(), 0);
             } else {
                 fputs("unnamed function", fp);
@@ -478,16 +484,38 @@ MConstant::printOpcode(FILE *fp) const
       case MIRType_Magic:
         fprintf(fp, "magic");
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
+// Needs a static function to avoid overzealous optimizations by certain compilers (MSVC).
+static bool
+IsFloat32Representable(double x)
+{
+    float asFloat = static_cast<float>(x);
+    double floatAsDouble = static_cast<double>(asFloat);
+    return floatAsDouble == x;
+}
+
+bool
+MConstant::canProduceFloat32() const
+{
+    if (!IsNumberType(type()))
+        return false;
+
+    if (type() == MIRType_Int32)
+        return IsFloat32Representable(static_cast<double>(value_.toInt32()));
+    if (type() == MIRType_Double)
+        return IsFloat32Representable(value_.toDouble());
+    return true;
+}
+
 void
 MControlInstruction::printOpcode(FILE *fp) const
 {
     MDefinition::printOpcode(fp);
     for (size_t j = 0; j < numSuccessors(); j++)
         fprintf(fp, " block%d", getSuccessor(j)->id());
 }
 
@@ -594,16 +622,31 @@ MTableSwitch::New(MDefinition *ins, int3
 
 MGoto *
 MGoto::New(MBasicBlock *target)
 {
     JS_ASSERT(target);
     return new MGoto(target);
 }
 
+static void
+PrintBailoutKind(FILE *fp, BailoutKind bailoutKind)
+{
+    switch(bailoutKind) {
+      case Bailout_Normal: fprintf(fp, "(normal)"); break;
+      case Bailout_ArgumentCheck: fprintf(fp, "(args)"); break;
+      case Bailout_TypeBarrier: fprintf(fp, "(typebarrier)"); break;
+      case Bailout_Monitor: fprintf(fp, "(monitor)"); break;
+      case Bailout_BoundsCheck: fprintf(fp, "(boundscheck)"); break;
+      case Bailout_ShapeGuard: fprintf(fp, "(shapeguard)"); break;
+      case Bailout_CachedShapeGuard: fprintf(fp, "(cached shapeguard)"); break;
+      default: break;
+    }
+}
+
 void
 MUnbox::printOpcode(FILE *fp) const
 {
     PrintOpcodeName(fp, op());
     fprintf(fp, " ");
     getOperand(0)->printName(fp);
     fprintf(fp, " ");
 
@@ -615,22 +658,38 @@ MUnbox::printOpcode(FILE *fp) const
       case MIRType_Object: fprintf(fp, "to Object"); break;
       default: break;
     }
 
     switch (mode()) {
       case Fallible: fprintf(fp, " (fallible)"); break;
       case Infallible: fprintf(fp, " (infallible)"); break;
       case TypeBarrier: fprintf(fp, " (typebarrier)"); break;
-      case TypeGuard: fprintf(fp, " (typeguard)"); break;
       default: break;
     }
+
+    if (mode() == Infallible)
+        return;
+
+    fprintf(fp, " ");
+    PrintBailoutKind(fp, bailoutKind());
 }
 
 void
+MTypeBarrier::printOpcode(FILE *fp) const
+{
+    PrintOpcodeName(fp, op());
+    fprintf(fp, " ");
+    getOperand(0)->printName(fp);
+    fprintf(fp, " ");
+
+    PrintBailoutKind(fp, bailoutKind());
+ }
+ 
+void
 MPhi::removeOperand(size_t index)
 {
     MUse *use = getUseFor(index);
 
     JS_ASSERT(index < inputs_.length());
     JS_ASSERT(inputs_.length() > 1);
 
     JS_ASSERT(use->index() == index);
@@ -710,17 +769,19 @@ MakeMIRTypeSet(MIRType type)
 
 void
 jit::MergeTypes(MIRType *ptype, types::StackTypeSet **ptypeSet,
                 MIRType newType, types::StackTypeSet *newTypeSet)
 {
     if (newTypeSet && newTypeSet->empty())
         return;
     if (newType != *ptype) {
-        if (IsNumberType(newType) && IsNumberType(*ptype)) {
+        if (IsFloatType(newType) && IsFloatType(*ptype)) {
+            *ptype = MIRType_Float32;
+        } else if (IsNumberType(newType) && IsNumberType(*ptype)) {
             *ptype = MIRType_Double;
         } else if (*ptype != MIRType_Value) {
             if (!*ptypeSet)
                 *ptypeSet = MakeMIRTypeSet(*ptype);
             *ptype = MIRType_Value;
         } else if (*ptypeSet && (*ptypeSet)->empty()) {
             *ptype = newType;
         }
@@ -1134,16 +1195,53 @@ MBinaryArithInstruction::foldsTo(bool us
         return this;
 
     if (IsConstant(lhs, getIdentity()))
         return rhs; // x op id => x
 
     return this;
 }
 
+template<size_t Op> static void
+ConvertDefinitionToDouble(MDefinition *def, MInstruction *consumer)
+{
+    MInstruction *replace = MToDouble::New(def);
+    consumer->replaceOperand(Op, replace);
+    consumer->block()->insertBefore(consumer, replace);
+}
+
+static bool
+CheckUsesAreFloat32Consumers(MInstruction *ins)
+{
+    bool allConsumerUses = true;
+    for (MUseDefIterator use(ins); allConsumerUses && use; use++)
+        allConsumerUses &= use.def()->canConsumeFloat32();
+    return allConsumerUses;
+}
+
+void
+MBinaryArithInstruction::trySpecializeFloat32()
+{
+    MDefinition *left = lhs();
+    MDefinition *right = rhs();
+
+    if (!left->canProduceFloat32() || !right->canProduceFloat32()
+        || !CheckUsesAreFloat32Consumers(this))
+    {
+        if (left->type() == MIRType_Float32)
+            ConvertDefinitionToDouble<0>(left, this);
+        if (right->type() == MIRType_Float32)
+            ConvertDefinitionToDouble<1>(right, this);
+        return;
+    }
+
+    specialization_ = MIRType_Float32;
+    setResultType(MIRType_Float32);
+}
+
 bool
 MAbs::fallible() const
 {
     return !implicitTruncate_ && (!range() || !range()->isInt32());
 }
 
 MDefinition *
 MDiv::foldsTo(bool useValueNumbers)
@@ -1372,17 +1470,19 @@ MBinaryArithInstruction::infer(BaselineI
     // unless baseline type hints suggest it might be profitable
     if (!KnownNonStringPrimitive(getOperand(0)) || !KnownNonStringPrimitive(getOperand(1)))
         return inferFallback(inspector, pc);
 
     // Guess a result type based on the inputs.
     // Don't specialize for neither-integer-nor-double results.
     if (lhs == MIRType_Int32 && rhs == MIRType_Int32)
         setResultType(MIRType_Int32);
-    else if (lhs == MIRType_Double || rhs == MIRType_Double)
+    // Double operations are prioritary over float32 operations (i.e. if any operand needs
+    // a double as an input, convert all operands to doubles)
+    else if (IsFloatingPointType(lhs) || IsFloatingPointType(rhs))
         setResultType(MIRType_Double);
     else
         return inferFallback(inspector, pc);
 
     // If the operation has ever overflowed, use a double specialization.
     if (inspector->hasSeenDoubleResult(pc))
         setResultType(MIRType_Double);
 
@@ -1397,17 +1497,17 @@ MBinaryArithInstruction::infer(BaselineI
 
     JS_ASSERT(lhs < MIRType_String || lhs == MIRType_Value);
     JS_ASSERT(rhs < MIRType_String || rhs == MIRType_Value);
 
     MIRType rval = this->type();
 
     // Don't specialize values when result isn't double
     if (lhs == MIRType_Value || rhs == MIRType_Value) {
-        if (rval != MIRType_Double) {
+        if (!IsFloatingPointType(rval)) {
             specialization_ = MIRType_None;
             return;
         }
     }
 
     // Don't specialize as int32 if one of the operands is undefined,
     // since ToNumber(undefined) is NaN.
     if (rval == MIRType_Int32 && (lhs == MIRType_Undefined || rhs == MIRType_Undefined)) {
@@ -1953,16 +2053,38 @@ MToDouble::foldsTo(bool useValueNumbers)
     // Fold unnecessary numeric conversions.
     if (input()->isToInt32())
         replaceOperand(0, input()->getOperand(0));
 
     return this;
 }
 
 MDefinition *
+MToFloat32::foldsTo(bool useValueNumbers)
+{
+    if (input()->type() == MIRType_Float32)
+        return input();
+
+    // If x is a Float32, Float32(Double(x)) == x
+    if (input()->isToDouble() && input()->toToDouble()->input()->type() == MIRType_Float32)
+        return input()->toToDouble()->input();
+
+    if (input()->isConstant()) {
+        const Value &v = input()->toConstant()->value();
+        if (v.isNumber()) {
+            float out = v.toNumber();
+            MConstant *c = MConstant::New(DoubleValue(out));
+            c->setResultType(MIRType_Float32);
+            return c;
+        }
+    }
+    return this;
+}
+
+MDefinition *
 MToString::foldsTo(bool useValueNumbers)
 {
     MDefinition *in = input();
     if (in->type() == MIRType_String)
         return in;
     return this;
 }
 
@@ -2885,16 +3007,21 @@ jit::PropertyWriteNeedsTypeBarrier(JSCon
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeObject *object;
         if (!types->getTypeOrSingleObject(cx, i, &object))
             return false;
 
         if (!object || object->unknownProperties())
             continue;
 
+        // TI doesn't track TypedArray objects and should never insert a type
+        // barrier for them.
+        if (object->getTypedArrayType() < ScalarTypeRepresentation::TYPE_MAX)
+            continue;
+
         types::HeapTypeSet *property = object->getProperty(cx, id, false);
         if (!property) {
             success = false;
             break;
         }
         if (!TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet())) {
             // Either pobj or pvalue needs to be modified to filter out the
             // types which the value could have but are not in the property,
@@ -2924,16 +3051,18 @@ jit::PropertyWriteNeedsTypeBarrier(JSCon
     types::TypeObject *excluded = NULL;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeObject *object;
         if (!types->getTypeOrSingleObject(cx, i, &object))
             return false;
 
         if (!object || object->unknownProperties())
             continue;
+        if (object->getTypedArrayType() < ScalarTypeRepresentation::TYPE_MAX)
+            continue;
 
         types::HeapTypeSet *property = object->getProperty(cx, id, false);
         if (!property) {
             *result = true;
             return true;
         }
 
         if (TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -448,16 +448,23 @@ class MDefinition : public MNode
             return true;
 
         if (MIRType_Value != this->type())
             return false;
 
         return !resultTypeSet() || resultTypeSet()->mightBeType(ValueTypeFromMIRType(type));
     }
 
+    // Float32 specialization operations (see big comment in IonAnalysis before the Float32
+    // specialization algorithm).
+    virtual bool isFloat32Commutative() const { return false; }
+    virtual bool canProduceFloat32() const { return false; }
+    virtual bool canConsumeFloat32() const { return false; }
+    virtual void trySpecializeFloat32() {}
+
     // Returns the beginning of this definition's use chain.
     MUseIterator usesBegin() const {
         return uses_.begin();
     }
 
     // Returns the end of this definition's use chain.
     MUseIterator usesEnd() const {
         return uses_.end();
@@ -943,18 +950,31 @@ class MConstant : public MNullaryInstruc
 
     HashNumber valueHash() const;
     bool congruentTo(MDefinition *ins) const;
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 
+    bool updateForReplacement(MDefinition *def) {
+        MConstant *c = def->toConstant();
+        // During constant folding, we don't want to replace a float32
+        // value by a double value.
+        if (type() == MIRType_Float32)
+            return c->type() == MIRType_Float32;
+        if (type() == MIRType_Double)
+            return c->type() != MIRType_Float32;
+        return true;
+    }
+
     void computeRange();
     bool truncate();
+
+    bool canProduceFloat32() const;
 };
 
 class MParameter : public MNullaryInstruction
 {
     int32_t index_;
 
   public:
     static const int32_t THIS_SLOT = -1;
@@ -1718,16 +1738,20 @@ class MCall
     }
 
     void addArg(size_t argnum, MPassArg *arg);
 
     MDefinition *getArg(uint32_t index) const {
         return getOperand(NumNonArgumentOperands + index);
     }
 
+    void replaceArg(uint32_t index, MDefinition *def) {
+        replaceOperand(NumNonArgumentOperands + index, def);
+    }
+
     void rootTargetScript(JSFunction *target) {
         targetScript_.setRoot(target->nonLazyScript());
     }
     bool hasRootedScript() {
         return targetScript_ != NULL;
     }
 
     // For TI-informed monomorphic callsites.
@@ -2124,64 +2148,69 @@ JSOpToCondition(MCompare::CompareType co
 // is unpacked and returned as that type. Otherwise, it is considered a
 // deoptimization.
 class MUnbox : public MUnaryInstruction, public BoxInputsPolicy
 {
   public:
     enum Mode {
         Fallible,       // Check the type, and deoptimize if unexpected.
         Infallible,     // Type guard is not necessary.
-        TypeBarrier,    // Guard on the type, and act like a TypeBarrier on failure.
-        TypeGuard       // Guard on the type, and deoptimize otherwise.
+        TypeBarrier     // Guard on the type, and act like a TypeBarrier on failure.
     };
 
   private:
     Mode mode_;
-
-    MUnbox(MDefinition *ins, MIRType type, Mode mode)
+    BailoutKind bailoutKind_;
+
+    MUnbox(MDefinition *ins, MIRType type, Mode mode, BailoutKind kind)
       : MUnaryInstruction(ins),
         mode_(mode)
     {
         JS_ASSERT(ins->type() == MIRType_Value);
         JS_ASSERT(type == MIRType_Boolean ||
                   type == MIRType_Int32   ||
                   type == MIRType_Double  ||
                   type == MIRType_String  ||
                   type == MIRType_Object);
 
         setResultType(type);
         setResultTypeSet(ins->resultTypeSet());
         setMovable();
 
-        if (mode_ == TypeBarrier || mode_ == TypeGuard)
+        if (mode_ == TypeBarrier)
             setGuard();
-        if (mode_ == TypeGuard)
-            mode_ = Fallible;
-    }
-
+
+        bailoutKind_ = kind;
+    }
   public:
     INSTRUCTION_HEADER(Unbox)
     static MUnbox *New(MDefinition *ins, MIRType type, Mode mode)
     {
-        return new MUnbox(ins, type, mode);
+        BailoutKind kind = Bailout_Normal;
+        if (mode == TypeBarrier && ins->isEffectful())
+            kind = Bailout_TypeBarrier;
+        return new MUnbox(ins, type, mode, kind);
+    }
+
+    static MUnbox *New(MDefinition *ins, MIRType type, Mode mode, BailoutKind kind)
+    {
+        return new MUnbox(ins, type, mode, kind);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
     Mode mode() const {
         return mode_;
     }
     BailoutKind bailoutKind() const {
         // If infallible, no bailout should be generated.
         JS_ASSERT(fallible());
-        return mode() == Fallible
-               ? Bailout_Normal
-               : Bailout_TypeBarrier;
+        return bailoutKind_;
     }
     bool fallible() const {
         return mode() != Infallible;
     }
     bool congruentTo(MDefinition *ins) const {
         if (!ins->isUnbox() || ins->toUnbox()->mode() != mode())
             return false;
         return congruentIfOperandsEqual(ins);
@@ -2631,16 +2660,75 @@ class MToDouble
         return AliasSet::None();
     }
 
     void computeRange();
     bool truncate();
     bool isOperandTruncated(size_t index) const;
 };
 
+// Converts a primitive (either typed or untyped) to a float32. If the input is
+// not primitive at runtime, a bailout occurs.
+class MToFloat32
+  : public MUnaryInstruction,
+    public ToDoublePolicy
+{
+  public:
+    // Types of values which can be converted.
+    enum ConversionKind {
+        NonStringPrimitives,
+        NonNullNonStringPrimitives,
+        NumbersOnly
+    };
+
+  protected:
+    ConversionKind conversion_;
+
+    MToFloat32(MDefinition *def, ConversionKind conversion)
+      : MUnaryInstruction(def), conversion_(conversion)
+    {
+        setResultType(MIRType_Float32);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(ToFloat32)
+    static MToFloat32 *New(MDefinition *def, ConversionKind conversion = NonStringPrimitives) {
+        return new MToFloat32(def, conversion);
+    }
+    static MToFloat32 *NewAsmJS(MDefinition *def) {
+        return new MToFloat32(def, NonStringPrimitives);
+    }
+
+    ConversionKind conversion() const {
+        return conversion_;
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
+    virtual MDefinition *foldsTo(bool useValueNumbers);
+    bool congruentTo(MDefinition *ins) const {
+        if (!ins->isToFloat32() || ins->toToFloat32()->conversion() != conversion())
+            return false;
+        return congruentIfOperandsEqual(ins);
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+
+    void computeRange();
+    bool truncate();
+    bool isOperandTruncated(size_t index) const;
+
+    bool canConsumeFloat32() const { return true; }
+    bool canProduceFloat32() const { return true; }
+};
+
 // Converts a uint32 to a double (coming from asm.js).
 class MAsmJSUnsignedToDouble
   : public MUnaryInstruction
 {
     MAsmJSUnsignedToDouble(MDefinition *def)
       : MUnaryInstruction(def)
     {
         setResultType(MIRType_Double);
@@ -3096,16 +3184,18 @@ class MBinaryArithInstruction
     void infer(BaselineInspector *inspector,
                jsbytecode *pc, bool overflowed);
 
     void setInt32() {
         specialization_ = MIRType_Int32;
         setResultType(MIRType_Int32);
     }
 
+    virtual void trySpecializeFloat32();
+
     bool congruentTo(MDefinition *ins) const {
         return MBinaryInstruction::congruentTo(ins);
     }
     AliasSet getAliasSet() const {
         if (specialization_ >= MIRType_Object)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
     }
@@ -3471,16 +3561,18 @@ class MAdd : public MBinaryArithInstruct
         add->setResultType(type);
         if (type == MIRType_Int32) {
             add->setTruncated(true);
             add->setCommutative();
         }
         return add;
     }
 
+    bool isFloat32Commutative() const { return true; }
+
     double getIdentity() {
         return 0;
     }
 
     bool fallible();
     void computeRange();
     bool truncate();
     bool isOperandTruncated(size_t index) const;
@@ -3507,16 +3599,18 @@ class MSub : public MBinaryArithInstruct
             sub->setTruncated(true);
         return sub;
     }
 
     double getIdentity() {
         return 0;
     }
 
+    bool isFloat32Commutative() const { return true; }
+
     bool fallible();
     void computeRange();
     bool truncate();
     bool isOperandTruncated(size_t index) const;
 };
 
 class MMul : public MBinaryArithInstruction
 {
@@ -3579,16 +3673,18 @@ class MMul : public MBinaryArithInstruct
     }
 
     bool updateForReplacement(MDefinition *ins);
 
     bool fallible() {
         return canBeNegativeZero_ || canOverflow();
     }
 
+    bool isFloat32Commutative() const { return true; }
+
     void computeRange();
     bool truncate();
     bool isOperandTruncated(size_t index) const;
 
     Mode mode() { return mode_; }
 };
 
 class MDiv : public MBinaryArithInstruction
@@ -3647,16 +3743,18 @@ class MDiv : public MBinaryArithInstruct
     bool canBeDivideByZero() {
         return canBeDivideByZero_;
     }
 
     bool isUnsigned() {
         return unsigned_;
     }
 
+    bool isFloat32Commutative() const { return true; }
+
     bool fallible();
     bool truncate();
 };
 
 class MMod : public MBinaryArithInstruction
 {
     bool unsigned_;
     bool canBeNegativeDividend_;
@@ -3834,27 +3932,31 @@ class MFromCharCode
 class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
 {
     js::Vector<MUse, 2, IonAllocPolicy> inputs_;
 
     uint32_t slot_;
     bool hasBackedgeType_;
     bool triedToSpecialize_;
     bool isIterator_;
+    bool canProduceFloat32_;
+    bool canConsumeFloat32_;
 
 #if DEBUG
     bool specialized_;
     uint32_t capacity_;
 #endif
 
     MPhi(uint32_t slot, MIRType resultType)
       : slot_(slot),
         hasBackedgeType_(false),
         triedToSpecialize_(false),
-        isIterator_(false)
+        isIterator_(false),
+        canProduceFloat32_(false),
+        canConsumeFloat32_(false)
 #if DEBUG
         , specialized_(false)
         , capacity_(0)
 #endif
     {
         setResultType(resultType);
     }
 
@@ -3942,16 +4044,32 @@ class MPhi MOZ_FINAL : public MDefinitio
         // those two cases).
         MDefinition *first = getOperand(0);
         for (size_t i = 1, e = numOperands(); i < e; i++) {
             if (getOperand(i) != first && getOperand(i) != this)
                 return NULL;
         }
         return first;
     }
+
+    bool canProduceFloat32() const {
+        return canProduceFloat32_;
+    }
+
+    void setCanProduceFloat32(bool can) {
+        canProduceFloat32_ = can;
+    }
+
+    bool canConsumeFloat32() const {
+        return canConsumeFloat32_;
+    }
+
+    void setCanConsumeFloat32(bool can) {
+        canConsumeFloat32_ = can;
+    }
 };
 
 // The goal of a Beta node is to split a def at a conditionally taken
 // branch, so that uses dominated by it have a different name.
 class MBeta : public MUnaryInstruction
 {
   private:
     const Range *comparison_;
@@ -4946,17 +5064,17 @@ class MStoreElementCommon
         racy_ = true;
     }
 };
 
 // Store a value to a dense array slots vector.
 class MStoreElement
   : public MAryInstruction<3>,
     public MStoreElementCommon,
-    public SingleObjectPolicy
+    public MixPolicy<SingleObjectPolicy, NoFloatPolicy<2> >
 {
     bool needsHoleCheck_;
 
     MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value, bool needsHoleCheck) {
         setOperand(0, elements);
         setOperand(1, index);
         setOperand(2, value);
         needsHoleCheck_ = needsHoleCheck;
@@ -4996,17 +5114,17 @@ class MStoreElement
 
 // Like MStoreElement, but supports indexes >= initialized length. The downside
 // is that we cannot hoist the elements vector and bounds check, since this
 // instruction may update the (initialized) length and reallocate the elements
 // vector.
 class MStoreElementHole
   : public MAryInstruction<4>,
     public MStoreElementCommon,
-    public SingleObjectPolicy
+    public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >
 {
     MStoreElementHole(MDefinition *object, MDefinition *elements,
                       MDefinition *index, MDefinition *value) {
         setOperand(0, object);
         setOperand(1, elements);
         setOperand(2, index);
         setOperand(3, value);
         JS_ASSERT(elements->type() == MIRType_Elements);
@@ -5196,16 +5314,18 @@ class MLoadTypedArrayElement
     MDefinition *index() const {
         return getOperand(1);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::TypedArrayElement);
     }
 
     void computeRange();
+
+    bool canProduceFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 // Load a value from a typed array. Out-of-bounds accesses are handled using
 // a VM call.
 class MLoadTypedArrayElementHole
   : public MBinaryInstruction,
     public SingleObjectPolicy
 {
@@ -5244,28 +5364,31 @@ class MLoadTypedArrayElementHole
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::TypedArrayElement);
     }
+    bool canProduceFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 // Load a value fallibly or infallibly from a statically known typed array.
 class MLoadTypedArrayElementStatic
   : public MUnaryInstruction,
     public IntPolicy<0>
 {
     MLoadTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr)
       : MUnaryInstruction(ptr), typedArray_(typedArray), fallible_(true)
     {
         int type = typedArray_->type();
-        if (type == ScalarTypeRepresentation::TYPE_FLOAT32 || type == ScalarTypeRepresentation::TYPE_FLOAT64)
+        if (type == ScalarTypeRepresentation::TYPE_FLOAT32)
+            setResultType(MIRType_Float32);
+        else if (type == ScalarTypeRepresentation::TYPE_FLOAT64)
             setResultType(MIRType_Double);
         else
             setResultType(MIRType_Int32);
     }
 
     CompilerRoot<TypedArrayObject*> typedArray_;
     bool fallible_;
 
@@ -5294,16 +5417,17 @@ class MLoadTypedArrayElementStatic
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
     void computeRange();
     bool truncate();
+    bool canProduceFloat32() const { return typedArray_->type() == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 class MStoreTypedArrayElement
   : public MTernaryInstruction,
     public StoreTypedArrayPolicy
 {
     int arrayType_;
 
@@ -5357,16 +5481,18 @@ class MStoreTypedArrayElement
     }
     bool racy() const {
         return racy_;
     }
     void setRacy() {
         racy_ = true;
     }
     bool isOperandTruncated(size_t index) const;
+
+    bool canConsumeFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 class MStoreTypedArrayElementHole
   : public MAryInstruction<4>,
     public StoreTypedArrayHolePolicy
 {
     int arrayType_;
 
@@ -5420,16 +5546,18 @@ class MStoreTypedArrayElementHole
     }
     MDefinition *value() const {
         return getOperand(3);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
     bool isOperandTruncated(size_t index) const;
+
+    bool canConsumeFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 // Store a value infallibly to a statically known typed array.
 class MStoreTypedArrayElementStatic :
     public MBinaryInstruction
   , public StoreTypedArrayElementStaticPolicy
 {
     MStoreTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr, MDefinition *v)
@@ -5461,16 +5589,18 @@ class MStoreTypedArrayElementStatic :
     size_t length() const;
 
     MDefinition *ptr() const { return getOperand(0); }
     MDefinition *value() const { return getOperand(1); }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
     bool isOperandTruncated(size_t index) const;
+
+    bool canConsumeFloat32() const { return typedArray_->type() == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 // Compute an "effective address", i.e., a compound computation of the form:
 //   base + index * scale + displacement
 class MEffectiveAddress : public MBinaryInstruction
 {
     MEffectiveAddress(MDefinition *base, MDefinition *index, Scale scale, int32_t displacement)
       : MBinaryInstruction(base, index), scale_(scale), displacement_(displacement)
@@ -5580,17 +5710,17 @@ class MLoadFixedSlot
         return AliasSet::Load(AliasSet::FixedSlot);
     }
 
     bool mightAlias(MDefinition *store);
 };
 
 class MStoreFixedSlot
   : public MBinaryInstruction,
-    public SingleObjectPolicy
+    public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >
 {
     bool needsBarrier_;
     size_t slot_;
 
     MStoreFixedSlot(MDefinition *obj, MDefinition *rval, size_t slot, bool barrier)
       : MBinaryInstruction(obj, rval),
         needsBarrier_(barrier),
         slot_(slot)
@@ -6401,17 +6531,17 @@ class MForkJoinSlice
     bool possiblyCalls() const {
         return true;
     }
 };
 
 // Store to vp[slot] (slots that are not inline in an object).
 class MStoreSlot
   : public MBinaryInstruction,
-    public SingleObjectPolicy
+    public MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >
 {
     uint32_t slot_;
     MIRType slotType_;
     bool needsBarrier_;
 
     MStoreSlot(MDefinition *slots, uint32_t slot, MDefinition *value, bool barrier)
         : MBinaryInstruction(slots, value),
           slot_(slot),
@@ -6701,17 +6831,17 @@ class MCallSetProperty
     }
     bool possiblyCalls() const {
         return true;
     }
 };
 
 class MSetPropertyCache
   : public MSetPropertyInstruction,
-    public SingleObjectPolicy
+    public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >
 {
     bool needsTypeBarrier_;
 
     MSetPropertyCache(MDefinition *obj, MDefinition *value, HandlePropertyName name, bool strict,
                       bool typeBarrier)
       : MSetPropertyInstruction(obj, value, name, strict),
         needsTypeBarrier_(typeBarrier)
     {
@@ -7543,44 +7673,47 @@ class MGuardThreadLocalObject
         return true;
     }
 };
 
 // Given a value, guard that the value is in a particular TypeSet, then returns
 // that value.
 class MTypeBarrier
   : public MUnaryInstruction,
-    public BoxInputsPolicy
+    public TypeBarrierPolicy
 {
     BailoutKind bailoutKind_;
 
     MTypeBarrier(MDefinition *def, types::StackTypeSet *types, BailoutKind bailoutKind)
       : MUnaryInstruction(def)
     {
         JS_ASSERT(!types->unknown());
-        setResultType(MIRType_Value);
+
+        MIRType type = MIRTypeFromValueType(types->getKnownTypeTag());
+        setResultType(type);
         setResultTypeSet(types);
+
         setGuard();
         setMovable();
         bailoutKind_ = bailoutKind;
     }
 
   public:
     INSTRUCTION_HEADER(TypeBarrier)
 
     static MTypeBarrier *New(MDefinition *def, types::StackTypeSet *types) {
-        BailoutKind bailoutKind = def->isEffectful()
-                                  ? Bailout_TypeBarrier
-                                  : Bailout_Normal;
-        return new MTypeBarrier(def, types, bailoutKind);
+        BailoutKind kind = def->isEffectful() ? Bailout_TypeBarrier : Bailout_Normal;
+        return new MTypeBarrier(def, types, kind);
     }
     static MTypeBarrier *New(MDefinition *def, types::StackTypeSet *types,
-                             BailoutKind bailoutKind) {
-        return new MTypeBarrier(def, types, bailoutKind);
-    }
+                             BailoutKind kind) {
+        return new MTypeBarrier(def, types, kind);
+    }
+
+    void printOpcode(FILE *fp) const;
 
     TypePolicy *typePolicy() {
         return this;
     }
 
     bool congruentTo(MDefinition *def) const {
         return false;
     }
@@ -7588,16 +7721,27 @@ class MTypeBarrier
         return bailoutKind_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
     virtual bool neverHoist() const {
         return resultTypeSet()->empty();
     }
+
+    bool alwaysBails() const {
+        // If mirtype of input doesn't agree with mirtype of barrier,
+        // we will definitely bail.
+        MIRType type = MIRTypeFromValueType(resultTypeSet()->getKnownTypeTag());
+        if (type == MIRType_Value)
+            return false;
+        if (input()->type() == MIRType_Value)
+            return false;
+        return input()->type() != type;
+    }
 };
 
 // Like MTypeBarrier, guard that the value is in the given type set. This is
 // used before property writes to ensure the value being written is represented
 // in the property types for the object.
 class MMonitorTypes : public MUnaryInstruction, public BoxInputsPolicy
 {
     const types::StackTypeSet *typeSet_;
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -71,16 +71,17 @@ namespace jit {
     _(Return)                                                               \
     _(Throw)                                                                \
     _(Box)                                                                  \
     _(Unbox)                                                                \
     _(GuardObject)                                                          \
     _(GuardString)                                                          \
     _(AssertRange)                                                          \
     _(ToDouble)                                                             \
+    _(ToFloat32)                                                            \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(ToString)                                                             \
     _(NewSlots)                                                             \
     _(NewParallelArray)                                                     \
     _(NewArray)                                                             \
     _(NewObject)                                                            \
     _(NewDeclEnvObject)                                                     \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -158,16 +158,17 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(CharCodeAt)
     UNSAFE_OP(FromCharCode)
     SAFE_OP(Return)
     CUSTOM_OP(Throw)
     SAFE_OP(Box)     // Boxing just creates a JSVal, doesn't alloc.
     SAFE_OP(Unbox)
     SAFE_OP(GuardObject)
     SAFE_OP(ToDouble)
+    SAFE_OP(ToFloat32)
     SAFE_OP(ToInt32)
     SAFE_OP(TruncateToInt32)
     SAFE_OP(MaybeToDoubleElement)
     CUSTOM_OP(ToString)
     SAFE_OP(NewSlots)
     CUSTOM_OP(NewArray)
     CUSTOM_OP(NewObject)
     CUSTOM_OP(NewCallObject)
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1101,16 +1101,22 @@ MMod::computeRange()
 
 void
 MToDouble::computeRange()
 {
     setRange(new Range(getOperand(0)));
 }
 
 void
+MToFloat32::computeRange()
+{
+    setRange(new Range(getOperand(0)));
+}
+
+void
 MTruncateToInt32::computeRange()
 {
     Range *output = new Range(getOperand(0));
     output->wrapAroundToInt32();
     setRange(output);
 }
 
 void
@@ -1825,16 +1831,30 @@ MToDouble::truncate()
     setResultType(MIRType_Int32);
     if (range())
         range()->wrapAroundToInt32();
 
     return true;
 }
 
 bool
+MToFloat32::truncate()
+{
+    JS_ASSERT(type() == MIRType_Float32);
+
+    // We use the return type to flag that this MToFloat32 sould be replaced by a
+    // MTruncateToInt32 when modifying the graph.
+    setResultType(MIRType_Int32);
+    if (range())
+        range()->wrapAroundToInt32();
+
+    return true;
+}
+
+bool
 MLoadTypedArrayElementStatic::truncate()
 {
     setInfallible();
     return false;
 }
 
 bool
 MDefinition::isOperandTruncated(size_t index) const
@@ -1876,16 +1896,24 @@ bool
 MToDouble::isOperandTruncated(size_t index) const
 {
     // The return type is used to flag that we are replacing this Double by a
     // Truncate of its operand if needed.
     return type() == MIRType_Int32;
 }
 
 bool
+MToFloat32::isOperandTruncated(size_t index) const
+{
+    // The return type is used to flag that we are replacing this Float32 by a
+    // Truncate of its operand if needed.
+    return type() == MIRType_Int32;
+}
+
+bool
 MStoreTypedArrayElement::isOperandTruncated(size_t index) const
 {
     return index == 2 && !isFloatArray();
 }
 
 bool
 MStoreTypedArrayElementHole::isOperandTruncated(size_t index) const
 {
--- a/js/src/jit/RegisterAllocator.cpp
+++ b/js/src/jit/RegisterAllocator.cpp
@@ -355,100 +355,100 @@ AllocationIntegrityState::addPredecessor
 
     return worklist.append(item);
 }
 
 void
 AllocationIntegrityState::dump()
 {
 #ifdef DEBUG
-    printf("Register Allocation:\n");
+    fprintf(stderr, "Register Allocation:\n");
 
     for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
         LBlock *block = graph.getBlock(blockIndex);
         MBasicBlock *mir = block->mir();
 
-        printf("\nBlock %lu", static_cast<unsigned long>(blockIndex));
+        fprintf(stderr, "\nBlock %lu", static_cast<unsigned long>(blockIndex));
         for (size_t i = 0; i < mir->numSuccessors(); i++)
-            printf(" [successor %u]", mir->getSuccessor(i)->id());
-        printf("\n");
+            fprintf(stderr, " [successor %u]", mir->getSuccessor(i)->id());
+        fprintf(stderr, "\n");
 
         for (size_t i = 0; i < block->numPhis(); i++) {
             InstructionInfo &info = blocks[blockIndex].phis[i];
             LPhi *phi = block->getPhi(i);