Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 21 Nov 2012 19:50:22 -0500
changeset 113925 20ec9014f220c96a0434a4043f5a5cabb0cd5e37
parent 113867 39325f2d56b9f731dcde1e4fc443c0617f550c6a (current diff)
parent 113924 8f61af76095066558acb9018551dc0b0b5a90f72 (diff)
child 113933 fcad43f8558ff673945fde97e018333604a0657e
child 113966 5a755dd85a35f65062375ec2e05f140e82e2e41d
push id23893
push userryanvm@gmail.com
push dateThu, 22 Nov 2012 00:50:15 +0000
treeherdermozilla-central@20ec9014f220 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone20.0a1
first release with
nightly linux32
20ec9014f220 / 20.0a1 / 20121122030751 / files
nightly linux64
20ec9014f220 / 20.0a1 / 20121122030751 / files
nightly mac
20ec9014f220 / 20.0a1 / 20121122030751 / files
nightly win32
20ec9014f220 / 20.0a1 / 20121122030751 / files
nightly win64
20ec9014f220 / 20.0a1 / 20121122030751 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge the last PGO-green inbound changeset to m-c.
--- a/accessible/src/base/AccEvent.cpp
+++ b/accessible/src/base/AccEvent.cpp
@@ -27,17 +27,20 @@ using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccEvent constructors
 
 AccEvent::AccEvent(uint32_t aEventType, Accessible* aAccessible,
                    EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) :
   mEventType(aEventType), mEventRule(aEventRule), mAccessible(aAccessible)
 {
-  CaptureIsFromUserInput(aIsFromUserInput);
+  if (aIsFromUserInput == eAutoDetect)
+    mIsFromUserInput = nsEventStateManager::IsHandlingUserInput();
+  else
+    mIsFromUserInput = aIsFromUserInput == eFromUserInput ? true : false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccEvent public methods
 
 already_AddRefed<nsAccEvent>
 AccEvent::CreateXPCOMObject()
 {
@@ -59,39 +62,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAccessible");
   cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mAccessible));
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AccEvent, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AccEvent, Release)
 
 ////////////////////////////////////////////////////////////////////////////////
-// AccEvent protected methods
-
-void
-AccEvent::CaptureIsFromUserInput(EIsFromUserInput aIsFromUserInput)
-{
-  if (aIsFromUserInput != eAutoDetect) {
-    mIsFromUserInput = aIsFromUserInput == eFromUserInput ? true : false;
-    return;
-  }
-
-  DocAccessible* document = mAccessible->Document();
-  if (!document) {
-    NS_ASSERTION(mAccessible == ApplicationAcc(),
-                 "Accessible other than application should always have a doc!");
-    return;
-  }
-
-  mIsFromUserInput =
-    document->PresContext()->EventStateManager()->IsHandlingUserInputExternal();
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
 // AccStateChangeEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 already_AddRefed<nsAccEvent>
 AccStateChangeEvent::CreateXPCOMObject()
 {
   nsAccEvent* event = new nsAccStateChangeEvent(this);
   NS_IF_ADDREF(event);
--- a/accessible/src/base/AccEvent.h
+++ b/accessible/src/base/AccEvent.h
@@ -110,23 +110,16 @@ public:
 
   /**
    * Reference counting and cycle collection.
    */
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AccEvent)
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AccEvent)
 
 protected:
-
-  /**
-   * Determine whether the event is from user input by event state manager if
-   * it's not pointed explicetly.
-   */
-  void CaptureIsFromUserInput(EIsFromUserInput aIsFromUserInput);
-
   bool mIsFromUserInput;
   uint32_t mEventType;
   EEventRule mEventRule;
   nsRefPtr<Accessible> mAccessible;
 
   friend class NotificationController;
   friend class AccReorderEvent;
 };
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -511,18 +511,27 @@ nsBrowserContentHandler.prototype = {
       }
     }
     if (cmdLine.handleFlag("preferences", false)) {
       openPreferences();
       cmdLine.preventDefault = true;
     }
     if (cmdLine.handleFlag("silent", false))
       cmdLine.preventDefault = true;
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    if (cmdLine.findFlag("private-window", false) >= 0) {
+      openWindow(null, this.chromeURL, "_blank",
+        "chrome,dialog=no,private,all" + this.getFeatures(cmdLine),
+        "about:privatebrowsing");
+      cmdLine.preventDefault = true;
+    }
+#else
     if (cmdLine.findFlag("private-toggle", false) >= 0)
       cmdLine.preventDefault = true;
+#endif
 
     var searchParam = cmdLine.handleFlagWithParam("search", false);
     if (searchParam) {
       doSearch(searchParam, cmdLine);
       cmdLine.preventDefault = true;
     }
 
     // The global PB Service consumes this flag, so only eat it in per-window
--- a/browser/locales/en-US/chrome/browser/taskbar.properties
+++ b/browser/locales/en-US/chrome/browser/taskbar.properties
@@ -5,10 +5,12 @@
 taskbar.tasks.newTab.label=Open new tab
 taskbar.tasks.newTab.description=Open a new browser tab.
 taskbar.tasks.newWindow.label=Open new window
 taskbar.tasks.newWindow.description=Open a new browser window.
 taskbar.tasks.enterPrivacyMode.label=Enter private browsing
 taskbar.tasks.enterPrivacyMode.description=Start private browsing. The current session will be saved.
 taskbar.tasks.exitPrivacyMode.label=Quit private browsing
 taskbar.tasks.exitPrivacyMode.description=Quit private browsing and restore the previous session.
+taskbar.tasks.newPrivateWindow.label=New private window
+taskbar.tasks.newPrivateWindow.description=Open a new window in private browsing mode.
 taskbar.frequent.label=Frequent
 taskbar.recent.label=Recent
--- a/browser/modules/Makefile.in
+++ b/browser/modules/Makefile.in
@@ -26,13 +26,15 @@ EXTRA_JS_MODULES = \
 	webappsUI.jsm \
 	webrtcUI.jsm \
 	KeywordURLResetPrompter.jsm \
 	$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 EXTRA_JS_MODULES += \
 	WindowsPreviewPerTab.jsm \
+	$(NULL)
+EXTRA_PP_JS_MODULES = \
 	WindowsJumpLists.jsm \
 	$(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/modules/WindowsJumpLists.jsm
+++ b/browser/modules/WindowsJumpLists.jsm
@@ -69,19 +69,21 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "_taskbarService",
                                    "@mozilla.org/windows-taskbar;1",
                                    "nsIWinTaskbar");
 
 XPCOMUtils.defineLazyServiceGetter(this, "_winShellService",
                                    "@mozilla.org/browser/shell-service;1",
                                    "nsIWindowsShellService");
 
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 XPCOMUtils.defineLazyServiceGetter(this, "_privateBrowsingSvc",
                                    "@mozilla.org/privatebrowsing;1",
                                    "nsIPrivateBrowsingService");
+#endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 /**
  * Global functions
  */
 
@@ -99,39 +101,51 @@ var tasksCfg = [
    * title       - Task title displayed in the list. (strings in the table are temp fillers.)
    * description - Tooltip description on the list item.
    * args        - Command line args to invoke the task.
    * iconIndex   - Optional win icon index into the main application for the
    *               list item.
    * open        - Boolean indicates if the command should be visible after the browser opens.
    * close       - Boolean indicates if the command should be visible after the browser closes.
    */
-  // Open new window
+  // Open new tab
   {
     get title()       _getString("taskbar.tasks.newTab.label"),
     get description() _getString("taskbar.tasks.newTab.description"),
     args:             "-new-tab about:blank",
     iconIndex:        3, // New window icon
     open:             true,
     close:            true, // The jump list already has an app launch icon, but
                             // we don't always update the list on shutdown.
                             // Thus true for consistency.
   },
 
-  // Open new tab
+  // Open new window
   {
     get title()       _getString("taskbar.tasks.newWindow.label"),
     get description() _getString("taskbar.tasks.newWindow.description"),
     args:             "-browser",
     iconIndex:        2, // New tab icon
     open:             true,
     close:            true, // No point, but we don't always update the list on
-                            //  shutdown.  Thus true for consistency.
+                            // shutdown. Thus true for consistency.
   },
 
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  // Open new private window
+  {
+    get title()       _getString("taskbar.tasks.newPrivateWindow.label"),
+    get description() _getString("taskbar.tasks.newPrivateWindow.description"),
+    args:             "-private-window",
+    iconIndex:        4, // Private browsing mode icon
+    open:             true,
+    close:            true, // No point, but we don't always update the list on
+                            // shutdown. Thus true for consistency.
+  },
+#else
   // Toggle the Private Browsing mode
   {
     get title() {
       if (_privateBrowsingSvc.privateBrowsingEnabled)
         return _getString("taskbar.tasks.exitPrivacyMode.label");
       else
         return _getString("taskbar.tasks.enterPrivacyMode.label");
     },
@@ -147,16 +161,17 @@ var tasksCfg = [
       // Don't show when inside permanent private browsing mode
       return !PrivateBrowsingUtils.permanentPrivateBrowsing;
     },
     get close() {
       // Don't show when inside permanent private browsing mode
       return !PrivateBrowsingUtils.permanentPrivateBrowsing;
     },
   },
+#endif
 ];
 
 /////////////////////////////////////////////////////////////////////////////
 // Implementation
 
 this.WinTaskbarJumpList =
 {
   _builder: null,
@@ -509,27 +524,31 @@ this.WinTaskbarJumpList =
     this._builder = _taskbarService.createJumpListBuilder();
     if (!this._builder || !this._builder.available)
       return false;
 
     return true;
   },
 
   _initObs: function WTBJL__initObs() {
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     Services.obs.addObserver(this, "private-browsing", false);
+#endif
     // If the browser is closed while in private browsing mode, the "exit"
     // notification is fired on quit-application-granted.
     // History cleanup can happen at profile-change-teardown.
     Services.obs.addObserver(this, "profile-before-change", false);
     Services.obs.addObserver(this, "browser:purge-session-history", false);
     _prefs.addObserver("", this, false);
   },
  
   _freeObs: function WTBJL__freeObs() {
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     Services.obs.removeObserver(this, "private-browsing");
+#endif
     Services.obs.removeObserver(this, "profile-before-change");
     Services.obs.removeObserver(this, "browser:purge-session-history");
     _prefs.removeObserver("", this);
   },
 
   _updateTimer: function WTBJL__updateTimer() {
     if (this._enabled && !this._shuttingDown && !this._timer) {
       this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@@ -585,21 +604,21 @@ this.WinTaskbarJumpList =
 
       case "profile-before-change":
         this._shutdown();
       break;
 
       case "browser:purge-session-history":
         this.update();
       break;
-
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
       case "private-browsing":
         this.update();
       break;
-
+#endif
       case "idle":
         if (this._timer) {
           this._timer.cancel();
           delete this._timer;
         }
       break;
 
       case "back":
--- a/content/base/public/DirectionalityUtils.h
+++ b/content/base/public/DirectionalityUtils.h
@@ -2,34 +2,39 @@
  *
  * 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 DirectionalityUtils_h___
 #define DirectionalityUtils_h___
 
+#include "prtypes.h"
+#include "mozilla/StandardInteger.h"
+
 class nsIContent;
 class nsIDocument;
 class nsINode;
+class nsAString;
+class nsAttrValue;
+class nsTextNode;
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 namespace mozilla {
 
-namespace directionality {
-
 enum Directionality {
-  eDir_NotSet = 0,
-  eDir_RTL    = 1,
-  eDir_LTR    = 2
+  eDir_NotSet,
+  eDir_RTL,
+  eDir_LTR,
+  eDir_Auto
 };
 
 /**
  * Set the directionality of an element according to the algorithm defined at
  * http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-directionality,
  * not including elements with auto direction.
  *
  * @return the directionality that the element was set to
@@ -39,17 +44,90 @@ Directionality RecomputeDirectionality(m
 
 /**
  * Set the directionality of any descendants of a node that do not themselves
  * have a dir attribute.
  * For performance reasons we walk down the descendant tree in the rare case
  * of setting the dir attribute, rather than walking up the ancestor tree in
  * the much more common case of getting the element's directionality.
  */
-void SetDirectionalityOnDescendants(mozilla::dom::Element* aElement, 
+void SetDirectionalityOnDescendants(mozilla::dom::Element* aElement,
                                     Directionality aDir,
                                     bool aNotify = true);
 
-} // end namespace directionality
+/**
+ * Walk the descendants of a node in tree order and, for any text node
+ * descendant that determines the directionality of some element and is not a
+ * descendant of another descendant of the original node with dir=auto,
+ * redetermine that element's directionality
+  */
+void WalkDescendantsResetAutoDirection(mozilla::dom::Element* aElement);
+
+/**
+ * After setting dir=auto on an element, walk its descendants in tree order.
+ * If the node doesn't have the NODE_ANCESTOR_HAS_DIR_AUTO flag, set the
+ * NODE_ANCESTOR_HAS_DIR_AUTO flag on all of its descendants.
+ * Resolve the directionality of the element by the "downward propagation
+ * algorithm" (defined in section 3 in the comments at the beginning of
+ * DirectionalityUtils.cpp)
+ */
+void WalkDescendantsSetDirAuto(mozilla::dom::Element* aElement,
+                               bool aNotify = true);
+
+/**
+ * After unsetting dir=auto on an element, walk its descendants in tree order,
+ * skipping any that have dir=auto themselves, and unset the
+ * NODE_ANCESTOR_HAS_DIR_AUTO flag
+ */
+void WalkDescendantsClearAncestorDirAuto(mozilla::dom::Element* aElement);
+
+/**
+ * Walk the parent chain of a text node whose dir attribute has been removed and
+ * reset the direction of any of its ancestors which have dir=auto and whose
+ * directionality is determined by a text node descendant.
+ */
+void WalkAncestorsResetAutoDirection(mozilla::dom::Element* aElement,
+                                     bool aNotify = true);
+
+/**
+ * When the contents of a text node have changed, deal with any elements whose
+ * directionality needs to change
+ */
+void SetDirectionFromChangedTextNode(nsIContent* aTextNode, uint32_t aOffset,
+                                     const PRUnichar* aBuffer, uint32_t aLength,
+                                     bool aNotify);
+
+/**
+ * When a text node is appended to an element, find any ancestors with dir=auto
+ * whose directionality will be determined by the text node
+ */
+void SetDirectionFromNewTextNode(nsTextNode* aTextNode);
+
+/**
+ * When a text node is removed from a document, find any ancestors whose
+ * directionality it determined and redetermine their directionality
+ */
+void ResetDirectionSetByTextNode(nsTextNode* aTextNode);
+
+/**
+ * Set the directionality of an element according to the directionality of the
+ * text in aValue
+ */
+void SetDirectionalityFromValue(mozilla::dom::Element* aElement,
+                                const nsAString& aValue,
+                                bool aNotify);
+
+/**
+ * Called when setting the dir attribute on an element, immediately after
+ * AfterSetAttr. This is instead of using BeforeSetAttr or AfterSetAttr, because
+ * in AfterSetAttr we don't know the old value, so we can't identify all cases
+ * where we need to walk up or down the document tree and reset the direction;
+ * and in BeforeSetAttr we can't do the walk because this element hasn't had the
+ * value set yet so the results will be wrong.
+ */
+void OnSetDirAttr(mozilla::dom::Element* aElement,
+                  const nsAttrValue* aNewValue,
+                  bool hadValidDir,
+                  bool aNotify);
 
 } // end namespace mozilla
 
 #endif /* DirectionalityUtils_h___ */
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -277,44 +277,43 @@ public:
 
   /**
    * Returns an atom holding the name of the "class" attribute on this
    * content node (if applicable).  Returns null if there is no
    * "class" attribute for this type of content node.
    */
   virtual nsIAtom *GetClassAttributeName() const;
 
-  inline directionality::Directionality GetDirectionality() const {
+  inline Directionality GetDirectionality() const {
     if (HasFlag(NODE_HAS_DIRECTION_RTL)) {
-      return directionality::eDir_RTL;
+      return eDir_RTL;
     }
 
     if (HasFlag(NODE_HAS_DIRECTION_LTR)) {
-      return directionality::eDir_LTR;
+      return eDir_LTR;
     }
 
-    return directionality::eDir_NotSet;
+    return eDir_NotSet;
   }
 
-  inline void SetDirectionality(directionality::Directionality aDir,
-                                bool aNotify) {
+  inline void SetDirectionality(Directionality aDir, bool aNotify) {
     UnsetFlags(NODE_ALL_DIRECTION_FLAGS);
     if (!aNotify) {
       RemoveStatesSilently(DIRECTION_STATES);
     }
 
     switch (aDir) {
-      case (directionality::eDir_RTL):
+      case (eDir_RTL):
         SetFlags(NODE_HAS_DIRECTION_RTL);
         if (!aNotify) {
           AddStatesSilently(NS_EVENT_STATE_RTL);
         }
         break;
 
-      case(directionality::eDir_LTR):
+      case(eDir_LTR):
         SetFlags(NODE_HAS_DIRECTION_LTR);
         if (!aNotify) {
           AddStatesSilently(NS_EVENT_STATE_LTR);
         }
         break;
 
       default:
         break;
@@ -327,16 +326,25 @@ public:
      */
     if (aNotify) {
       UpdateState(true);
     }
   }
 
   bool GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult);
 
+  // The bdi element defaults to dir=auto if it has no dir attribute set.
+  // Other elements will only have dir=auto if they have an explicit dir=auto,
+  // which will mean that HasValidDir() returns true but HasFixedDir() returns
+  // false
+  inline bool HasDirAuto() const {
+    return (!HasFixedDir() &&
+            (HasValidDir() || NodeInfo()->Equals(nsGkAtoms::bdi)));
+  }
+
 protected:
   /**
    * Method to get the _intrinsic_ content state of this element.  This is the
    * state that is independent of the element's presentation.  To get the full
    * content state, use State().  See nsEventStates.h for
    * the possible bits that could be set here.
    */
   virtual nsEventStates IntrinsicState() const;
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -414,17 +414,17 @@ public:
    * Set the sandbox flags for this document.
    * @see nsSandboxFlags.h for the possible flags
    */
   void SetSandboxFlags(uint32_t sandboxFlags)
   {
     mSandboxFlags = sandboxFlags;
   }
 
-  inline mozilla::directionality::Directionality GetDocumentDirectionality() {
+  inline mozilla::Directionality GetDocumentDirectionality() {
     return mDirectionality;
   }
   
   /**
    * Access HTTP header data (this may also get set from other
    * sources, like HTML META tags).
    */
   virtual void GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const = 0;
@@ -1911,17 +1911,17 @@ protected:
   uint32_t mBidiOptions;
 
   // The sandbox flags on the document. These reflect the value of the sandbox attribute of the
   // associated IFRAME or CSP-protectable content, if existent. These are set at load time and
   // are immutable - see nsSandboxFlags.h for the possible flags.
   uint32_t mSandboxFlags;
 
   // The root directionality of this document.
-  mozilla::directionality::Directionality mDirectionality;
+  mozilla::Directionality mDirectionality;
 
   nsCString mContentLanguage;
 private:
   nsCString mContentType;
 protected:
 
   // The document's security info
   nsCOMPtr<nsISupports> mSecurityInfo;
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -1290,18 +1290,31 @@ private:
     // Set if element has pointer locked
     ElementHasPointerLock,
     // Set if the node may have DOMMutationObserver attached to it.
     NodeMayHaveDOMMutationObserver,
     // Set if node is Content
     NodeIsContent,
     // Set if the node has animations or transitions
     ElementHasAnimations,
-    // Set if node has a dir attribute with a valid value (ltr or rtl)
+    // Set if node has a dir attribute with a valid value (ltr, rtl, or auto)
     NodeHasValidDirAttribute,
+    // Set if node has a dir attribute with a fixed value (ltr or rtl, NOT auto)
+    NodeHasFixedDir,
+    // Set if the node has dir=auto and has a property pointing to the text
+    // node that determines its direction
+    NodeHasDirAutoSet,
+    // Set if the node is a text node descendant of a node with dir=auto
+    // and has a TextNodeDirectionalityMap property listing the elements whose
+    // direction it determines.
+    NodeHasTextNodeDirectionalityMap,
+    // Set if the node has dir=auto.
+    NodeHasDirAuto,
+    // Set if a node in the node's parent chain has dir=auto.
+    NodeAncestorHasDirAuto,
     // Guard value
     BooleanFlagCount
   };
 
   void SetBoolFlag(BooleanFlag name, bool value) {
     PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
     mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name);
   }
@@ -1362,16 +1375,62 @@ public:
   bool HasPointerLock() const { return GetBoolFlag(ElementHasPointerLock); }
   void SetPointerLock() { SetBoolFlag(ElementHasPointerLock); }
   void ClearPointerLock() { ClearBoolFlag(ElementHasPointerLock); }
   bool MayHaveAnimations() { return GetBoolFlag(ElementHasAnimations); }
   void SetMayHaveAnimations() { SetBoolFlag(ElementHasAnimations); }
   void SetHasValidDir() { SetBoolFlag(NodeHasValidDirAttribute); }
   void ClearHasValidDir() { ClearBoolFlag(NodeHasValidDirAttribute); }
   bool HasValidDir() const { return GetBoolFlag(NodeHasValidDirAttribute); }
+  void SetHasFixedDir() {
+    MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
+               "SetHasFixedDir on text node");
+    SetBoolFlag(NodeHasFixedDir);
+  }
+  void ClearHasFixedDir() {
+    MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
+               "ClearHasFixedDir on text node");
+    ClearBoolFlag(NodeHasFixedDir);
+  }
+  bool HasFixedDir() const { return GetBoolFlag(NodeHasFixedDir); }
+  void SetHasDirAutoSet() {
+    MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
+               "SetHasDirAutoSet on text node");
+    SetBoolFlag(NodeHasDirAutoSet);
+  }
+  void ClearHasDirAutoSet() {
+    MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
+               "ClearHasDirAutoSet on text node");
+    ClearBoolFlag(NodeHasDirAutoSet);
+  }
+  bool HasDirAutoSet() const
+    { return GetBoolFlag(NodeHasDirAutoSet); }
+  void SetHasTextNodeDirectionalityMap() {
+    MOZ_ASSERT(NodeType() == nsIDOMNode::TEXT_NODE,
+               "SetHasTextNodeDirectionalityMap on non-text node");
+    SetBoolFlag(NodeHasTextNodeDirectionalityMap);
+  }
+  void ClearHasTextNodeDirectionalityMap() {
+    MOZ_ASSERT(NodeType() == nsIDOMNode::TEXT_NODE,
+               "ClearHasTextNodeDirectionalityMap on non-text node");
+    ClearBoolFlag(NodeHasTextNodeDirectionalityMap);
+  }
+  bool HasTextNodeDirectionalityMap() const
+    { return GetBoolFlag(NodeHasTextNodeDirectionalityMap); }
+
+  void SetHasDirAuto() { SetBoolFlag(NodeHasDirAuto); }
+  void ClearHasDirAuto() { ClearBoolFlag(NodeHasDirAuto); }
+  bool HasDirAuto() const { return GetBoolFlag(NodeHasDirAuto); }
+
+  void SetAncestorHasDirAuto() { SetBoolFlag(NodeAncestorHasDirAuto); }
+  void ClearAncestorHasDirAuto() { ClearBoolFlag(NodeAncestorHasDirAuto); }
+  bool AncestorHasDirAuto() const { return GetBoolFlag(NodeAncestorHasDirAuto); }
+
+  bool NodeOrAncestorHasDirAuto() const
+    { return HasDirAuto() || AncestorHasDirAuto(); }
 protected:
   void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
   void SetInDocument() { SetBoolFlag(IsInDocument); }
   void SetNodeIsContent() { SetBoolFlag(NodeIsContent); }
   void ClearInDocument() { ClearBoolFlag(IsInDocument); }
   void SetIsElement() { SetBoolFlag(NodeIsElement); }
   void SetHasID() { SetBoolFlag(ElementHasID); }
   void ClearHasID() { ClearBoolFlag(ElementHasID); }
--- a/content/base/src/DirectionalityUtils.cpp
+++ b/content/base/src/DirectionalityUtils.cpp
@@ -1,33 +1,562 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=78: */
 /* 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/. */
 
+/*
+  Implementation description from https://etherpad.mozilla.org/dir-auto
+
+  Static case
+  ===========
+  When we see a new content node with @dir=auto from the parser, we set the
+  NodeHasDirAuto flag on the node.  We won't have enough information to
+  decide the directionality of the node at this point.
+
+  When we bind a new content node to the document, if its parent has either of
+  the NodeAncestorHasDirAuto or NodeHasDirAuto flags, we set the
+  NodeAncestorHasDirAuto flag on the node.
+
+  When a new input with @type=text/search/tel/url/email and @dir=auto is added
+  from the parser, we resolve the directionality based on its @value.
+
+  When a new text node with non-neutral content is appended to a textarea
+  element with NodeHasDirAuto, if the directionality of the textarea element
+  is still unresolved, it is resolved based on the value of the text node.
+  Elements with unresolved directionality behave as LTR.
+
+  When a new text node with non-neutral content is appended to an element that
+  is not a textarea but has either of the NodeAncestorHasDirAuto or
+  NodeHasDirAuto flags, we walk up the parent chain while the
+  NodeAncestorHasDirAuto flag is present, and when we reach an element with
+  NodeHasDirAuto and no resolved directionality, we resolve the directionality
+  based on the contents of the text node and cease walking the parent chain.
+  Note that we should ignore elements with NodeHasDirAuto with resolved
+  directionality, so that the second text node in this example tree doesn't
+  affect the directionality of the div:
+
+  <div dir=auto>
+    <span>foo</span>
+    <span>بار</span>
+  </div>
+
+  The parent chain walk will be aborted if we hit a script or style element, or
+  if we hit an element with @dir=ltr or @dir=rtl.
+
+  I will call this algorithm "upward propagation".
+
+  Each text node should maintain a list of elements which have their
+  directionality determined by the first strong character of that text node.
+  This is useful to make dynamic changes more efficient.  One way to implement
+  this is to have a per-document hash table mapping a text node to a set of
+  elements.  I'll call this data structure TextNodeDirectionalityMap. The
+  algorithm for appending a new text node above needs to update this data
+  structure.
+
+  *IMPLEMENTATION NOTE*
+  In practice, the implementation uses two per-node properties:
+
+  dirAutoSetBy, which is set on a node with auto-directionality, and points to
+  the textnode that contains the strong character which determines the
+  directionality of the node.
+
+  textNodeDirectionalityMap, which is set on a text node and points to a hash
+  table listing the nodes whose directionality is determined by the text node.
+
+  Handling dynamic changes
+  ========================
+
+  We need to handle the following cases:
+
+  1. When the value of an input element with @type=text/search/tel/url/email is
+  changed, if it has NodeHasDirAuto, we update the resolved directionality.
+
+  2. When the dir attribute is changed from something else (including the case
+  where it doesn't exist) to auto on a textarea or an input element with
+  @type=text/search/tel/url/email, we set the NodeHasDirAuto flag and resolve
+  the directionality based on the value of the element.
+
+  3. When the dir attribute is changed from something else (including the case
+  where it doesn't exist) to auto on any element except case 1 above and the bdi
+  element, we run the following algorithm:
+  * We set the NodeHasDirAuto flag.
+  * If the element doesn't have the NodeAncestorHasDirAuto flag, we set the
+  NodeAncestorHasDirAuto flag on all of its child nodes.  (Note that if the
+  element does have NodeAncestorHasDirAuto, all of its children should
+  already have this flag too.  We can assert this in debug builds.)
+  * To resolve the directionality of the element, we run the algorithm explained
+  in http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-dir-attribute
+  (I'll call this the "downward propagation algorithm".) by walking the child
+  subtree in tree order.  Note that an element with @dir=auto should not affect
+  other elements in its document with @dir=auto.  So there is no need to walk up
+  the parent chain in this case.  TextNodeDirectionalityMap needs to be updated
+  as appropriate.
+
+  3a. When the dir attribute is set to any valid value on an element that didn't
+  have a valid dir attribute before, this means that any descendant of that
+  element will not affect the directionality of any of its ancestors. So we need
+  to check whether any text node descendants of the element are listed in
+  TextNodeDirectionalityMap, and whether the elements whose direction they set
+  are ancestors of the element. If so, we need to rerun the downward propagation
+  algorithm for those ancestors.
+
+  4.  When the dir attribute is changed from auto to something else (including
+  the case where it gets removed) on a textarea or an input element with
+  @type=text/search/tel/url/email, we unset the NodeHasDirAuto flag and
+  resolve the directionality based on the directionality of the value of the @dir
+  attribute on element itself or its parent element.
+
+  5. When the dir attribute is changed from auto to something else (including the
+  case where it gets removed) on any element except case 4 above and the bdi
+  element, we run the following algorithm:
+  * We unset the NodeHasDirAuto flag.
+  * If the element does not have the NodeAncestorHasDirAuto flag, we unset
+  the NodeAncestorHasDirAuto flag on all of its child nodes, except those
+  who are a descendant of another element with NodeHasDirAuto.  (Note that if
+  the element has the NodeAncestorHasDirAuto flag, all of its child nodes
+  should still retain the same flag.)
+  * We resolve the directionality of the element based on the value of the @dir
+  attribute on the element itself or its parent element.
+  TextNodeDirectionalityMap needs to be updated as appropriate.
+
+  5a. When the dir attribute is removed or set to an invalid value on any
+  element (except a bdi element) with the NodeAncestorHasDirAuto flag which
+  previously had a valid dir attribute, it might have a text node descendant that
+  did not previously affect the directionality of any of its ancestors but should
+  now begin to affect them.
+  We run the following algorithm:
+  * Walk up the parent chain from the element.
+  * For any element that appears in the TextNodeDirectionalityMap, remove the
+    element from the map and rerun the downward propagation algorithm
+    (see section 3).
+  * If we reach an element without either of the NodeHasDirAuto or
+    NodeAncestorHasDirAuto flags, abort the parent chain walk.
+
+  6. When an element with @dir=auto is added to the document, we should handle it
+  similar to the case 2/3 above.
+
+  7. When an element with NodeHasDirAuto or NodeAncestorHasDirAuto is
+  removed from the document, we should handle it similar to the case 4/5 above,
+  except that we don't need to handle anything in the child subtree.  We should
+  also remove all of the occurrences of that node and its descendants from
+  TextNodeDirectionalityMap. (This is the conceptual description of what needs to
+  happen but in the implementation UnbindFromTree is going to be called on all of
+  the descendants so we don't need to descend into the child subtree).
+
+  8. When the contents of a text node is changed either from script or by the
+  user, we need to run the following algorithm:
+  * If the change has happened after the first character with strong
+  directionality in the text node, do nothing.
+  * If the text node is a child of a bdi, script or style element, do nothing.
+  * If the text node belongs to a textarea with NodeHasDirAuto, we need to
+  update the directionality of the textarea.
+  * Grab a list of elements affected by this text node from
+  TextNodeDirectionalityMap and re-resolve the directionality of each one of them
+  based on the new contents of the text node.
+  * If the text node does not exist in TextNodeDirectionalityMap, and it has the
+  NodeAncestorHasDirAuto flag set, this could potentially be a text node
+  which is going to start affecting the directionality of its parent @dir=auto
+  elements. In this case, we need to fall back to the (potentially expensive)
+  "upward propagation algorithm".  The TextNodeDirectionalityMap data structure
+  needs to be update during this algorithm.
+  * If the new contents of the text node do not have any strong characters, and
+  the old contents used to, and the text node used to exist in
+  TextNodeDirectionalityMap and it has the NodeAncestorHasDirAuto flag set,
+  the elements associated with this text node inside TextNodeDirectionalityMap
+  will now get their directionality from another text node.  In this case, for
+  each element in the list retrieved from TextNodeDirectionalityMap, run the
+  downward propagation algorithm (section 3), and remove the text node from
+  TextNodeDirectionalityMap.
+
+  9. When a new text node is injected into a document, we need to run the
+  following algorithm:
+  * If the contents of the text node do not have any characters with strong
+  direction, do nothing.
+  * If the text node is a child of a bdi, script or style element, do nothing.
+  * If the text node is appended to a textarea element with NodeHasDirAuto, we
+  need to update the directionality of the textarea.
+  * If the text node has NodeAncestorHasDirAuto, we need to run the "upward
+  propagation algorithm".  The TextNodeDirectionalityMap data structure needs to
+  be update during this algorithm.
+
+  10. When a text node is removed from a document, we need to run the following
+  algorithm:
+  * If the contents of the text node do not have any characters with strong
+  direction, do nothing.
+  * If the text node is a child of a bdi, script or style element, do nothing.
+  * If the text node is removed from a textarea element with NodeHasDirAuto,
+  set the directionality to "ltr". (This is what the spec currently says, but I'm
+  filing a spec bug to get it fixed -- the directionality should depend on the
+  parent element here.)
+  * If the text node has NodeAncestorHasDirAuto, we need to look at the list
+  of elements being affected by this text node from TextNodeDirectionalityMap,
+  run the "downward propagation algorithm" (section 3) for each one of them,
+  while updating TextNodeDirectionalityMap along the way.
+
+  11. If the value of the @dir attribute on a bdi element is changed to an
+  invalid value (or if it's removed), determine the new directionality similar
+  to the case 3 above.
+
+  == Implemention Notes ==
+  When a new node gets bound to the tree, the BindToTree function gets called.
+  The reverse case is UnbindFromTree.
+  When the contents of a text node change, nsGenericDOMDataNode::SetTextInternal
+  gets called.
+  */
+
 #include "mozilla/dom/DirectionalityUtils.h"
 #include "nsINode.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "mozilla/dom/Element.h"
-#include "nsIDOMNodeFilter.h"
-#include "nsTreeWalker.h"
 #include "nsIDOMHTMLDocument.h"
-
+#include "nsUnicodeProperties.h"
+#include "nsTextFragment.h"
+#include "nsAttrValue.h"
+#include "nsContentUtils.h"
+#include "nsTextNode.h"
+#include "nsCheapSets.h"
 
 namespace mozilla {
 
-namespace directionality {
+typedef mozilla::dom::Element Element;
+
+/**
+ * Returns true if aNode is one of the elements whose text content should not
+ * affect its own direction, nor the direction of ancestors with dir=auto.
+ *
+ * Note that this does not include <bdi>, whose content does affect its own
+ * direction when it has dir=auto (which it has by default), so one needs to
+ * test for it separately.
+ * It *does* include textarea, because even if a textarea has dir=auto, it has
+ * unicode-bidi: plaintext and is handled automatically in bidi resolution.
+ */
+static bool
+DoesNotParticipateInAutoDirection(const Element* aElement)
+{
+  nsINodeInfo* nodeInfo = aElement->NodeInfo();
+  return (aElement->IsHTML() &&
+          (nodeInfo->Equals(nsGkAtoms::script) ||
+           nodeInfo->Equals(nsGkAtoms::style) ||
+           nodeInfo->Equals(nsGkAtoms::textarea)));
+}
+
+/**
+ * Returns the directionality of a Unicode character
+ */
+static Directionality
+GetDirectionFromChar(uint32_t ch)
+{
+  switch(mozilla::unicode::GetBidiCat(ch)) {
+    case eCharType_RightToLeft:
+    case eCharType_RightToLeftArabic:
+      return eDir_RTL;
+
+    case eCharType_LeftToRight:
+      return eDir_LTR;
+
+    default:
+      return eDir_NotSet;
+  }
+}
+
+inline static bool NodeAffectsDirAutoAncestor(nsINode* aTextNode)
+{
+  Element* parent = aTextNode->GetElementParent();
+  return (parent &&
+          !DoesNotParticipateInAutoDirection(parent) &&
+          parent->NodeOrAncestorHasDirAuto());
+}
+
+/**
+ * Various methods for returning the directionality of a string using the
+ * first-strong algorithm defined in http://unicode.org/reports/tr9/#P2
+ *
+ * @param[out] aFirstStrong the offset to the first character in the string with
+ *             strong directionality, or PR_UINT32_MAX if there is none (return
+               value is eDir_NotSet).
+ * @return the directionality of the string
+ */
+static Directionality
+GetDirectionFromText(const PRUnichar* aText, const uint32_t aLength,
+                     uint32_t* aFirstStrong = nullptr)
+{
+  const PRUnichar* start = aText;
+  const PRUnichar* end = aText + aLength;
+
+  while (start < end) {
+    uint32_t current = start - aText;
+    uint32_t ch = *start++;
+
+    if (NS_IS_HIGH_SURROGATE(ch) &&
+        start < end &&
+        NS_IS_LOW_SURROGATE(*start)) {
+      ch = SURROGATE_TO_UCS4(ch, *start++);
+    }
+
+    Directionality dir = GetDirectionFromChar(ch);
+    if (dir != eDir_NotSet) {
+      if (aFirstStrong) {
+        *aFirstStrong = current;
+      }
+      return dir;
+    }
+  }
+
+  if (aFirstStrong) {
+    *aFirstStrong = PR_UINT32_MAX;
+  }
+  return eDir_NotSet;
+}
+
+static Directionality
+GetDirectionFromText(const char* aText, const uint32_t aLength,
+                        uint32_t* aFirstStrong = nullptr)
+{
+  const char* start = aText;
+  const char* end = aText + aLength;
+
+  while (start < end) {
+    uint32_t current = start - aText;
+    unsigned char ch = (unsigned char)*start++;
+
+    Directionality dir = GetDirectionFromChar(ch);
+    if (dir != eDir_NotSet) {
+      if (aFirstStrong) {
+        *aFirstStrong = current;
+      }
+      return dir;
+    }
+  }
+
+  if (aFirstStrong) {
+    *aFirstStrong = PR_UINT32_MAX;
+  }
+  return eDir_NotSet;
+}
+
+static Directionality
+GetDirectionFromText(const nsTextFragment* aFrag,
+                     uint32_t* aFirstStrong = nullptr)
+{
+  if (aFrag->Is2b()) {
+    return GetDirectionFromText(aFrag->Get2b(), aFrag->GetLength(),
+                                   aFirstStrong);
+  }
+
+  return GetDirectionFromText(aFrag->Get1b(), aFrag->GetLength(),
+                                 aFirstStrong);
+}
+
+/**
+ * Set the directionality of a node with dir=auto as defined in
+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-directionality
+ *
+ * @param[in] aStartAfterNode as an optimization, a caller may pass in a node
+ *            from which to begin walking the descendants of aElement, if it is
+ *            known that all text nodes before this node do not contain any
+ *            strong directional characters
+ * @return the text node containing the character that determined the direction
+ */
+static nsINode*
+WalkDescendantsSetDirectionFromText(Element* aElement, bool aNotify = true,
+                                       nsINode* aStartAfterNode = nullptr)
+{
+  MOZ_ASSERT(aElement, "aElement is null");
 
-typedef mozilla::dom::Element Element;
+  nsIContent* child;
+  if (aStartAfterNode &&
+      nsContentUtils::ContentIsDescendantOf(aStartAfterNode, aElement)) {
+#ifdef DEBUG
+    child = aElement->GetFirstChild();
+    while (child && child != aStartAfterNode) {
+      if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
+        MOZ_ASSERT(GetDirectionFromText(child->GetText()) == eDir_NotSet,
+                   "Strong directional characters before aStartAfterNode");
+      }
+      child = child->GetNextNode(aElement);
+    }
+#endif
+    child = aStartAfterNode->GetNextNode(aElement);
+  } else {
+    child = aElement->GetFirstChild();
+  }
+
+  while (child) {
+    if (child->IsElement() &&
+        (DoesNotParticipateInAutoDirection(child->AsElement()) ||
+         child->NodeInfo()->Equals(nsGkAtoms::bdi) ||
+         child->HasFixedDir())) {
+      child = child->GetNextNonChildNode(aElement);
+      continue;
+    }
+
+    if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
+      Directionality textNodeDir = GetDirectionFromText(child->GetText());
+      if (textNodeDir != eDir_NotSet) {
+        // We found a descendant text node with strong directional characters.
+        // Set the directionality of aElement to the corresponding value.
+        aElement->SetDirectionality(textNodeDir, aNotify);
+        return child;
+      }
+    }
+    child = child->GetNextNode(aElement);
+  }
+
+  // We walked all the descendants without finding a text node with strong
+  // directional characters. Set the directionality to LTR
+  aElement->SetDirectionality(eDir_LTR, aNotify);
+  return nullptr;
+}
+
+class nsTextNodeDirectionalityMap
+{
+  static void
+  nsTextNodeDirectionalityMapDtor(void *aObject, nsIAtom* aPropertyName,
+                                  void *aPropertyValue, void* aData)
+  {
+    nsTextNodeDirectionalityMap* map =
+      reinterpret_cast<nsTextNodeDirectionalityMap * >(aPropertyValue);
+    delete map;
+  }
+
+public:
+  nsTextNodeDirectionalityMap(nsINode* aTextNode)
+  {
+    MOZ_ASSERT(aTextNode, "Null text node");
+    MOZ_COUNT_CTOR(nsTextNodeDirectionalityMap);
+    aTextNode->SetProperty(nsGkAtoms::textNodeDirectionalityMap, this,
+                           nsTextNodeDirectionalityMapDtor);
+    aTextNode->SetHasTextNodeDirectionalityMap();
+  }
+
+  ~nsTextNodeDirectionalityMap()
+  {
+    MOZ_COUNT_DTOR(nsTextNodeDirectionalityMap);
+  }
+
+  void AddEntry(nsINode* aTextNode, Element* aElement)
+  {
+    if (!mElements.Contains(aElement)) {
+      mElements.Put(aElement);
+      aElement->SetProperty(nsGkAtoms::dirAutoSetBy, aTextNode);
+      aElement->SetHasDirAutoSet();
+    }
+  }
+
+  void RemoveEntry(nsINode* aTextNode, Element* aElement)
+  {
+    if (mElements.Contains(aElement)) {
+      mElements.Remove(aElement);
+
+      aElement->ClearHasDirAutoSet();
+      aElement->UnsetProperty(nsGkAtoms::dirAutoSetBy);
+    }
+  }
+
+private:
+  nsCheapSet<nsPtrHashKey<Element> > mElements;
+
+  static nsTextNodeDirectionalityMap* GetDirectionalityMap(nsINode* aTextNode)
+  {
+    MOZ_ASSERT(aTextNode->NodeType() == nsIDOMNode::TEXT_NODE,
+               "Must be a text node");
+    nsTextNodeDirectionalityMap* map = nullptr;
+
+    if (aTextNode->HasTextNodeDirectionalityMap()) {
+      map = static_cast<nsTextNodeDirectionalityMap * >
+        (aTextNode->GetProperty(nsGkAtoms::textNodeDirectionalityMap));
+    }
+
+    return map;
+  }
+
+  static PLDHashOperator SetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aDir)
+  {
+    MOZ_ASSERT(aEntry->GetKey()->IsElement(), "Must be an Element");
+    aEntry->GetKey()->SetDirectionality(*reinterpret_cast<Directionality*>(aDir),
+                                        true);
+    return PL_DHASH_NEXT;
+  }
+
+  static PLDHashOperator ResetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aData)
+  {
+    MOZ_ASSERT(aEntry->GetKey()->IsElement(), "Must be an Element");
+    // run the downward propagation algorithm
+    // and remove the text node from the map
+    nsINode* startAfterNode = static_cast<Element*>(aData);
+    Element* rootNode = aEntry->GetKey();
+    nsINode* textNode = WalkDescendantsSetDirectionFromText(rootNode, true,
+                                                            startAfterNode);
+    if (textNode) {
+      nsTextNodeDirectionalityMap::AddEntryToMap(textNode, rootNode);
+    }
+    return PL_DHASH_REMOVE;
+  }
+
+public:
+  void UpdateAutoDirection(Directionality aDir)
+  {
+    mElements.EnumerateEntries(SetNodeDirection, &aDir);
+  }
+
+  void ResetAutoDirection(nsINode* aTextNode, nsINode* aStartAfterNode)
+  {
+    mElements.EnumerateEntries(ResetNodeDirection, aStartAfterNode);
+  }
+
+  static void RemoveElementFromMap(nsINode* aTextNode, Element* aElement)
+  {
+    if (aTextNode->HasTextNodeDirectionalityMap()) {
+      GetDirectionalityMap(aTextNode)->RemoveEntry(aTextNode, aElement);
+    }
+  }
+
+  static void AddEntryToMap(nsINode* aTextNode, Element* aElement)
+  {
+    nsTextNodeDirectionalityMap* map = GetDirectionalityMap(aTextNode);
+    if (!map) {
+      map = new nsTextNodeDirectionalityMap(aTextNode);
+    }
+
+    map->AddEntry(aTextNode, aElement);
+  }
+
+  static void UpdateTextNodeDirection(nsINode* aTextNode, Directionality aDir)
+  {
+    MOZ_ASSERT(aTextNode->HasTextNodeDirectionalityMap(),
+               "Map missing in UpdateTextNodeDirection");
+    GetDirectionalityMap(aTextNode)->UpdateAutoDirection(aDir);
+  }
+
+  static void ResetTextNodeDirection(nsINode* aTextNode,
+                                     nsINode* aStartAfterNode = nullptr)
+  {
+    MOZ_ASSERT(aTextNode->HasTextNodeDirectionalityMap(),
+               "Map missing in ResetTextNodeDirection");
+    GetDirectionalityMap(aTextNode)->ResetAutoDirection(aTextNode,
+                                                        aStartAfterNode);
+  }
+};
 
 Directionality
 RecomputeDirectionality(Element* aElement, bool aNotify)
 {
+  MOZ_ASSERT(!aElement->HasDirAuto(),
+             "RecomputeDirectionality called with dir=auto");
+  if (aElement->HasDirAutoSet()) {
+    nsINode* setByNode =
+      static_cast<nsINode*>(aElement->GetProperty(nsGkAtoms::dirAutoSetBy));
+    if (setByNode) {
+      nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, aElement);
+    }
+  }
+
   Directionality dir = eDir_LTR;
 
   if (aElement->HasValidDir()) {
     dir = aElement->GetDirectionality();
   } else {
     Element* parent = aElement->GetElementParent();
     if (parent) {
       // If the element doesn't have an explicit dir attribute with a valid
@@ -41,38 +570,303 @@ RecomputeDirectionality(Element* aElemen
       // If there is no parent element, the directionality is the same as the
       // document direction.
       Directionality documentDir =
         aElement->OwnerDoc()->GetDocumentDirectionality();
       if (documentDir != eDir_NotSet) {
         dir = documentDir;
       }
     }
-    
+
     aElement->SetDirectionality(dir, aNotify);
   }
   return dir;
 }
 
 void
 SetDirectionalityOnDescendants(Element* aElement, Directionality aDir,
                                bool aNotify)
 {
   for (nsIContent* child = aElement->GetFirstChild(); child; ) {
     if (!child->IsElement()) {
       child = child->GetNextNode(aElement);
       continue;
     }
 
     Element* element = child->AsElement();
-    if (element->HasValidDir()) {
+    if (element->HasValidDir() || element->HasDirAuto()) {
       child = child->GetNextNonChildNode(aElement);
       continue;
     }
     element->SetDirectionality(aDir, aNotify);
     child = child->GetNextNode(aElement);
   }
 }
 
-} // end namespace directionality
+void
+WalkAncestorsResetAutoDirection(Element* aElement, bool aNotify)
+{
+  nsINode* setByNode;
+  Element* parent = aElement->GetElementParent();
+
+  while (parent && parent->NodeOrAncestorHasDirAuto()) {
+    if (parent->HasDirAutoSet()) {
+      // If the parent has the DirAutoSet flag, its direction is determined by
+      // some text node descendant.
+      // Remove it from the map and reset its direction by the downward
+      // propagation algorithm
+      setByNode =
+        static_cast<nsINode*>(parent->GetProperty(nsGkAtoms::dirAutoSetBy));
+      if (setByNode) {
+        nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, parent);
+      }
+    }
+    if (parent->HasDirAuto()) {
+      setByNode = WalkDescendantsSetDirectionFromText(parent, aNotify);
+      if (setByNode) {
+        nsTextNodeDirectionalityMap::AddEntryToMap(setByNode, parent);
+      }
+      break;
+    }
+    parent = parent->GetElementParent();
+  }
+}
+
+void
+WalkDescendantsResetAutoDirection(Element* aElement)
+{
+  nsIContent* child = aElement->GetFirstChild();
+  while (child) {
+    if (child->HasDirAuto()) {
+      child = child->GetNextNonChildNode(aElement);
+      continue;
+    }
+
+    if (child->HasTextNodeDirectionalityMap()) {
+      nsTextNodeDirectionalityMap::ResetTextNodeDirection(child, child);
+    }
+    child = child->GetNextNode(aElement);
+  }
+}
+
+void
+WalkDescendantsSetDirAuto(Element* aElement, bool aNotify)
+{
+  bool setAncestorDirAutoFlag =
+#ifdef DEBUG
+    true;
+#else
+    !aElement->AncestorHasDirAuto();
+#endif
+
+  if (setAncestorDirAutoFlag) {
+    nsIContent* child = aElement->GetFirstChild();
+    while (child) {
+      MOZ_ASSERT(!aElement->AncestorHasDirAuto() ||
+                 child->AncestorHasDirAuto(),
+                 "AncestorHasDirAuto set on node but not its children");
+      child->SetHasDirAuto();
+      child = child->GetNextNode(aElement);
+    }
+  }
+
+  nsINode* textNode = WalkDescendantsSetDirectionFromText(aElement, aNotify);
+  if (textNode) {
+    nsTextNodeDirectionalityMap::AddEntryToMap(textNode, aElement);
+  }
+}
+
+void
+WalkDescendantsClearAncestorDirAuto(Element* aElement)
+{
+  nsIContent* child = aElement->GetFirstChild();
+  while (child) {
+    if (child->HasDirAuto()) {
+      child = child->GetNextNonChildNode(aElement);
+      continue;
+    }
+
+    child->ClearAncestorHasDirAuto();
+    child = child->GetNextNode(aElement);
+  }
+}
+
+void SetAncestorDirectionIfAuto(nsINode* aTextNode, Directionality aDir,
+                                bool aNotify = true)
+{
+  MOZ_ASSERT(aTextNode->NodeType() == nsIDOMNode::TEXT_NODE,
+             "Must be a text node");
+
+  Element* parent = aTextNode->GetElementParent();
+  while (parent && parent->NodeOrAncestorHasDirAuto()) {
+    if (DoesNotParticipateInAutoDirection(parent) || parent->HasFixedDir()) {
+      break;
+    }
+
+    if (parent->HasDirAuto()) {
+      bool resetDirection = false;
+
+      if (!parent->HasDirAutoSet()) {
+        // Fast path if parent's direction is not yet set by any descendant
+        resetDirection = true;
+      } else {
+        // If parent's direction is already set, we need to know if
+        // aTextNode is before or after the text node that had set it.
+        // We will walk parent's descendants in tree order starting from
+        // aTextNode to optimize for the most common case where text nodes are
+        // being appended to tree.
+        nsINode* directionWasSetByTextNode =
+          static_cast<nsINode*>(parent->GetProperty(nsGkAtoms::dirAutoSetBy));
+        if (!directionWasSetByTextNode) {
+          resetDirection = true;
+        } else if (directionWasSetByTextNode != aTextNode) {
+          nsIContent* child = aTextNode->GetNextNode(parent);
+          while (child) {
+            if (child->IsElement() &&
+                (DoesNotParticipateInAutoDirection(child->AsElement()) ||
+                 child->NodeInfo()->Equals(nsGkAtoms::bdi) ||
+                 child->HasFixedDir())) {
+              child = child->GetNextNonChildNode(parent);
+              continue;
+            }
+
+            if (child == directionWasSetByTextNode) {
+              // we found the node that set the element's direction after our
+              // text node, so we need to reset the direction
+              resetDirection = true;
+              break;
+            }
+
+            child = child->GetNextNode(parent);
+          }
+        }
+      }
+
+      if (resetDirection) {
+        parent->SetDirectionality(aDir, aNotify);
+        nsTextNodeDirectionalityMap::AddEntryToMap(aTextNode, parent);
+        SetDirectionalityOnDescendants(parent, aDir, aNotify);
+      }
+
+      // Since we found an element with dir=auto, we can stop walking the
+      // parent chain: none of its ancestors will have their direction set by
+      // any of its descendants.
+      return;
+    }
+    parent = parent->GetElementParent();
+  }
+}
+
+void
+SetDirectionFromChangedTextNode(nsIContent* aTextNode, uint32_t aOffset,
+                                const PRUnichar* aBuffer, uint32_t aLength,
+                                bool aNotify)
+{
+  if (!NodeAffectsDirAutoAncestor(aTextNode)) {
+    return;
+  }
+
+  uint32_t firstStrong;
+  Directionality oldDir = GetDirectionFromText(aTextNode->GetText(),
+                                               &firstStrong);
+  if (aOffset > firstStrong) {
+    return;
+  }
+
+  Directionality newDir = GetDirectionFromText(aBuffer, aLength);
+  if (newDir == eDir_NotSet) {
+    if (oldDir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
+      // This node used to have a strong directional character but no
+      // longer does. ResetTextNodeDirection() will re-resolve the
+      // directionality of any elements whose directionality was
+      // determined by this node.
+      nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode);
+    }
+  } else {
+    // This node has a strong directional character. If it has a
+    // TextNodeDirectionalityMap property, it already determines the
+    // directionality of some element(s), so call UpdateTextNodeDirection to
+    // reresolve their directionality. Otherwise call
+    // SetAncestorDirectionIfAuto to find ancestor elements which should
+    // have their directionality determined by this node.
+    if (aTextNode->HasTextNodeDirectionalityMap()) {
+      nsTextNodeDirectionalityMap::UpdateTextNodeDirection(aTextNode, newDir);
+    } else {
+      SetAncestorDirectionIfAuto(aTextNode, newDir, aNotify);
+    }
+  }
+}
+
+void
+SetDirectionFromNewTextNode(nsTextNode* aTextNode)
+{
+  if (!NodeAffectsDirAutoAncestor(aTextNode)) {
+    return;
+  }
+
+  Directionality dir = GetDirectionFromText(aTextNode->GetText());
+  if (dir != eDir_NotSet) {
+    SetAncestorDirectionIfAuto(aTextNode, dir);
+  }
+}
+
+void
+ResetDirectionSetByTextNode(nsTextNode* aTextNode)
+{
+  if (!NodeAffectsDirAutoAncestor(aTextNode)) {
+    return;
+  }
+
+  Directionality dir = GetDirectionFromText(aTextNode->GetText());
+  if (dir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
+    nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode);
+  }
+}
+
+void
+SetDirectionalityFromValue(Element* aElement, const nsAString& value,
+                           bool aNotify)
+{
+  Directionality dir = GetDirectionFromText(PromiseFlatString(value).get(),
+                                            value.Length());
+  if (dir == eDir_NotSet) {
+    dir = eDir_LTR;
+  }
+
+  aElement->SetDirectionality(dir, aNotify);
+}
+
+void
+OnSetDirAttr(Element* aElement, const nsAttrValue* aNewValue,
+             bool hadValidDir, bool aNotify)
+{
+  if (aElement->IsHTML() && aElement->NodeInfo()->Equals(nsGkAtoms::input)) {
+    return;
+  }
+
+  if (aElement->AncestorHasDirAuto()) {
+    if (!hadValidDir) {
+      // The element is a descendant of an element with dir = auto, is
+      // having its dir attribute set, and previously didn't have a valid dir
+      // attribute.
+      // Check whether any of its text node descendants determine the
+      // direction of any of its ancestors, and redetermine their direction
+      WalkDescendantsResetAutoDirection(aElement);
+    } else if (!aElement->HasValidDir()) {
+      // The element is a descendant of an element with dir = auto and is
+      // having its dir attribute removed or set to an invalid value.
+      // Reset the direction of any of its ancestors whose direction is
+      // determined by a text node descendant
+      WalkAncestorsResetAutoDirection(aElement, aNotify);
+    }
+  }
+
+  if (aElement->HasDirAuto()) {
+    WalkDescendantsSetDirAuto(aElement, aNotify);
+  } else {
+    SetDirectionalityOnDescendants(aElement,
+                                   RecomputeDirectionality(aElement, aNotify),
+                                   aNotify);
+  }
+}
 
 } // end namespace mozilla
 
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -125,17 +125,16 @@
 
 #include "mozilla/CORSMode.h"
 
 #include "nsStyledElement.h"
 #include "nsXBLService.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
-using namespace mozilla::directionality;
 
 nsEventStates
 Element::IntrinsicState() const
 {
   return IsEditable() ? NS_EVENT_STATE_MOZ_READWRITE :
                         NS_EVENT_STATE_MOZ_READONLY;
 }
 
@@ -1158,17 +1157,31 @@ Element::BindToTree(nsIDocument* aDocume
     // If we're not in the doc, update our subtree pointer.
     SetSubtreeRootPointer(aParent->SubtreeRoot());
   }
 
   // This has to be here, rather than in nsGenericHTMLElement::BindToTree, 
   //  because it has to happen after updating the parent pointer, but before
   //  recursively binding the kids.
   if (IsHTML()) {
-    RecomputeDirectionality(this, false);
+    if (aParent && aParent->NodeOrAncestorHasDirAuto()) {
+      SetAncestorHasDirAuto();
+      // if we are binding an element to the tree that already has descendants,
+      // and the parent has NodeHasDirAuto or NodeAncestorHasDirAuto, we may
+      // need to reset the direction of an ancestor with dir=auto
+      if (GetFirstChild()) {
+        WalkAncestorsResetAutoDirection(this);
+      }
+    }
+
+    if (!HasDirAuto()) {
+      // if the element doesn't have dir=auto, set its directionality from
+      // the dir attribute or by inheriting from its ancestors.
+      RecomputeDirectionality(this, false);
+    }
   }
 
   // If NODE_FORCE_XBL_BINDINGS was set we might have anonymous children
   // that also need to be told that they are moving.
   nsresult rv;
   if (hadForceXBL) {
     nsBindingManager* bmgr = OwnerDoc()->BindingManager();
 
@@ -1349,17 +1362,17 @@ Element::UnbindFromTree(bool aDeep, bool
     if (slots) {
       slots->mBindingParent = nullptr;
     }
   }
 
   // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree, 
   //  because it has to happen after unsetting the parent pointer, but before
   //  recursively unbinding the kids.
-  if (IsHTML()) {
+  if (IsHTML() && !HasDirAuto()) {
     RecomputeDirectionality(this, false);
   }
 
   if (aDeep) {
     // Do the kids. Don't call GetChildCount() here since that'll force
     // XUL to generate template children, which there is no need for since
     // all we're going to do is unbind them anyway.
     uint32_t i, n = mAttrsAndChildren.ChildCount();
@@ -1822,17 +1835,23 @@ Element::SetAttrAndNotify(int32_t aNames
 
   // Copy aParsedValue for later use since it will be lost when we call
   // SetAndTakeMappedAttr below
   nsAttrValue aValueForAfterSetAttr;
   if (aCallAfterSetAttr) {
     aValueForAfterSetAttr.SetTo(aParsedValue);
   }
 
+  bool hadValidDir = false;
+
   if (aNamespaceID == kNameSpaceID_None) {
+    if (aName == nsGkAtoms::dir) {
+      hadValidDir = HasValidDir() || NodeInfo()->Equals(nsGkAtoms::bdi);
+    }
+
     // XXXbz Perhaps we should push up the attribute mapping function
     // stuff to Element?
     if (!IsAttributeMapped(aName) ||
         !SetMappedAttribute(document, aName, aParsedValue, &rv)) {
       rv = mAttrsAndChildren.SetAndTakeAttr(aName, aParsedValue);
     }
   }
   else {
@@ -1858,16 +1877,20 @@ Element::SetAttrAndNotify(int32_t aNames
 
   if (aNotify) {
     nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType);
   }
 
   if (aCallAfterSetAttr) {
     rv = AfterSetAttr(aNamespaceID, aName, &aValueForAfterSetAttr, aNotify);
     NS_ENSURE_SUCCESS(rv, rv);
+
+    if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
+      OnSetDirAttr(this, &aValueForAfterSetAttr, hadValidDir, aNotify);
+    }
   }
 
   if (aFireMutation) {
     nsMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED);
 
     nsAutoString ns;
     nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
     ErrorResult rv;
@@ -2060,16 +2083,22 @@ Element::UnsetAttr(int32_t aNameSpaceID,
   if (slots && slots->mAttributeMap) {
     slots->mAttributeMap->DropAttribute(aNameSpaceID, aName);
   }
 
   // The id-handling code, and in the future possibly other code, need to
   // react to unexpected attribute changes.
   nsMutationGuard::DidMutate();
 
+  bool hadValidDir = false;
+
+  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
+    hadValidDir = HasValidDir() || NodeInfo()->Equals(nsGkAtoms::bdi);
+  }
+
   nsAttrValue oldValue;
   rv = mAttrsAndChildren.RemoveAttrAt(index, oldValue);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
     nsRefPtr<nsXBLBinding> binding =
       OwnerDoc()->BindingManager()->GetBinding(this);
     if (binding) {
@@ -2082,16 +2111,20 @@ Element::UnsetAttr(int32_t aNameSpaceID,
   if (aNotify) {
     nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
                                   nsIDOMMutationEvent::REMOVAL);
   }
 
   rv = AfterSetAttr(aNameSpaceID, aName, nullptr, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
+    OnSetDirAttr(this, nullptr, hadValidDir, aNotify);
+  }
+
   if (hasMutationListeners) {
     nsCOMPtr<nsIDOMEventTarget> node = do_QueryObject(this);
     nsMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED);
 
     mutation.mRelatedNode = attrNode;
     mutation.mAttrName = aName;
 
     nsAutoString value;
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -172,17 +172,16 @@
 
 #include "imgILoader.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsSandboxFlags.h"
 #include "nsIAppsService.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
-using namespace mozilla::directionality;
 
 typedef nsTArray<Link*> LinkArray;
 
 // Reference to the document which requested DOM full-screen mode.
 nsWeakPtr nsDocument::sFullScreenDoc = nullptr;
 
 // Reference to the root document of the branch containing the document
 // which requested DOM full-screen mode.
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1048,17 +1048,17 @@ protected:
 
   // Refreshes the hrefs of all the links in the document.
   void RefreshLinkHrefs();
 
   nsIContent* GetFirstBaseNodeWithHref();
   nsresult SetFirstBaseNodeWithHref(nsIContent *node);
 
   inline void
-  SetDocumentDirectionality(mozilla::directionality::Directionality aDir)
+  SetDocumentDirectionality(mozilla::Directionality aDir)
   {
     mDirectionality = aDir;
   }
 
   // Get the first <title> element with the given IsNodeOfType type, or
   // return null if there isn't one
   nsIContent* GetTitleContent(uint32_t aNodeType);
   // Find the first "title" element in the given IsNodeOfType type and
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -21,16 +21,17 @@
 #include "nsIDOMText.h"
 #include "nsCOMPtr.h"
 #include "nsDOMString.h"
 #include "nsIDOMUserDataHandler.h"
 #include "nsChangeHint.h"
 #include "nsEventDispatcher.h"
 #include "nsCOMArray.h"
 #include "nsNodeUtils.h"
+#include "mozilla/dom/DirectionalityUtils.h"
 #include "nsBindingManager.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozAutoDocUpdate.h"
 #include "nsAsyncDOMEvent.h"
 
 #include "pldhash.h"
 #include "prprf.h"
 #include "nsWrapperCacheInlines.h"
@@ -274,16 +275,20 @@ nsGenericDOMDataNode::SetTextInternal(ui
       aOffset,
       endOffset,
       aLength,
       aDetails
     };
     nsNodeUtils::CharacterDataWillChange(this, &info);
   }
 
+  if (NodeType() == nsIDOMNode::TEXT_NODE) {
+    SetDirectionFromChangedTextNode(this, aOffset, aBuffer, aLength, aNotify);
+  }
+
   if (aOffset == 0 && endOffset == textLength) {
     // Replacing whole text or old text was empty.  Don't bother to check for
     // bidi in this string if the document already has bidi enabled.
     mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled());
   }
   else if (aOffset == textLength) {
     // Appending to existing
     mText.Append(aBuffer, aLength, !document || !document->GetBidiEnabled());
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -271,16 +271,17 @@ GK_ATOM(details, "details")
 GK_ATOM(deviceAspectRatio, "device-aspect-ratio")
 GK_ATOM(deviceHeight, "device-height")
 GK_ATOM(deviceWidth, "device-width")
 GK_ATOM(dfn, "dfn")
 GK_ATOM(dialog, "dialog")
 GK_ATOM(difference, "difference")
 GK_ATOM(digit, "digit")
 GK_ATOM(dir, "dir")
+GK_ATOM(dirAutoSetBy, "dirAutoSetBy")
 GK_ATOM(directionality, "directionality")
 GK_ATOM(disableOutputEscaping, "disable-output-escaping")
 GK_ATOM(disabled, "disabled")
 GK_ATOM(display, "display")
 GK_ATOM(distinct, "distinct")
 GK_ATOM(div, "div")
 GK_ATOM(dl, "dl")
 GK_ATOM(doctypePublic, "doctype-public")
@@ -1028,16 +1029,17 @@ GK_ATOM(td, "td")
 GK_ATOM(_template, "template")
 GK_ATOM(text_decoration, "text-decoration")
 GK_ATOM(terminate, "terminate")
 GK_ATOM(test, "test")
 GK_ATOM(text, "text")
 GK_ATOM(textarea, "textarea")
 GK_ATOM(textbox, "textbox")
 GK_ATOM(textnode, "textnode")
+GK_ATOM(textNodeDirectionalityMap, "textNodeDirectionalityMap")
 GK_ATOM(tfoot, "tfoot")
 GK_ATOM(th, "th")
 GK_ATOM(thead, "thead")
 GK_ATOM(thumb, "thumb")
 GK_ATOM(time, "time")
 GK_ATOM(title, "title")
 GK_ATOM(titlebar, "titlebar")
 GK_ATOM(titletip, "titletip")
--- a/content/base/src/nsTextNode.cpp
+++ b/content/base/src/nsTextNode.cpp
@@ -4,25 +4,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Implementation of DOM Core's nsIDOMText node.
  */
 
 #include "nsTextNode.h"
 #include "nsContentUtils.h"
+#include "mozilla/dom/DirectionalityUtils.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIDocument.h"
 #include "nsThreadUtils.h"
 #include "nsStubMutationObserver.h"
 #ifdef DEBUG
 #include "nsRange.h"
 #endif
 
+using namespace mozilla;
 using namespace mozilla::dom;
 
 /**
  * class used to implement attr() generated content
  */
 class nsAttributeTextNode : public nsTextNode,
                             public nsStubMutationObserver
 {
@@ -157,16 +159,37 @@ nsTextNode::AppendTextForNormalize(const
                                    bool aNotify, nsIContent* aNextSibling)
 {
   CharacterDataChangeInfo::Details details = {
     CharacterDataChangeInfo::Details::eMerge, aNextSibling
   };
   return SetTextInternal(mText.GetLength(), 0, aBuffer, aLength, aNotify, &details);
 }
 
+nsresult
+nsTextNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                       nsIContent* aBindingParent, bool aCompileEventHandlers)
+{
+  nsresult rv = nsGenericDOMDataNode::BindToTree(aDocument, aParent,
+                                                 aBindingParent,
+                                                 aCompileEventHandlers);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  SetDirectionFromNewTextNode(this);
+
+  return NS_OK;
+}
+
+void nsTextNode::UnbindFromTree(bool aDeep, bool aNullParent)
+{
+  ResetDirectionSetByTextNode(this);
+
+  nsGenericDOMDataNode::UnbindFromTree(aDeep, aNullParent);
+}
+
 #ifdef DEBUG
 void
 nsTextNode::List(FILE* out, int32_t aIndent) const
 {
   int32_t index;
   for (index = aIndent; --index >= 0; ) fputs("  ", out);
 
   fprintf(out, "Text@%p", static_cast<const void*>(this));
--- a/content/base/src/nsTextNode.h
+++ b/content/base/src/nsTextNode.h
@@ -35,16 +35,22 @@ public:
   // nsINode
   virtual bool IsNodeOfType(uint32_t aFlags) const;
 
   virtual nsGenericDOMDataNode* CloneDataNode(nsINodeInfo *aNodeInfo,
                                               bool aCloneText) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 
+  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                              nsIContent* aBindingParent,
+                              bool aCompileEventHandlers);
+  virtual void UnbindFromTree(bool aDeep = true,
+                              bool aNullParent = true);
+
   nsresult AppendTextForNormalize(const PRUnichar* aBuffer, uint32_t aLength,
                                   bool aNotify, nsIContent* aNextSibling);
 
   virtual nsIDOMNode* AsDOMNode() { return this; }
 
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const;
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -158,38 +158,36 @@ public:
   static void StopHandlingUserInput()
   {
     --sUserInputEventDepth;
     if (sUserInputEventDepth == 0) {
       sHandlingInputStart = TimeStamp();
     }
   }
 
+  /**
+   * Returns true if the current code is being executed as a result of user input.
+   * This includes timers or anything else that is initiated from user input.
+   * However, mouse over events are not counted as user input, nor are
+   * page load events. If this method is called from asynchronously executed code,
+   * such as during layout reflows, it will return false. If more time has elapsed
+   * since the user input than is specified by the
+   * dom.event.handling-user-input-time-limit pref (default 1 second), this
+   * function also returns false.
+   */
   static bool IsHandlingUserInput()
   {
     if (sUserInputEventDepth <= 0) {
       return false;
     }
     TimeDuration timeout = nsContentUtils::HandlingUserInputTimeout();
     return timeout <= TimeDuration(0) ||
            (TimeStamp::Now() - sHandlingInputStart) <= timeout;
   }
 
-  /**
-   * Returns true if the current code is being executed as a result of user input.
-   * This includes timers or anything else that is initiated from user input.
-   * However, mouse hover events are not counted as user input, nor are
-   * page load events. If this method is called from asynchronously executed code,
-   * such as during layout reflows, it will return false. If more time has elapsed
-   * since the user input than is specified by the
-   * dom.event.handling-user-input-time-limit pref (default 1 second), this
-   * function also returns false.
-   */
-  NS_IMETHOD_(bool) IsHandlingUserInputExternal() { return IsHandlingUserInput(); }
-  
   nsPresContext* GetPresContext() { return mPresContext; }
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsEventStateManager,
                                            nsIObserver)
 
   static nsIDocument* sMouseOverDocument;
 
   static nsEventStateManager* GetActiveEventStateManager() { return sActiveESM; }
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -63,16 +63,17 @@
 #include "nsIDOMEvent.h"
 #include "nsDOMCSSDeclaration.h"
 #include "nsITextControlFrame.h"
 #include "nsIForm.h"
 #include "nsIFormControl.h"
 #include "nsIDOMHTMLFormElement.h"
 #include "nsHTMLFormElement.h"
 #include "nsFocusManager.h"
+#include "nsAttrValueOrString.h"
 
 #include "nsMutationEvent.h"
 
 #include "nsContentCID.h"
 
 #include "nsDOMStringMap.h"
 
 #include "nsIEditor.h"
@@ -97,17 +98,16 @@
 #include "nsThreadUtils.h"
 #include "nsTextFragment.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/ErrorResult.h"
 #include "nsHTMLDocument.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
-using namespace mozilla::directionality;
 
 class nsINodeInfo;
 class nsIDOMNodeList;
 class nsRuleWalker;
 
 // XXX todo: add in missing out-of-memory checks
 
 //----------------------------------------------------------------------
@@ -342,18 +342,19 @@ nsGenericHTMLElement::ClearDataset()
   NS_ASSERTION(slots && slots->mDataset,
                "Slots should exist and dataset should not be null.");
   slots->mDataset = nullptr;
 
   return NS_OK;
 }
 
 static const nsAttrValue::EnumTable kDirTable[] = {
-  { "ltr", NS_STYLE_DIRECTION_LTR },
-  { "rtl", NS_STYLE_DIRECTION_RTL },
+  { "ltr", eDir_LTR },
+  { "rtl", eDir_RTL },
+  { "auto", eDir_Auto },
   { 0 }
 };
 
 void
 nsGenericHTMLElement::GetAccessKeyLabel(nsAString& aLabel)
 {
   nsPresContext *presContext = GetPresContext();
 
@@ -1694,16 +1695,33 @@ nsGenericHTMLElement::GetHrefURIForAncho
   // We use the nsAttrValue's copy of the URI string to avoid copying.
   nsCOMPtr<nsIURI> uri;
   GetURIAttr(nsGkAtoms::href, nullptr, getter_AddRefs(uri));
 
   return uri.forget();
 }
 
 nsresult
+nsGenericHTMLElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
+                                    const nsAttrValueOrString* aValue,
+                                    bool aNotify)
+{
+  if (aNamespaceID == kNameSpaceID_None &&
+      aName == nsGkAtoms::dir &&
+      HasDirAuto()) {
+      // setting dir on an element that currently has dir=auto
+    WalkDescendantsClearAncestorDirAuto(this);
+    SetHasDirAuto();
+  }
+
+  return nsGenericHTMLElementBase::BeforeSetAttr(aNamespaceID, aName,
+                                                 aValue, aNotify);
+}
+
+nsresult
 nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                                    const nsAttrValue* aValue, bool aNotify)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     uint32_t eventType = EventNameType_HTML;
     if (mNodeInfo->Equals(nsGkAtoms::body) ||
         mNodeInfo->Equals(nsGkAtoms::frameset)) {
       eventType |= EventNameType_HTMLBodyOrFramesetOnly;
@@ -1714,26 +1732,38 @@ nsGenericHTMLElement::AfterSetAttr(int32
         "Expected string value for script body");
       nsresult rv = SetEventHandler(aName, aValue->GetStringValue());
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else if (aNotify && aName == nsGkAtoms::spellcheck) {
       SyncEditorsOnSubtree(this);
     }
     else if (aName == nsGkAtoms::dir) {
-      Directionality dir;
-      if (aValue &&
-          (aValue->Equals(nsGkAtoms::ltr, eIgnoreCase) ||
-           aValue->Equals(nsGkAtoms::rtl, eIgnoreCase))) {
+      Directionality dir = eDir_LTR;
+      if (aValue && aValue->Type() == nsAttrValue::eEnum) {
         SetHasValidDir();
-        dir = aValue->Equals(nsGkAtoms::rtl, eIgnoreCase) ? eDir_RTL : eDir_LTR;
-        SetDirectionality(dir, aNotify);
+        Directionality dirValue = (Directionality)aValue->GetEnumValue();
+        if (dirValue == eDir_Auto) {
+          SetHasDirAuto();
+          ClearHasFixedDir();
+        } else {
+          dir = dirValue;
+          SetDirectionality(dir, aNotify);
+          ClearHasDirAuto();
+          ClearHasDirAutoSet();
+          SetHasFixedDir();
+        }
       } else {
         ClearHasValidDir();
-        dir = RecomputeDirectionality(this, aNotify);
+        ClearHasFixedDir();
+        if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
+          SetHasDirAuto();
+        } else {
+          dir = RecomputeDirectionality(this, aNotify);
+        }
       }
       SetDirectionalityOnDescendants(this, dir, aNotify);
     }
   }
 
   return nsGenericHTMLElementBase::AfterSetAttr(aNamespaceID, aName,
                                                 aValue, aNotify);
 }
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -772,16 +772,20 @@ private:
 protected:
   /**
    * Determine whether an attribute is an event (onclick, etc.)
    * @param aName the attribute
    * @return whether the name is an event handler name
    */
   bool IsEventName(nsIAtom* aName);
 
+  virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify);
+
   virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue, bool aNotify);
 
   virtual nsEventListenerManager*
     GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer);
 
   virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const;
 
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -76,16 +76,17 @@
 #include "nsGlobalWindow.h"
 
 // input type=image
 #include "nsImageLoadingContent.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentUtils.h"
+#include "mozilla/dom/DirectionalityUtils.h"
 #include "nsRadioVisitor.h"
 
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Util.h" // DebugOnly
 #include "mozilla/Preferences.h"
 
 #include "nsIIDNService.h"
 
@@ -755,16 +756,20 @@ nsHTMLInputElement::BeforeSetAttr(int32_
       if (aValue) {
         LoadImage(aValue->String(), true, aNotify);
       } else {
         // Null value means the attr got unset; drop the image
         CancelImageRequests(aNotify);
       }
     } else if (aNotify && aName == nsGkAtoms::disabled) {
       mDisabledChanged = true;
+    } else if (aName == nsGkAtoms::dir &&
+               AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
+                           nsGkAtoms::_auto, eIgnoreCase)) {
+      SetDirectionIfAuto(false, aNotify);
     }
   }
 
   return nsGenericHTMLFormElement::BeforeSetAttr(aNameSpaceID, aName,
                                                  aValue, aNotify);
 }
 
 nsresult
@@ -861,16 +866,19 @@ nsHTMLInputElement::AfterSetAttr(int32_t
       UpdateHasRange();
       UpdateRangeOverflowValidityState();
     } else if (aName == nsGkAtoms::min) {
       UpdateHasRange();
       UpdateRangeUnderflowValidityState();
       UpdateStepMismatchValidityState();
     } else if (aName == nsGkAtoms::step) {
       UpdateStepMismatchValidityState();
+    } else if (aName == nsGkAtoms::dir &&
+               aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
+      SetDirectionIfAuto(true, aNotify);
     }
 
     UpdateState(aNotify);
   }
 
   return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
                                                 aValue, aNotify);
 }
@@ -2577,16 +2585,19 @@ nsHTMLInputElement::BindToTree(nsIDocume
   }
 
   // Add radio to document if we don't have a form already (if we do it's
   // already been added into that group)
   if (aDocument && !mForm && mType == NS_FORM_INPUT_RADIO) {
     AddedToRadioGroup();
   }
 
+  // Set direction based on value if dir=auto
+  SetDirectionIfAuto(HasDirAuto(), false);
+
   // An element can't suffer from value missing if it is not in a document.
   // We have to check if we suffer from that as we are now in a document.
   UpdateValueMissingValidityState();
 
   // If there is a disabled fieldset in the parent chain, the element is now
   // barred from constraint validation and can't suffer from value missing
   // (call done before).
   UpdateBarredFromConstraintValidation();
@@ -3192,16 +3203,31 @@ nsHTMLInputElement::SetDefaultValueAsVal
   // it's in the value mode value.
   nsAutoString resetVal;
   GetDefaultValue(resetVal);
 
   // SetValueInternal is going to sanitize the value.
   return SetValueInternal(resetVal, false, false);
 }
 
+void
+nsHTMLInputElement::SetDirectionIfAuto(bool aAuto, bool aNotify)
+{
+  if (aAuto) {
+    SetHasDirAuto();
+    if (IsSingleLineTextControl(true)) {
+      nsAutoString value;
+      GetValue(value);
+      SetDirectionalityFromValue(this, value, aNotify);
+    }
+  } else {
+    ClearHasDirAuto();
+  }
+}
+
 NS_IMETHODIMP
 nsHTMLInputElement::Reset()
 {
   // We should be able to reset all dirty flags regardless of the type.
   SetCheckedChanged(false);
   SetValueChanged(false);
 
   switch (GetValueMode()) {
@@ -4601,16 +4627,20 @@ nsHTMLInputElement::InitializeKeyboardEv
     state->InitializeKeyboardEventListeners();
   }
 }
 
 NS_IMETHODIMP_(void)
 nsHTMLInputElement::OnValueChanged(bool aNotify)
 {
   UpdateAllValidityStates(aNotify);
+
+  if (HasDirAuto()) {
+    SetDirectionIfAuto(true, aNotify);
+  }
 }
 
 NS_IMETHODIMP_(bool)
 nsHTMLInputElement::HasCachedSelection()
 {
   bool isCached = false;
   nsTextEditorState *state = GetEditorState();
   if (state) {
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -506,16 +506,18 @@ protected:
 
   /**
    * Set the current default value to the value of the input element.
    * @note You should not call this method if GetValueMode() doesn't return
    * VALUE_MODE_VALUE.
    */
   nsresult SetDefaultValueAsValue();
 
+  virtual void SetDirectionIfAuto(bool aAuto, bool aNotify);
+
   /**
    * Return if an element should have a specific validity UI
    * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
    *
    * @return Whether the elemnet should have a validity UI.
    */
   bool ShouldShowValidityUI() const {
     /**
--- a/content/html/content/src/nsHTMLUnknownElement.h
+++ b/content/html/content/src/nsHTMLUnknownElement.h
@@ -10,16 +10,19 @@
 
 class nsHTMLUnknownElement : public nsGenericHTMLElement
                            , public nsIDOMHTMLUnknownElement
 {
 public:
   nsHTMLUnknownElement(already_AddRefed<nsINodeInfo> aNodeInfo)
     : nsGenericHTMLElement(aNodeInfo)
   {
+    if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
+      SetHasDirAuto();
+    }
   }
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE_TO_NSINODE
 
--- a/content/html/content/test/test_bug660663.html
+++ b/content/html/content/test/test_bug660663.html
@@ -16,16 +16,15 @@ https://bugzilla.mozilla.org/show_bug.cg
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 /** Test for Bug 660663 **/
 reflectLimitedEnumerated({
   element: document.createElement("div"),
   attribute: "dir",
-  validValues: ["ltr", "rtl"],
-  invalidValues: ["cheesecake", ""],
-  unsupportedValues: ["auto"]
+  validValues: ["ltr", "rtl", "auto"],
+  invalidValues: ["cheesecake", ""]
 });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -759,17 +759,17 @@ this.DOMApplicationRegistry = {
     app.progress = 0;
     app.installState = app.previousState;
     app.downloading = false;
     app.downloadAvailable = false;
     app.downloadSize = 0;
     this._saveApps((function() {
       this.broadcastMessage("Webapps:PackageEvent",
                              { type: "canceled",
-                               manifestURL:  aApp.manifestURL,
+                               manifestURL:  app.manifestURL,
                                app: app,
                                error: "DOWNLOAD_CANCELED" });
     }).bind(this));
   },
 
   startDownload: function startDownload(aManifestURL) {
     debug("startDownload for " + aManifestURL);
     let id = this._appIdForManifestURL(aManifestURL);
@@ -1309,20 +1309,28 @@ this.DOMApplicationRegistry = {
     let self = this;
     // Removes the directory we created, and sends an error to the DOM side.
     function cleanup(aError) {
       debug("Cleanup: " + aError);
       let dir = FileUtils.getDir("TmpD", ["webapps", id], true, true);
       try {
         dir.remove(true);
       } catch (e) { }
-        self.broadcastMessage("Webapps:PackageEvent",
-                              { type: "error",
-                                manifestURL:  aApp.manifestURL,
-                                error: aError });
+
+      // We avoid notifying the error to the DOM side if the app download
+      // was cancelled via cancelDownload, which already sends its own
+      // notification.
+      if (!app.downloading && !app.downloadAvailable && !app.downloadSize) {
+        return;
+      }
+
+      self.broadcastMessage("Webapps:PackageEvent",
+                            { type: "error",
+                              manifestURL:  aApp.manifestURL,
+                              error: aError });
     }
 
     function getInferedStatus() {
       // XXX Update once we have digital signatures (bug 772365)
       return Ci.nsIPrincipal.APP_STATUS_INSTALLED;
     }
 
     function getAppStatus(aManifest) {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -798,25 +798,24 @@ class CGGetDeferredFinalizePointers(CGAb
 
     def definition_body(self):
         return """  nsTArray<%s >* pointers = sDeferredFinalizePointers;
   sDeferredFinalizePointers = nullptr;
   return pointers;""" % DeferredFinalizeSmartPtr(self.descriptor)
 
 class CGDeferredFinalize(CGAbstractStaticMethod):
     def __init__(self, descriptor):
-        CGAbstractStaticMethod.__init__(self, descriptor, "DeferredFinalize", "bool", [Argument('int32_t', 'slice'), Argument('void*', 'data')])
+        CGAbstractStaticMethod.__init__(self, descriptor, "DeferredFinalize", "bool", [Argument('uint32_t', 'slice'), Argument('void*', 'data')])
 
     def definition_body(self):
         smartPtr = DeferredFinalizeSmartPtr(self.descriptor)
-        return """  nsTArray<%(smartPtr)s >* pointers = static_cast<nsTArray<%(smartPtr)s >*>(data);
+        return """  MOZ_ASSERT(slice > 0, "nonsensical/useless call with slice == 0");
+  nsTArray<%(smartPtr)s >* pointers = static_cast<nsTArray<%(smartPtr)s >*>(data);
   uint32_t oldLen = pointers->Length();
-  if (slice == -1 || slice > oldLen) {
-    slice = oldLen;
-  }
+  slice = NS_MIN(oldLen, slice);
   uint32_t newLen = oldLen - slice;
   pointers->RemoveElementsAt(newLen, slice);
   if (newLen == 0) {
     delete pointers;
     return true;
   }
   return false;""" % { 'smartPtr': smartPtr }
 
--- a/dom/bluetooth/BluetoothManager.cpp
+++ b/dom/bluetooth/BluetoothManager.cpp
@@ -6,23 +6,21 @@
 
 #include "base/basictypes.h"
 #include "BluetoothManager.h"
 #include "BluetoothCommon.h"
 #include "BluetoothAdapter.h"
 #include "BluetoothService.h"
 #include "BluetoothReplyRunnable.h"
 
+#include "DOMRequest.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
-#include "nsDOMEvent.h"
-#include "nsIDOMDOMRequest.h"
 #include "nsIPermissionManager.h"
 #include "nsThreadUtils.h"
-#include "nsXPCOMCIDInternal.h"
 #include "mozilla/Util.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 
 using namespace mozilla;
 
 USING_BLUETOOTH_NAMESPACE
 
 DOMCI_DATA(BluetoothManager, BluetoothManager)
@@ -53,82 +51,58 @@ public:
     BluetoothReplyRunnable(aReq),
     mManagerPtr(aManager)
   {
   }
 
   bool
   ParseSuccessfulReply(jsval* aValue)
   {
-    nsCOMPtr<nsIDOMBluetoothAdapter> adapter;
     *aValue = JSVAL_VOID;
 
-    const InfallibleTArray<BluetoothNamedValue>& v =
-      mReply->get_BluetoothReplySuccess().value().get_ArrayOfBluetoothNamedValue();
-    adapter = BluetoothAdapter::Create(mManagerPtr->GetOwner(), v);
+    const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
+
+    MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
+    const InfallibleTArray<BluetoothNamedValue>& values =
+      v.get_ArrayOfBluetoothNamedValue();
+    nsCOMPtr<nsIDOMBluetoothAdapter> adapter;
+    adapter = BluetoothAdapter::Create(mManagerPtr->GetOwner(), values);
 
     nsresult rv;
     nsIScriptContext* sc = mManagerPtr->GetContextForEventHandlers(&rv);
     if (!sc) {
       NS_WARNING("Cannot create script context!");
       SetError(NS_LITERAL_STRING("BluetoothScriptContextError"));
       return false;
     }
 
     rv = nsContentUtils::WrapNative(sc->GetNativeContext(),
                                     sc->GetNativeGlobal(),
                                     adapter,
                                     aValue);
-    bool result = NS_SUCCEEDED(rv);
-    if (!result) {
+    if (NS_FAILED(rv)) {
       NS_WARNING("Cannot create native object!");
       SetError(NS_LITERAL_STRING("BluetoothNativeObjectError"));
+      return false;
     }
 
-    return result;
+    return true;
   }
 
   void
   ReleaseMembers()
   {
     BluetoothReplyRunnable::ReleaseMembers();
     mManagerPtr = nullptr;
   }
   
 private:
   nsRefPtr<BluetoothManager> mManagerPtr;
 };
 
-class ToggleBtResultTask : public nsRunnable
-{
-public:
-  ToggleBtResultTask(BluetoothManager* aManager, bool aEnabled)
-    : mManagerPtr(aManager),
-      mEnabled(aEnabled)
-  {
-  }
-
-  NS_IMETHOD Run()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    mManagerPtr->FireEnabledDisabledEvent(mEnabled);
-
-    // mManagerPtr must be null before returning to prevent the background
-    // thread from racing to release it during the destruction of this runnable.
-    mManagerPtr = nullptr;
-
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<BluetoothManager> mManagerPtr;
-  bool mEnabled;
-};
-
 nsresult
 BluetoothManager::FireEnabledDisabledEvent(bool aEnabled)
 {
   return DispatchTrustedEvent(aEnabled ? NS_LITERAL_STRING("enabled")
                               : NS_LITERAL_STRING("disabled"));
 }
 
 BluetoothManager::BluetoothManager(nsPIDOMWindow *aWindow)
@@ -159,72 +133,56 @@ BluetoothManager::SetPropertyByValue(con
     NS_WARNING(warningMsg.get());
 #endif
 }
 
 NS_IMETHODIMP
 BluetoothManager::GetEnabled(bool* aEnabled)
 {
   BluetoothService* bs = BluetoothService::Get();
-  if (!bs) {
-    NS_WARNING("BluetoothService not available!");
-    return NS_ERROR_FAILURE;
-  }
+  NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
 
   *aEnabled = bs->IsEnabled();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BluetoothManager::GetDefaultAdapter(nsIDOMDOMRequest** aAdapter)
 {
+  nsCOMPtr<nsIDOMRequestService> rs =
+    do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
+
+  nsCOMPtr<nsIDOMDOMRequest> request;
+  nsresult rv = rs->CreateRequest(GetOwner(), getter_AddRefs(request));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<BluetoothReplyRunnable> results = new GetAdapterTask(this, request);
+
   BluetoothService* bs = BluetoothService::Get();
-  if (!bs) {
-    NS_WARNING("BluetoothService not available!");
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIDOMRequestService> rs = do_GetService("@mozilla.org/dom/dom-request-service;1");
-
-  if (!rs) {
-    NS_WARNING("No DOMRequest Service!");
+  NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
+  if (NS_FAILED(bs->GetDefaultAdapterPathInternal(results))) {
     return NS_ERROR_FAILURE;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = rs->CreateRequest(GetOwner(), getter_AddRefs(request));
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Can't create DOMRequest!");
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<BluetoothReplyRunnable> results = new GetAdapterTask(this, request);
-
-  if (NS_FAILED(bs->GetDefaultAdapterPathInternal(results))) {
-    return NS_ERROR_FAILURE;
-  }
   request.forget(aAdapter);
   return NS_OK;
 }
 
 // static
 already_AddRefed<BluetoothManager>
 BluetoothManager::Create(nsPIDOMWindow* aWindow)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
 
-  BluetoothService* bs = BluetoothService::Get();
-  if (!bs) {
-    NS_WARNING("BluetoothService not available!");
-    return nullptr;
-  }
-
   nsRefPtr<BluetoothManager> manager = new BluetoothManager(aWindow);
 
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE(bs, nullptr);
   bs->RegisterManager(manager);
 
   return manager.forget();
 }
 
 nsresult
 NS_NewBluetoothManager(nsPIDOMWindow* aWindow,
                        nsIDOMBluetoothManager** aBluetoothManager)
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIDOMFile.h"
 #include "nsIFile.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
+#include "nsLocalFile.h"
 #include "nsNetUtil.h"
 
 #define TARGET_FOLDER "/sdcard/downloads/bluetooth/"
 
 USING_BLUETOOTH_NAMESPACE
 using namespace mozilla;
 using namespace mozilla::ipc;
 
@@ -271,24 +272,32 @@ BluetoothOppManager::SendFile(BlobParent
   if (mBlob) {
     // Means there's a sending process. Reply error.
     return false;
   }
 
   /*
    * Process of sending a file:
    *  - Keep blob because OPP connection has not been established yet.
+   *  - Try to retrieve file name from the blob or assign one if failed to get.
    *  - Create an OPP connection by SendConnectRequest()
    *  - After receiving the response, start to read file and send.
    */
   mBlob = aActor->GetBlob();
 
-  nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob);
-  if (file) {
-    file->GetName(sFileName);
+  nsCOMPtr<nsIDOMFile> domFile = do_QueryInterface(mBlob);
+  nsString fullPath;
+
+  if (domFile && NS_SUCCEEDED(domFile->GetMozFullPathInternal(fullPath))) {
+    nsCOMPtr<nsIFile> localFile = new nsLocalFile();
+    NS_NewLocalFile(fullPath, false, getter_AddRefs(localFile));
+
+    if (localFile) {
+      localFile->GetLeafName(sFileName);
+    }
   }
 
   if (sFileName.IsEmpty()) {
     sFileName.AssignLiteral("Unknown");
   }
 
   SendConnectRequest();
 
--- a/dom/bluetooth/BluetoothService.cpp
+++ b/dom/bluetooth/BluetoothService.cpp
@@ -7,35 +7,33 @@
 #include "base/basictypes.h"
 
 #include "BluetoothService.h"
 
 #include "BluetoothManager.h"
 #include "BluetoothParent.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothServiceChildProcess.h"
+#include "BluetoothUtils.h"
 
 #include "jsapi.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #include "mozilla/Util.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "nsContentUtils.h"
-#include "nsIDOMDOMRequest.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITimer.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOM.h"
-#include "nsXPCOMCIDInternal.h"
-#include "nsXULAppAPI.h"
 
 #if defined(MOZ_B2G_BT)
 # if defined(MOZ_BLUETOOTH_GONK)
 #  include "BluetoothGonkService.h"
 # elif defined(MOZ_BLUETOOTH_DBUS)
 #  include "BluetoothDBusService.h"
 # else
 #  error No_suitable_backend_for_bluetooth!
@@ -58,25 +56,16 @@ StaticRefPtr<BluetoothService> gBluetoot
 bool gInShutdown = false;
 
 bool
 IsMainProcess()
 {
   return XRE_GetProcessType() == GeckoProcessType_Default;
 }
 
-PLDHashOperator
-RemoveAllSignalHandlers(const nsAString& aKey,
-                        nsAutoPtr<BluetoothSignalObserverList>& aData,
-                        void* aUserArg)
-{
-  aData->RemoveObserver(static_cast<BluetoothSignalObserver*>(aUserArg));
-  return aData->Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
-}
-
 void
 ShutdownTimeExceeded(nsITimer* aTimer, void* aClosure)
 {
   MOZ_ASSERT(NS_IsMainThread());
   *static_cast<bool*>(aClosure) = true;
 }
 
 void
@@ -264,31 +253,24 @@ BluetoothService::Init()
 
   // Only the main process should observe bluetooth settings changes.
   if (IsMainProcess() &&
       NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false))) {
     NS_WARNING("Failed to add settings change observer!");
     return false;
   }
 
-  mRegisteredForLocalAgent = true;
-
   return true;
 }
 
 void
 BluetoothService::Cleanup()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (mRegisteredForLocalAgent) {
-    UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(LOCAL_AGENT_PATH), this);
-    UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(REMOTE_AGENT_PATH), this);
-    mRegisteredForLocalAgent = false;
-  }
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (obs &&
       (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) ||
        NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID)))) {
     NS_WARNING("Can't unregister observers!");
   }
 }
@@ -328,24 +310,32 @@ BluetoothService::UnregisterBluetoothSig
 }
 
 void
 BluetoothService::UnregisterAllSignalHandlers(BluetoothSignalObserver* aHandler)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aHandler);
 
-  mBluetoothSignalObserverTable.Enumerate(RemoveAllSignalHandlers, aHandler);
+  mBluetoothSignalObserverTable.Clear();
 }
 
 void
 BluetoothService::DistributeSignal(const BluetoothSignal& aSignal)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  // Notify observers that a message has been sent
+
+  if (aSignal.path().EqualsLiteral(LOCAL_AGENT_PATH)) {
+    Notify(aSignal);
+    return;
+  } else if (aSignal.path().EqualsLiteral(REMOTE_AGENT_PATH)) {
+    Notify(aSignal);
+    return;
+  }
+
   BluetoothSignalObserverList* ol;
   if (!mBluetoothSignalObserverTable.Get(aSignal.path(), &ol)) {
 #if DEBUG
     nsAutoCString msg("No observer registered for path ");
     msg.Append(NS_ConvertUTF16toUTF8(aSignal.path()));
     NS_WARNING(msg.get());
 #endif
     return;
@@ -378,21 +368,16 @@ BluetoothService::StartStopBluetooth(boo
   if (!mBluetoothCommandThread) {
     MOZ_ASSERT(!gInShutdown);
 
     rv = NS_NewNamedThread("BluetoothCmd",
                            getter_AddRefs(mBluetoothCommandThread));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  if (aStart) {
-    RegisterBluetoothSignalHandler(NS_LITERAL_STRING(LOCAL_AGENT_PATH), this);
-    RegisterBluetoothSignalHandler(NS_LITERAL_STRING(REMOTE_AGENT_PATH), this);
-  }
-
   nsCOMPtr<nsIRunnable> runnable = new ToggleBtTask(aStart);
   rv = mBluetoothCommandThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void
@@ -414,22 +399,19 @@ BluetoothService::SetEnabled(bool aEnabl
     unused << childActors[index]->SendEnabled(aEnabled);
   }
 
   if (aEnabled) {
     BluetoothManagerList::ForwardIterator iter(mLiveManagers);
     BluetoothSignalObserverList* ol;
     nsString managerPath = NS_LITERAL_STRING("/");
 
-    // Skip when BluetoothManager has been registered in constructor
     // Re-register here after toggling due to table mBluetoothSignalObserverTable was cleared
-    if (!mBluetoothSignalObserverTable.Get(managerPath, &ol)) {
-      while (iter.HasMore()) {
-        RegisterBluetoothSignalHandler(managerPath, (BluetoothSignalObserver*)iter.GetNext());
-      }
+    while (iter.HasMore()) {
+      RegisterBluetoothSignalHandler(managerPath, (BluetoothSignalObserver*)iter.GetNext());
     }
   } else {
     mBluetoothSignalObserverTable.Clear();
   }
 
   BluetoothManagerList::ForwardIterator iter(mLiveManagers);
   while (iter.HasMore()) {
     if (NS_FAILED(iter.GetNext()->FireEnabledDisabledEvent(aEnabled))) {
@@ -631,27 +613,25 @@ BluetoothService::HandleShutdown()
 void
 BluetoothService::RegisterManager(BluetoothManager* aManager)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aManager);
   MOZ_ASSERT(!mLiveManagers.Contains(aManager));
 
   mLiveManagers.AppendElement(aManager);
-  RegisterBluetoothSignalHandler(aManager->GetPath(), aManager);
 }
 
 void
 BluetoothService::UnregisterManager(BluetoothManager* aManager)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aManager);
   MOZ_ASSERT(mLiveManagers.Contains(aManager));
 
-  UnregisterBluetoothSignalHandler(aManager->GetPath(), aManager);
   mLiveManagers.RemoveElement(aManager);
 }
 
 // static
 BluetoothService*
 BluetoothService::Get()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -697,49 +677,16 @@ BluetoothService::Observe(nsISupports* a
   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     return HandleShutdown();
   }
 
   MOZ_ASSERT(false, "BluetoothService got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
-bool
-SetJsObject(JSContext* aContext,
-            JSObject* aObj,
-            const InfallibleTArray<BluetoothNamedValue>& aData)
-{
-  for (uint32_t i = 0; i < aData.Length(); i++) {
-    jsval v;
-    if (aData[i].value().type() == BluetoothValue::TnsString) {
-      nsString data = aData[i].value().get_nsString();
-      JSString* JsData = JS_NewStringCopyN(aContext,
-                                           NS_ConvertUTF16toUTF8(data).get(),
-                                           data.Length());
-      NS_ENSURE_TRUE(JsData, false);
-      v = STRING_TO_JSVAL(JsData);
-    } else if (aData[i].value().type() == BluetoothValue::Tuint32_t) {
-      int data = aData[i].value().get_uint32_t();
-      v = INT_TO_JSVAL(data);
-    } else if (aData[i].value().type() == BluetoothValue::Tbool) {
-      bool data = aData[i].value().get_bool();
-      v = BOOLEAN_TO_JSVAL(data);
-    } else {
-      NS_WARNING("SetJsObject: Parameter is not handled");
-    }
-
-    if (!JS_SetProperty(aContext, aObj,
-                        NS_ConvertUTF16toUTF8(aData[i].name()).get(),
-                        &v)) {
-      return false;
-    }
-  }
-  return true;
-}
-
 void
 BluetoothService::Notify(const BluetoothSignal& aData)
 {
   InfallibleTArray<BluetoothNamedValue> arr = aData.value().get_ArrayOfBluetoothNamedValue();
   nsString type;
 
   JSContext* cx = nsContentUtils::GetSafeJSContext();
   NS_ASSERTION(!::JS_IsExceptionPending(cx),
@@ -782,15 +729,12 @@ BluetoothService::Notify(const Bluetooth
     warningMsg.AssignLiteral("Not handling service signal: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
     NS_WARNING(warningMsg.get());
 #endif
   }
 
   nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
     do_GetService("@mozilla.org/system-message-internal;1");
+  NS_ENSURE_TRUE_VOID(systemMessenger);
 
-  if (!systemMessenger) {
-    NS_WARNING("Failed to get SystemMessenger service!");
-    return;
-  }
   systemMessenger->BroadcastMessage(type, OBJECT_TO_JSVAL(obj));
 }
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -300,18 +300,18 @@ public:
   bool
   IsEnabled() const
   {
     return mEnabled;
   }
 
 protected:
   BluetoothService()
-  : mEnabled(false), mSettingsCheckInProgress(false),
-    mRegisteredForLocalAgent(false)
+  : mEnabled(false)
+  , mSettingsCheckInProgress(false)
 #ifdef DEBUG
     , mLastRequestedEnable(false)
 #endif
   {
     mBluetoothSignalObserverTable.Init();
   }
 
   virtual ~BluetoothService();
@@ -394,17 +394,16 @@ protected:
 
   BluetoothSignalObserverTable mBluetoothSignalObserverTable;
 
   typedef nsTObserverArray<BluetoothManager*> BluetoothManagerList;
   BluetoothManagerList mLiveManagers;
 
   bool mEnabled;
   bool mSettingsCheckInProgress;
-  bool mRegisteredForLocalAgent;
 
 #ifdef DEBUG
   bool mLastRequestedEnable;
 #endif
 };
 
 END_BLUETOOTH_NAMESPACE
 
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -109,24 +109,28 @@ IceCandidate.prototype = {
                                       Ci.nsIDOMRTCIceCandidate
                                     ],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIDOMRTCIceCandidate, Ci.nsIDOMGlobalObjectConstructor
   ]),
 
-  constructor: function(win, cand, mid, mline) {
+  constructor: function(win, candidateInitDict) {
     if (this._win) {
       throw new Error("Constructor already called");
     }
     this._win = win;
-    this.candidate = cand;
-    this.sdpMid = mid;
-    this.sdpMLineIndex = mline;
+    if (candidateInitDict !== undefined) {
+      this.candidate = candidateInitDict.candidate || null;
+      this.sdpMid = candidateInitDict.sdbMid || null;
+      this.sdpMLineIndex = candidateInitDict.sdpMLineIndex || null;
+    } else {
+      this.candidate = this.sdpMid = this.sdpMLineIndex = null;
+    }
   }
 };
 
 function SessionDescription(type, sdp) {
   this.type = type;
   this.sdp = sdp;
 }
 SessionDescription.prototype = {
@@ -139,23 +143,27 @@ SessionDescription.prototype = {
                                       Ci.nsIDOMRTCSessionDescription
                                     ],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIDOMRTCSessionDescription, Ci.nsIDOMGlobalObjectConstructor
   ]),
 
-  constructor: function(win, type, sdp) {
+  constructor: function(win, descriptionInitDict) {
     if (this._win) {
       throw new Error("Constructor already called");
     }
     this._win = win;
-    this.type = type;
-    this.sdp = sdp;
+    if (descriptionInitDict !== undefined) {
+      this.type = descriptionInitDict.type || null;
+      this.sdp = descriptionInitDict.sdp || null;
+    } else {
+      this.type = this.sdp = null;
+    }
   },
 
   toString: function() {
     return JSON.stringify({
       type: this.type, sdp: this.sdp
     });
   }
 };
@@ -179,16 +187,17 @@ function PeerConnection() {
    * has finished, we will check the queue for the next operation and execute
    * if neccesary. The _pending flag indicates whether an operation is currently
    * in progress.
    */
   this._pending = false;
 
   // Public attributes.
   this.onaddstream = null;
+  this.onopen = null;
   this.onremovestream = null;
   this.onicecandidate = null;
   this.onstatechange = null;
   this.ongatheringchange = null;
   this.onicechange = null;
   this.remoteDescription = null;
 
   // Data channel.
--- a/dom/media/nsIDOMRTCPeerConnection.idl
+++ b/dom/media/nsIDOMRTCPeerConnection.idl
@@ -75,16 +75,17 @@ interface nsIDOMRTCPeerConnection : nsIS
   readonly attribute jsval localDescription;
   readonly attribute jsval remoteDescription;
 
   readonly attribute jsval localStreams; // MediaStream[]
   readonly attribute jsval remoteStreams; // MediaStream[]
 
   /* Event handlers. TODO: Use real EventTarget */
   attribute RTCPeerConnectionCallback onaddstream;
+  attribute RTCPeerConnectionCallback onopen;
   attribute RTCPeerConnectionCallback onremovestream;
   attribute RTCPeerConnectionCallback onicecandidate;
   attribute RTCPeerConnectionCallback onstatechange;
   attribute RTCPeerConnectionCallback ongatheringchange;
   attribute RTCPeerConnectionCallback onicechange;
 
   /* Data channels */
   nsIDOMDataChannel createDataChannel([optional] in ACString label,
--- a/dom/sms/tests/marionette/manifest.ini
+++ b/dom/sms/tests/marionette/manifest.ini
@@ -6,16 +6,17 @@ qemu = true
 ;[test_between_emulators.py]
 [test_incoming.js]
 [test_outgoing.js]
 [test_message_classes.js]
 [test_incoming_delete.js]
 [test_outgoing_delete.js]
 [test_getmessage.js]
 [test_getmessage_notfound.js]
+[test_getmessages.js]
 [test_filter_date.js]
 [test_filter_date_notfound.js]
 [test_filter_number_single.js]
 [test_filter_number_multiple.js]
 [test_filter_received.js]
 [test_filter_sent.js]
 [test_filter_read.js]
 [test_filter_unread.js]
new file mode 100644
--- /dev/null
+++ b/dom/sms/tests/marionette/test_getmessages.js
@@ -0,0 +1,225 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 20000;
+
+SpecialPowers.addPermission("sms", true, document);
+SpecialPowers.setBoolPref("dom.sms.enabled", true);
+
+let sms = window.navigator.mozSms;
+let numberMsgs = 10;
+let smsList = new Array();
+
+function verifyInitialState() {
+  log("Verifying initial state.");
+  ok(sms, "mozSms");
+  // Ensure test is starting clean with no existing sms messages
+  deleteAllMsgs(simulateIncomingSms);
+}
+
+function deleteAllMsgs(nextFunction) {
+  let msgList = new Array();
+  let smsFilter = new MozSmsFilter;
+
+  let request = sms.getMessages(smsFilter, false);
+  ok(request instanceof MozSmsRequest,
+      "request is instanceof " + request.constructor);
+
+  request.onsuccess = function(event) {
+    ok(event.target.result, "smsrequest event.target.result");
+    cursor = event.target.result;
+    // Check if message was found
+    if (cursor.message) {
+      msgList.push(cursor.message.id);
+      // Now get next message in the list
+      cursor.continue();
+    } else {
+      // No (more) messages found
+      if (msgList.length) {
+        log("Found " + msgList.length + " SMS messages to delete.");
+        deleteMsgs(msgList, nextFunction);
+      } else {
+        log("No SMS messages found.");
+        nextFunction();
+      }
+    }
+  };
+
+  request.onerror = function(event) {
+    log("Received 'onerror' smsrequest event.");
+    ok(event.target.error, "domerror obj");
+    log("sms.getMessages error: " + event.target.error.name);
+    ok(false,"Could not get SMS messages");
+    cleanUp();
+  };
+}
+
+function deleteMsgs(msgList, nextFunction) {
+  let smsId = msgList.shift();
+
+  log("Deleting SMS (id: " + smsId + ").");
+  let request = sms.delete(smsId);
+  ok(request instanceof MozSmsRequest,
+      "request is instanceof " + request.constructor);
+
+  request.onsuccess = function(event) {
+    log("Received 'onsuccess' smsrequest event.");
+    if (event.target.result) {
+      // Message deleted, continue until none are left
+      if (msgList.length) {
+        deleteMsgs(msgList, nextFunction);
+      } else {
+        log("Finished deleting SMS messages.");
+        nextFunction();
+      }
+    } else {
+      log("SMS delete failed.");
+      ok(false,"sms.delete request returned false");
+      cleanUp();
+    }
+  };
+
+  request.onerror = function(event) {
+    log("Received 'onerror' smsrequest event.");
+    ok(event.target.error, "domerror obj");
+    ok(false, "sms.delete request returned unexpected error: "
+        + event.target.error.name );
+    cleanUp();
+  };
+}
+
+function simulateIncomingSms() {
+  let text = "Incoming SMS number " + (smsList.length + 1);
+  let remoteNumber = "5552229797";
+
+  log("Simulating incoming SMS number " + (smsList.length + 1) + " of "
+      + numberMsgs + ".");
+
+  // Simulate incoming sms sent from remoteNumber to our emulator
+  rcvdEmulatorCallback = false;
+  runEmulatorCmd("sms send " + remoteNumber + " " + text, function(result) {
+    is(result[0], "OK", "emulator callback");
+    rcvdEmulatorCallback = true;
+  });
+}
+
+// Callback for incoming sms
+sms.onreceived = function onreceived(event) {
+  log("Received 'onreceived' sms event.");
+  let incomingSms = event.message;
+  log("Received SMS (id: " + incomingSms.id + ").");
+
+  // Add newly received message to array of received msgs
+  smsList.push(incomingSms);
+
+  // Wait for emulator to catch up before continuing
+  waitFor(nextRep,function() {
+    return(rcvdEmulatorCallback);
+  });
+};
+
+function nextRep() {
+  if (smsList.length < numberMsgs) {
+    simulateIncomingSms();
+  } else {
+    log("Received " + numberMsgs + " sms messages in total.");
+    getMsgs(false);
+  }
+}
+
+function getMsgs(reverse) {
+  let smsFilter = new MozSmsFilter;
+  let foundSmsCount = 0;
+  let foundSmsList = new Array();
+
+  if (!reverse) {
+    log("Getting the sms messages.");
+  } else {
+    log("Getting the sms messages in reverse order.");
+  }
+
+  // Note: This test is intended for getMessages, so just a basic test with
+  // no filter (default); separate tests will be written for sms filtering
+  let request = sms.getMessages(smsFilter, reverse);
+  ok(request instanceof MozSmsRequest,
+      "request is instanceof " + request.constructor);
+
+  request.onsuccess = function(event) {
+    log("Received 'onsuccess' smsrequest event.");
+    ok(event.target.result, "smsrequest event.target.result");
+    cursor = event.target.result;
+
+    if (cursor.message) {
+      // Another message found
+      log("Got SMS (id: " + cursor.message.id + ").");
+      foundSmsCount++;
+      // Store found message
+      foundSmsList.push(cursor.message);
+      // Now get next message in the list
+      cursor.continue();
+    } else {
+      // No more messages; ensure correct number found
+      if (foundSmsCount == numberMsgs) {
+        log("SMS getMessages returned " + foundSmsCount +
+            " messages as expected.");  
+      } else {
+        log("SMS getMessages returned " + foundSmsCount +
+            " messages, but expected " + numberMsgs + ".");
+        ok(false, "Incorrect number of messages returned by sms.getMessages");
+      }
+      verifyFoundMsgs(foundSmsList, reverse);
+    }
+  };
+
+  request.onerror = function(event) {
+    log("Received 'onerror' smsrequest event.");
+    ok(event.target.error, "domerror obj");
+    log("sms.getMessages error: " + event.target.error.name);
+    ok(false,"Could not get SMS messages");
+    cleanUp();
+  };
+}
+
+function verifyFoundMsgs(foundSmsList, reverse) {
+  if (reverse) {
+    smsList.reverse();
+  }
+  for (var x = 0; x < numberMsgs; x++) {
+    is(foundSmsList[x].id, smsList[x].id, "id");
+    is(foundSmsList[x].body, smsList[x].body, "body");
+    is(foundSmsList[x].delivery, smsList[x].delivery, "delivery");
+    is(foundSmsList[x].read, smsList[x].read, "read");
+
+    // Bug 805799: receiver null when onreceived event is fired, until do a
+    // getMessage. Default emulator (receiver) phone number is 15555215554
+    if (!smsList[x].receiver) {
+      is(foundSmsList[x].receiver, "15555215554", "receiver");
+    } else {
+      is(foundSmsList[x].receiver, smsList[x].receiver, "receiver");
+    }
+
+    is(foundSmsList[x].sender, smsList[x].sender, "sender");
+    is(foundSmsList[x].timestamp.getTime(), smsList[x].timestamp.getTime(),
+        "timestamp");
+  }
+
+  log("Content in all of the returned SMS messages is correct.");
+
+  if (!reverse) {
+    // Now get messages in reverse
+    getMsgs(true);
+  } else {
+    // Finished, delete all messages
+    deleteAllMsgs(cleanUp);
+  };
+}
+
+function cleanUp() {
+  sms.onreceived = null;
+  SpecialPowers.removePermission("sms", document);
+  SpecialPowers.clearUserPref("dom.sms.enabled");
+  finish();
+}
+
+// Start the test
+verifyInitialState();
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/license.myspell
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002 Kevin B. Hendricks, Stratford, Ontario, Canada
+ * And Contributors.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. All modifications to the source code must be clearly marked as
+ *    such.  Binary redistributions based on modified source code
+ *    must be clearly marked as modified versions in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KEVIN B. HENDRICKS AND CONTRIBUTORS 
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL 
+ * KEVIN B. HENDRICKS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * NOTE: A special thanks and credit goes to Geoff Kuenning
+ * the creator of ispell.  MySpell's affix algorithms were
+ * based on those of ispell which should be noted is
+ * copyright Geoff Kuenning et.al. and now available
+ * under a BSD style license. For more information on ispell
+ * and affix compression in general, please see:
+ * http://www.cs.ucla.edu/ficus-members/geoff/ispell.html
+ * (the home page for ispell)
+ *
+ * An almost complete rewrite  of MySpell for use by  
+ * the Mozilla project has been developed by David Einstein 
+ * (Deinst@world.std.com).  David and I are now 
+ * working on parallel development tracks to help 
+ * our respective projects (Mozilla and OpenOffice.org 
+ * and we will maintain full affix file and dictionary 
+ * file compatibility and work on merging our versions 
+ * of MySpell back into a single tree. David has been 
+ * a significant help in improving MySpell.
+ * 
+ * Special thanks also go to La'szlo' Ne'meth 
+ * <nemethl@gyorsposta.hu> who is the author of the 
+ * Hungarian dictionary and who developed and contributed 
+ * the code to support compound words in MySpell 
+ * and fixed numerous problems with the encoding 
+ * case conversion tables.
+ *
+ */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
@@ -278,26 +278,32 @@ hb_ot_shape_complex_categorize (const hb
     case HB_SCRIPT_TAKRI:
 
       /* Only use Indic shaper if the font has Indic tables. */
       if (planner->map.found_script[0])
 	return &_hb_ot_complex_shaper_indic;
       else
 	return &_hb_ot_complex_shaper_default;
 
+
     case HB_SCRIPT_KHMER:
-      /* If the font has 'liga', let the generic shaper do it. */
-      if (!planner->map.found_script[0] ||
+      /* A number of Khmer fonts in the wild don't have a 'pref' feature,
+       * and as such won't shape properly via the Indic shaper;
+       * however, they typically have 'liga' / 'clig' features that implement
+       * the necessary "reordering" by means of ligature substitutions.
+       * So we send such pref-less fonts through the generic shaper instead;
+       * use Indic only if the 'khmr' script has the 'pref' feature. */
+      if (planner->map.found_script[0] &&
 	  hb_ot_layout_language_find_feature (planner->face, HB_OT_TAG_GSUB,
 					      planner->map.script_index[0],
 					      planner->map.language_index[0],
-					      HB_TAG ('l','i','g','a'), NULL))
+					      HB_TAG ('p','r','e','f'), NULL))
+	return &_hb_ot_complex_shaper_indic;
+      else
 	return &_hb_ot_complex_shaper_default;
-      else
-	return &_hb_ot_complex_shaper_indic;
 
 
     case HB_SCRIPT_MYANMAR:
       /* For Myanmar, we only want to use the Indic shaper if the "new" script
        * tag is found.  For "old" script tag we want to use the default shaper. */
       if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2'))
 	return &_hb_ot_complex_shaper_indic;
       else
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -86,17 +86,16 @@ NS_IMPL_ISUPPORTS5(imgRequest,
                    nsIChannelEventSink,
                    nsIInterfaceRequestor,
                    nsIAsyncVerifyRedirectCallback)
 
 imgRequest::imgRequest(imgLoader* aLoader)
  : mLoader(aLoader)
  , mStatusTracker(new imgStatusTracker(nullptr, this))
  , mValidator(nullptr)
- , mImageSniffers("image-sniffing-services")
  , mInnerWindowId(0)
  , mCORSMode(imgIRequest::CORS_NONE)
  , mDecodeRequested(false)
  , mIsMultiPartChannel(false)
  , mGotData(false)
  , mIsInCache(false)
  , mResniffMimeType(false)
 {
@@ -688,17 +687,16 @@ NS_IMETHODIMP imgRequest::OnStopRequest(
   GetStatusTracker().OnStopRequest(lastPart, status);
 
   mTimedChannel = nullptr;
   return NS_OK;
 }
 
 struct mimetype_closure
 {
-  imgRequest* request;
   nsACString* newType;
 };
 
 /* prototype for these defined below */
 static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
                                          uint32_t toOffset, uint32_t count, uint32_t *writeCount);
 
 /** nsIStreamListener methods **/
@@ -717,17 +715,16 @@ imgRequest::OnDataAvailable(nsIRequest *
 
   if (!mGotData || mResniffMimeType) {
     LOG_SCOPE(GetImgLog(), "imgRequest::OnDataAvailable |First time through... finding mimetype|");
 
     mGotData = true;
 
     mimetype_closure closure;
     nsAutoCString newType;
-    closure.request = this;
     closure.newType = &newType;
 
     /* look at the first few bytes and see if we can tell what the data is from that
      * since servers tend to lie. :(
      */
     uint32_t out;
     inStr->ReadSegments(sniff_mimetype_callback, &closure, count, &out);
 
@@ -943,46 +940,22 @@ static NS_METHOD sniff_mimetype_callback
                                          uint32_t count,
                                          uint32_t *writeCount)
 {
   mimetype_closure* closure = static_cast<mimetype_closure*>(data);
 
   NS_ASSERTION(closure, "closure is null!");
 
   if (count > 0)
-    closure->request->SniffMimeType(fromRawSegment, count, *closure->newType);
+    imgLoader::GetMimeTypeFromContent(fromRawSegment, count, *closure->newType);
 
   *writeCount = 0;
   return NS_ERROR_FAILURE;
 }
 
-void
-imgRequest::SniffMimeType(const char *buf, uint32_t len, nsACString& newType)
-{
-  imgLoader::GetMimeTypeFromContent(buf, len, newType);
-
-  // The vast majority of the time, imgLoader will find a gif/jpeg/png image
-  // and fill newType with the sniffed MIME type.
-  if (!newType.IsEmpty())
-    return;
-
-  // When our sniffing fails, we want to query registered image decoders
-  // to see if they can identify the image. If we always trusted the server
-  // to send the right MIME, images sent as text/plain would not be rendered.
-  const nsCOMArray<nsIContentSniffer>& sniffers = mImageSniffers.GetEntries();
-  uint32_t length = sniffers.Count();
-  for (uint32_t i = 0; i < length; ++i) {
-    nsresult rv =
-      sniffers[i]->GetMIMETypeFromContent(nullptr, (const uint8_t *) buf, len, newType);
-    if (NS_SUCCEEDED(rv) && !newType.IsEmpty()) {
-      return;
-    }
-  }
-}
-
 
 /** nsIInterfaceRequestor methods **/
 
 NS_IMETHODIMP
 imgRequest::GetInterface(const nsIID & aIID, void **aResult)
 {
   if (!mPrevChannelSink || aIID.Equals(NS_GET_IID(nsIChannelEventSink)))
     return QueryInterface(aIID, aResult);
--- a/image/src/imgRequest.h
+++ b/image/src/imgRequest.h
@@ -3,27 +3,26 @@
  * 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 imgRequest_h__
 #define imgRequest_h__
 
 #include "nsIChannelEventSink.h"
-#include "nsIContentSniffer.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIRequest.h"
 #include "nsIProperties.h"
 #include "nsIStreamListener.h"
 #include "nsIURI.h"
 #include "nsIPrincipal.h"
 #include "nsITimedChannel.h"
 #include "nsIApplicationCache.h"
 
-#include "nsCategoryCache.h"
+#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsStringGlue.h"
 #include "nsError.h"
 #include "imgIRequest.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 
 class imgCacheValidator;
 class imgStatusTracker;
@@ -59,18 +58,16 @@ public:
                 nsIPrincipal* aLoadingPrincipal,
                 int32_t aCORSMode);
 
   // Callers must call imgRequestProxy::Notify later.
   void AddProxy(imgRequestProxy *proxy);
 
   nsresult RemoveProxy(imgRequestProxy *proxy, nsresult aStatus);
 
-  void SniffMimeType(const char *buf, uint32_t len, nsACString& newType);
-
   // Cancel, but also ensure that all work done in Init() is undone. Call this
   // only when the channel has failed to open, and so calling Cancel() on it
   // won't be sufficient.
   void CancelAndAbort(nsresult aStatus);
 
   // Methods that get forwarded to the Image, or deferred until it's
   // instantiated.
   nsresult LockImage();
@@ -213,17 +210,16 @@ private:
 
   nsCString mContentType;
 
   nsRefPtr<imgCacheEntry> mCacheEntry; /* we hold on to this to this so long as we have observers */
 
   void *mLoadId;
 
   imgCacheValidator *mValidator;
-  nsCategoryCache<nsIContentSniffer> mImageSniffers;
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
   nsCOMPtr<nsIChannel> mNewRedirectChannel;
 
   // The ID of the inner window origin, used for error reporting.
   uint64_t mInnerWindowId;
 
   // The CORS mode (defined in imgIRequest) this image was loaded with. By
   // default, imgIRequest::CORS_NONE.
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -104,16 +104,19 @@ CPPSRCS		= \
 		jsscope.cpp \
 		jsscript.cpp \
 		jsstr.cpp \
 		jstypedarray.cpp \
 		jsutil.cpp \
 		jswatchpoint.cpp \
 		jsweakmap.cpp \
 		jsworkers.cpp \
+		threadpool.cpp \
+		monitor.cpp \
+		forkjoin.cpp \
 		jswrapper.cpp \
 		jsxml.cpp \
 		prmjtime.cpp \
 		sharkctl.cpp \
 		ArgumentsObject.cpp \
 		ScopeObject.cpp \
 		Debugger.cpp \
 		GlobalObject.cpp \
--- a/js/src/assembler/assembler/ARMAssembler.h
+++ b/js/src/assembler/assembler/ARMAssembler.h
@@ -36,34 +36,17 @@
 // Some debug code uses s(n)printf for instruction logging.
 #include <stdio.h>
 
 #if ENABLE_ASSEMBLER && WTF_CPU_ARM_TRADITIONAL
 
 #include "AssemblerBufferWithConstantPool.h"
 #include "assembler/wtf/Assertions.h"
 
-#include "methodjit/Logging.h"
-#define IPFX    "        %s"
-#define ISPFX   "        "
-#ifdef JS_METHODJIT_SPEW
-# define MAYBE_PAD (isOOLPath ? ">  " : "")
-# define FIXME_INSN_PRINTING                                \
-    do {                                                   \
-        js::JaegerSpew(js::JSpew_Insns,                    \
-                       IPFX "FIXME insn printing %s:%d\n", \
-                       MAYBE_PAD,                          \
-                       __FILE__, __LINE__);                \
-    } while (0)
-#else
-# define MAYBE_PAD ""
-# define FIXME_INSN_PRINTING ((void) 0)
-#endif
-
-// TODO: We don't print the condition code in our JaegerSpew lines. Doing this
+// TODO: We don't print the condition code in our spew lines. Doing this
 // is awkward whilst maintaining a consistent field width.
 namespace js {
     namespace ion {
         class Assembler;
     }
 }
 
 namespace JSC {
@@ -138,26 +121,19 @@ namespace JSC {
         {
             return (FPRegisterID)(s*2);
         }
         inline FPRegisterID doubleShadow(FPRegisterID d)
         {
             return (FPRegisterID)(d / 2);
         }
     } // namespace ARMRegisters
-    class ARMAssembler {
+    class ARMAssembler : public GenericAssembler {
     public:
-        
-#ifdef JS_METHODJIT_SPEW
-        bool isOOLPath;
-        // Assign a default value to keep Valgrind quiet.
-        ARMAssembler() : isOOLPath(false) { }
-#else
-        ARMAssembler() { }
-#endif
+
         typedef ARMRegisters::RegisterID RegisterID;
         typedef ARMRegisters::FPRegisterID FPRegisterID;
         typedef AssemblerBufferWithConstantPool<2048, 4, 4, ARMAssembler> ARMBuffer;
         typedef SegmentedVector<int, 64> Jumps;
 
         unsigned char *buffer() const { return m_buffer.buffer(); }
         bool oom() const { return m_buffer.oom(); }
 
@@ -488,26 +464,24 @@ namespace JSC {
             spewInsWithOp2("mov", cc, rd, op2);
             emitInst(static_cast<ARMWord>(cc) | MOV, rd, ARMRegisters::r0, op2);
         }
 
 #if WTF_ARM_ARCH_VERSION >= 7
         void movw_r(int rd, ARMWord op2, Condition cc = AL)
         {
             ASSERT((op2 | 0xf0fff) == 0xf0fff);
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX    "%-15s %s, 0x%04x\n", MAYBE_PAD, "movw", nameGpReg(rd), (op2 & 0xfff) | ((op2 >> 4) & 0xf000));
+            spew("%-15s %s, 0x%04x", "movw", nameGpReg(rd), (op2 & 0xfff) | ((op2 >> 4) & 0xf000));
             m_buffer.putInt(static_cast<ARMWord>(cc) | MOVW | RD(rd) | op2);
         }
 
         void movt_r(int rd, ARMWord op2, Condition cc = AL)
         {
             ASSERT((op2 | 0xf0fff) == 0xf0fff);
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX    "%-15s %s, 0x%04x\n", MAYBE_PAD, "movt", nameGpReg(rd), (op2 & 0xfff) | ((op2 >> 4) & 0xf000));
+            spew("%-15s %s, 0x%04x", "movt", nameGpReg(rd), (op2 & 0xfff) | ((op2 >> 4) & 0xf000));
             m_buffer.putInt(static_cast<ARMWord>(cc) | MOVT | RD(rd) | op2);
         }
 #endif
 
         void movs_r(int rd, ARMWord op2, Condition cc = AL)
         {
             spewInsWithOp2("movs", cc, rd, op2);
             emitInst(static_cast<ARMWord>(cc) | MOV | SET_CC, rd, ARMRegisters::r0, op2);
@@ -546,37 +520,34 @@ namespace JSC {
         void muls_r(int rd, int rn, int rm, Condition cc = AL)
         {
             spewInsWithOp2("muls", cc, rd, rn, static_cast<ARMWord>(rm));
             m_buffer.putInt(static_cast<ARMWord>(cc) | MUL | SET_CC | RN(rd) | RS(rn) | RM(rm));
         }
 
         void mull_r(int rdhi, int rdlo, int rn, int rm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s, %s, %s\n", MAYBE_PAD, "mull", nameGpReg(rdlo), nameGpReg(rdhi), nameGpReg(rn), nameGpReg(rm));
+            spew("%-15s %s, %s, %s, %s", "mull", nameGpReg(rdlo), nameGpReg(rdhi), nameGpReg(rn), nameGpReg(rm));
             m_buffer.putInt(static_cast<ARMWord>(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm));
         }
 
         // pc relative loads (useful for loading from pools).
         void ldr_imm(int rd, ARMWord imm, Condition cc = AL)
         {
             char mnemonic[16];
             snprintf(mnemonic, 16, "ldr%s", nameCC(cc));
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX    "%-15s %s, =0x%x @ (%d) (reusable pool entry)\n", MAYBE_PAD, mnemonic, nameGpReg(rd), imm, static_cast<int32_t>(imm));
+            spew("%-15s %s, =0x%x @ (%d) (reusable pool entry)", mnemonic, nameGpReg(rd), imm, static_cast<int32_t>(imm));
             m_buffer.putIntWithConstantInt(static_cast<ARMWord>(cc) | DTR | DT_LOAD | DT_UP | RN(ARMRegisters::pc) | RD(rd), imm, true);
         }
 
         void ldr_un_imm(int rd, ARMWord imm, Condition cc = AL)
         {
             char mnemonic[16];
             snprintf(mnemonic, 16, "ldr%s", nameCC(cc));
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX    "%-15s %s, =0x%x @ (%d)\n", MAYBE_PAD, mnemonic, nameGpReg(rd), imm, static_cast<int32_t>(imm));
+            spew("%-15s %s, =0x%x @ (%d)", mnemonic, nameGpReg(rd), imm, static_cast<int32_t>(imm));
             m_buffer.putIntWithConstantInt(static_cast<ARMWord>(cc) | DTR | DT_LOAD | DT_UP | RN(ARMRegisters::pc) | RD(rd), imm);
         }
 
         void mem_imm_off(bool isLoad, bool isSigned, int size, bool posOffset,
                          int rd, int rb, ARMWord offset, Condition cc = AL)
         {
             ASSERT(size == 8 || size == 16 || size == 32);
             char const * mnemonic_act = (isLoad) ? ("ld") : ("st");
@@ -590,20 +561,19 @@ namespace JSC {
             case 2:
                 mnemonic_size = "h";
                 break;
             case 4:
                 mnemonic_size = "";
                 break;
             }
             char const * off_sign = (posOffset) ? ("+") : ("-");
-            js::JaegerSpew(js::JSpew_Insns, 
-                           IPFX "%sr%s%s %s, [%s, #%s%u]\n",
-                           MAYBE_PAD, mnemonic_act, mnemonic_sign, mnemonic_size,
-                           nameGpReg(rd), nameGpReg(rb), off_sign, offset);
+            spew("%sr%s%s %s, [%s, #%s%u]",
+                 mnemonic_act, mnemonic_sign, mnemonic_size,
+                 nameGpReg(rd), nameGpReg(rb), off_sign, offset);
             if (size == 32 || (size == 8 && !isSigned)) {
                 /* All (the one) 32 bit ops and the unsigned 8 bit ops use the original encoding.*/
                 emitInst(static_cast<ARMWord>(cc) | DTR |
                          (isLoad ? DT_LOAD : 0) |
                          (size == 8 ? DT_BYTE : 0) |
                          (posOffset ? DT_UP : 0), rd, rb, offset);
             } else {
                 /* All 16 bit ops and 8 bit unsigned use the newer encoding.*/
@@ -628,19 +598,18 @@ namespace JSC {
             case 2:
                 mnemonic_size = "h";
                 break;
             case 4:
                 mnemonic_size = "";
                 break;
             }
             char const * off_sign = (posOffset) ? ("+") : ("-");
-            js::JaegerSpew(js::JSpew_Insns, 
-                           IPFX "%sr%s%s %s, [%s, #%s%s]\n", MAYBE_PAD, mnemonic_act, mnemonic_sign, mnemonic_size,
-                           nameGpReg(rd), nameGpReg(rb), off_sign, nameGpReg(rm));
+            spew("%sr%s%s %s, [%s, #%s%s]", mnemonic_act, mnemonic_sign, mnemonic_size,
+                 nameGpReg(rd), nameGpReg(rb), off_sign, nameGpReg(rm));
             if (size == 32 || (size == 8 && !isSigned)) {
                 /* All (the one) 32 bit ops and the signed 8 bit ops use the original encoding.*/
                 emitInst(static_cast<ARMWord>(cc) | DTR |
                          (isLoad ? DT_LOAD : 0) |
                          (size == 8 ? DT_BYTE : 0) |
                          (posOffset ? DT_UP : 0) |
                          OP2_OFSREG, rd, rb, rm);
             } else {
@@ -654,175 +623,175 @@ namespace JSC {
         }
 
         // Data transfers like this:
         //  LDR rd, [rb, +offset]
         //  STR rd, [rb, +offset]
         void dtr_u(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
         {
             char const * mnemonic = (isLoad) ? ("ldr") : ("str");
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
+            spew("%-15s %s, [%s, #+%u]",
+                 mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
             emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0) | DT_UP, rd, rb, offset);
         }
 
         // Data transfers like this:
         //  LDR rd, [rb, +rm]
         //  STR rd, [rb, +rm]
         void dtr_ur(bool isLoad, int rd, int rb, int rm, Condition cc = AL)
         {
             char const * mnemonic = (isLoad) ? ("ldr") : ("str");
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, +%s]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
+            spew("%-15s %s, [%s, +%s]",
+                 mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
             emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0) | DT_UP | OP2_OFSREG, rd, rb, rm);
         }
 
         // Data transfers like this:
         //  LDR rd, [rb, -offset]
         //  STR rd, [rb, -offset]
         void dtr_d(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
         {
             char const * mnemonic = (isLoad) ? ("ldr") : ("str");
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
+            spew("%-15s %s, [%s, #-%u]",
+                 mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
             emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0), rd, rb, offset);
         }
 
         // Data transfers like this:
         //  LDR rd, [rb, -rm]
         //  STR rd, [rb, -rm]
         void dtr_dr(bool isLoad, int rd, int rb, int rm, Condition cc = AL)
         {
             char const * mnemonic = (isLoad) ? ("ldr") : ("str");
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, -%s]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
+            spew("%-15s %s, [%s, -%s]",
+                 mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
             emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0) | OP2_OFSREG, rd, rb, rm);
         }
 
         // Data transfers like this:
         //  LDRB rd, [rb, +offset]
         //  STRB rd, [rb, +offset]
         void dtrb_u(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
         {
             char const * mnemonic = (isLoad) ? ("ldrb") : ("strb");
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
+            spew("%-15s %s, [%s, #+%u]",
+                 mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
             emitInst(static_cast<ARMWord>(cc) | DTR | DT_BYTE | (isLoad ? DT_LOAD : 0) | DT_UP, rd, rb, offset);
         }
 
         // Data transfers like this:
         //  LDRSB rd, [rb, +offset]
         //  STRSB rd, [rb, +offset]
         void dtrsb_u(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
         {
             char const * mnemonic = (isLoad) ? ("ldrsb") : ("strb");
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
+            spew("%-15s %s, [%s, #+%u]",
+                 mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
             emitInst(static_cast<ARMWord>(cc) | DTRH | HDT_S | (isLoad ? DT_LOAD : 0) | DT_UP, rd, rb, offset);
         }
 
         // Data transfers like this:
         //  LDRB rd, [rb, +rm]
         //  STRB rd, [rb, +rm]
         void dtrb_ur(bool isLoad, int rd, int rb, int rm, Condition cc = AL)
         {
             char const * mnemonic = (isLoad) ? ("ldrb") : ("strb");
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, +%s]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
+            spew("%-15s %s, [%s, +%s]",
+                 mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
             emitInst(static_cast<ARMWord>(cc) | DTR | DT_BYTE | (isLoad ? DT_LOAD : 0) | DT_UP | OP2_OFSREG, rd, rb, rm);
         }
 
         // Data transfers like this:
         //  LDRB rd, [rb, #-offset]
         //  STRB rd, [rb, #-offset]
         void dtrb_d(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
         {
             char const * mnemonic = (isLoad) ? ("ldrb") : ("strb");
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
+            spew("%-15s %s, [%s, #-%u]",
+                 mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
             emitInst(static_cast<ARMWord>(cc) | DTR | DT_BYTE | (isLoad ? DT_LOAD : 0), rd, rb, offset);
         }
 
         // Data transfers like this:
         //  LDRSB rd, [rb, #-offset]
         //  STRSB rd, [rb, #-offset]
         void dtrsb_d(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
         {
             ASSERT(isLoad); /*can only do signed byte loads, not stores*/
             char const * mnemonic = (isLoad) ? ("ldrsb") : ("strb");
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
+            spew("%-15s %s, [%s, #-%u]",
+                 mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
             emitInst(static_cast<ARMWord>(cc) | DTRH | HDT_S | (isLoad ? DT_LOAD : 0), rd, rb, offset);
         }
 
         // Data transfers like this:
         //  LDRB rd, [rb, -rm]
         //  STRB rd, [rb, -rm]
         void dtrb_dr(bool isLoad, int rd, int rb, int rm, Condition cc = AL)
         {
             char const * mnemonic = (isLoad) ? ("ldrb") : ("strb");
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, -%s]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
+            spew("%-15s %s, [%s, -%s]",
+                 mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
             emitInst(static_cast<ARMWord>(cc) | DTR | DT_BYTE | (isLoad ? DT_LOAD : 0) | OP2_OFSREG, rd, rb, rm);
         }
 
         void ldrh_r(int rd, int rb, int rm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, +%s]\n", MAYBE_PAD, "ldrh", nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
+            spew("%-15s %s, [%s, +%s]",
+                 "ldrh", nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
             emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | DT_UP | DT_PRE, rd, rb, rm);
         }
 
         void ldrh_d(int rd, int rb, ARMWord offset, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, "ldrh", nameGpReg(rd), nameGpReg(rb), offset);
+            spew("%-15s %s, [%s, #-%u]",
+                 "ldrh", nameGpReg(rd), nameGpReg(rb), offset);
             emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | DT_PRE, rd, rb, offset);
         }
 
         void ldrh_u(int rd, int rb, ARMWord offset, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, "ldrh", nameGpReg(rd), nameGpReg(rb), offset);
+            spew("%-15s %s, [%s, #+%u]",
+                 "ldrh", nameGpReg(rd), nameGpReg(rb), offset);
             emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | DT_UP | DT_PRE, rd, rb, offset);
         }
 
         void ldrsh_d(int rd, int rb, ARMWord offset, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, "ldrsh", nameGpReg(rd), nameGpReg(rb), offset);
+            spew("%-15s %s, [%s, #-%u]",
+                 "ldrsh", nameGpReg(rd), nameGpReg(rb), offset);
             emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | HDT_S | DT_PRE, rd, rb, offset); 
        }
 
         void ldrsh_u(int rd, int rb, ARMWord offset, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, "ldrsh", nameGpReg(rd), nameGpReg(rb), offset);
+            spew("%-15s %s, [%s, #+%u]",
+                 "ldrsh", nameGpReg(rd), nameGpReg(rb), offset);
             emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | HDT_S | DT_UP | DT_PRE, rd, rb, offset);
         }
 
         void strh_r(int rb, int rm, int rd, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, [%s, +%s]\n", MAYBE_PAD, "strh", nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
+            spew("%-15s %s, [%s, +%s]",
+                 "strh", nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
             emitInst(static_cast<ARMWord>(cc) | STRH | HDT_UH | DT_UP | DT_PRE, rd, rb, rm);
         }
 
         void push_r(int reg, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s {%s}\n", MAYBE_PAD, "push", nameGpReg(reg));
+            spew("%-15s {%s}",
+                 "push", nameGpReg(reg));
             ASSERT(ARMWord(reg) <= 0xf);
             m_buffer.putInt(cc | DTR | DT_WB | RN(ARMRegisters::sp) | RD(reg) | 0x4);
         }
 
         void pop_r(int reg, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s {%s}\n", MAYBE_PAD, "pop", nameGpReg(reg));
+            spew("%-15s {%s}",
+                 "pop", nameGpReg(reg));
             ASSERT(ARMWord(reg) <= 0xf);
             m_buffer.putInt(cc | (DTR ^ DT_PRE) | DT_LOAD | DT_UP | RN(ARMRegisters::sp) | RD(reg) | 0x4);
         }
 
         inline void poke_r(int reg, Condition cc = AL)
         {
             dtr_d(false, ARMRegisters::sp, 0, reg, cc);
         }
@@ -840,44 +809,39 @@ namespace JSC {
             spewInsWithOp2("clz", cc, rd, static_cast<ARMWord>(rm));
             m_buffer.putInt(static_cast<ARMWord>(cc) | CLZ | RD(rd) | RM(rm));
         }
 #endif
 
         void bkpt(ARMWord value)
         {
 #if WTF_ARM_ARCH_VERSION >= 5
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s #0x%04x\n", MAYBE_PAD, "bkpt", value);
+            spew("%-15s #0x%04x", "bkpt", value);
             m_buffer.putInt(BKPT | ((value & 0xfff0) << 4) | (value & 0xf));
 #else
             // Cannot access to Zero memory address
             dtr_dr(true, ARMRegisters::S0, ARMRegisters::S0, ARMRegisters::S0);
 #endif
         }
 
         void bx(int rm, Condition cc = AL)
         {
 #if WTF_ARM_ARCH_VERSION >= 5 || defined(__ARM_ARCH_4T__)
-            js::JaegerSpew(
-                    js::JSpew_Insns,
-                    IPFX    "bx%-13s %s\n", MAYBE_PAD, nameCC(cc), nameGpReg(rm));
+            spew("bx%-13s %s", nameCC(cc), nameGpReg(rm));
             emitInst(static_cast<ARMWord>(cc) | BX, 0, 0, RM(rm));
 #else
             mov_r(ARMRegisters::pc, RM(rm), cc);
 #endif
         }
 
         JmpSrc blx(int rm, Condition cc = AL)
         {
 #if WTF_CPU_ARM && WTF_ARM_ARCH_VERSION >= 5
             int s = m_buffer.uncheckedSize();
-            js::JaegerSpew(
-                    js::JSpew_Insns,
-                    IPFX    "blx%-12s %s\n", MAYBE_PAD, nameCC(cc), nameGpReg(rm));
+            spew("blx%-12s %s", nameCC(cc), nameGpReg(rm));
             emitInst(static_cast<ARMWord>(cc) | BLX, 0, 0, RM(rm));
 #else
             ASSERT(rm != 14);
             ensureSpace(2 * sizeof(ARMWord), 0);
             mov_r(ARMRegisters::lr, ARMRegisters::pc, cc);
             int s = m_buffer.uncheckedSize();
             bx(rm, cc);
 #endif
@@ -956,17 +920,17 @@ namespace JSC {
         int flushCount()
         {
             return m_buffer.flushCount();
         }
 
         JmpDst label()
         {
             JmpDst label(m_buffer.size());
-            js::JaegerSpew(js::JSpew_Insns, IPFX "#label     ((%d))\n", MAYBE_PAD, label.m_offset);
+            spew("#label     ((%d))", label.m_offset);
             return label;
         }
 
         JmpDst align(int alignment)
         {
             while (!m_buffer.isAligned(alignment))
                 mov_r(ARMRegisters::r0, ARMRegisters::r0);
 
@@ -1041,37 +1005,34 @@ namespace JSC {
         }
 
         static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr);
 
         // Patch pointers
 
         static void linkPointer(void* code, JmpDst from, void* to)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                           "##linkPointer     ((%p + %#x)) points to ((%p))\n",
-                           code, from.m_offset, to);
+            staticSpew("##linkPointer     ((%p + %#x)) points to ((%p))",
+                       code, from.m_offset, to);
 
             patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to);
         }
 
         static void repatchInt32(void* from, int32_t to)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                           ISPFX "##repatchInt32    ((%p)) holds ((%#x))\n",
-                           from, to);
+            staticSpew("##repatchInt32    ((%p)) holds ((%#x))",
+                       from, to);
 
             patchPointerInternal(reinterpret_cast<intptr_t>(from), reinterpret_cast<void*>(to));
         }
 
         static void repatchPointer(void* from, void* to)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                           "##repatchPointer  ((%p)) points to ((%p))\n",
-                           from, to);
+            staticSpew("##repatchPointer  ((%p)) points to ((%p))",
+                       from, to);
 
             patchPointerInternal(reinterpret_cast<intptr_t>(from), to);
         }
 
         static void repatchLoadPtrToLEA(void* from)
         {
             // On arm, this is a patch from LDR to ADD. It is restricted conversion,
             // from special case to special case, altough enough for its purpose
@@ -1098,60 +1059,55 @@ namespace JSC {
         // Linkers
 
         void linkJump(JmpSrc from, JmpDst to)
         {
             ARMWord  code = reinterpret_cast<ARMWord>(m_buffer.data());
             ARMWord* insn = reinterpret_cast<ARMWord*>(code + from.m_offset);
             ARMWord* addr = getLdrImmAddressOnPool(insn, m_buffer.poolAddress());
 
-            js::JaegerSpew(js::JSpew_Insns,
-                           IPFX "##linkJump         ((%#x)) jumps to ((%#x))\n", MAYBE_PAD,
-                           from.m_offset, to.m_offset);
+            spew("##linkJump         ((%#x)) jumps to ((%#x))",
+                 from.m_offset, to.m_offset);
 
             *addr = to.m_offset;
         }
 
         static void linkJump(void* code, JmpSrc from, void* to)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                           ISPFX "##linkJump        ((%p + %#x)) jumps to ((%p))\n",
-                           code, from.m_offset, to);
+            staticSpew("##linkJump        ((%p + %#x)) jumps to ((%p))",
+                       code, from.m_offset, to);
 
             patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to);
         }
 
         static void relinkJump(void* from, void* to)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                           ISPFX "##relinkJump      ((%p)) jumps to ((%p))\n",
-                           from, to);
+            staticSpew("##relinkJump      ((%p)) jumps to ((%p))",
+                       from, to);
 
             patchPointerInternal(reinterpret_cast<intptr_t>(from), to);
         }
 
         static bool canRelinkJump(void* from, void* to)
         {
             return true;
         }
 
         static void linkCall(void* code, JmpSrc from, void* to)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                           ISPFX "##linkCall        ((%p + %#x)) jumps to ((%p))\n",
-                           code, from.m_offset, to);
+            staticSpew("##linkCall        ((%p + %#x)) jumps to ((%p))",
+                       code, from.m_offset, to);
 
             patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to);
         }
 
         static void relinkCall(void* from, void* to)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                           ISPFX "##relinkCall      ((%p)) jumps to ((%p))\n",
-                           from, to);
+            staticSpew("##relinkCall      ((%p)) jumps to ((%p))",
+                       from, to);
 
             patchPointerInternal(reinterpret_cast<intptr_t>(from), to);
         }
 
         // Address operations
 
         static void* getRelocatedAddress(void* code, JmpSrc jump)
         {
@@ -1380,30 +1336,28 @@ namespace JSC {
         void spewInsWithOp2(char const * ins, Condition cc, int rd, int rn, ARMWord op2)
         {
             char    mnemonic[16];
             snprintf(mnemonic, 16, "%s%s", ins, nameCC(cc));
 
             char    op2_fmt[48];
             fmtOp2(op2_fmt, op2);
 
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rn), op2_fmt);
+            spew("%-15s %s, %s, %s", mnemonic, nameGpReg(rd), nameGpReg(rn), op2_fmt);
         }
 
         void spewInsWithOp2(char const * ins, Condition cc, int r, ARMWord op2)
         {
             char    mnemonic[16];
             snprintf(mnemonic, 16, "%s%s", ins, nameCC(cc));
 
             char    op2_fmt[48];
             fmtOp2(op2_fmt, op2);
 
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s\n", MAYBE_PAD, mnemonic, nameGpReg(r), op2_fmt);
+            spew("%-15s %s, %s", mnemonic, nameGpReg(r), op2_fmt);
         }
 
         ARMWord RM(int reg)
         {
             ASSERT(reg <= ARMRegisters::pc);
             return reg;
         }
 
@@ -1551,40 +1505,38 @@ namespace JSC {
         {
             m_buffer.putInt(op | rn | rd | op2);
         }
 
         // NOTE: offset is the actual value that is going to be encoded.  It is the offset in words, NOT in bytes.
         void fmem_imm_off(bool isLoad, bool isDouble, bool isUp, int dest, int rn, ARMWord offset, Condition cc = AL)
         {
             char const * ins = isLoad ? "vldr.f" : "vstr.f";
-            js::JaegerSpew(js::JSpew_Insns,
-                           IPFX   "%s%d %s, [%s, #%s%u]\n", MAYBE_PAD, 
-                           ins, (isDouble ? 64 : 32), (isDouble ? nameFpRegD(dest) : nameFpRegS(dest)),
-                           nameGpReg(rn), (isUp ? "+" : "-"), offset);
+            spew("%s%d %s, [%s, #%s%u]", 
+                 ins, (isDouble ? 64 : 32), (isDouble ? nameFpRegD(dest) : nameFpRegS(dest)),
+                 nameGpReg(rn), (isUp ? "+" : "-"), offset);
             ASSERT(offset <= 0xff);
             emitVFPInst(static_cast<ARMWord>(cc) | 
                         VFP_EXT | VFP_DTR | 
                         (isDouble ? VFP_DBL : 0) |
                         (isUp ? DT_UP : 0) | 
                         (isLoad ? DT_LOAD : 0), isDouble ? DD(dest) : SD(dest), RN(rn), offset);
             
         }
 
         // WARNING: even for an int -> float conversion, all registers used
         // are VFP registers.
         void vcvt(RegType srcType, RegType dstType, int src, int dest, Condition cc = AL)
         {
             ASSERT(srcType != dstType);
             ASSERT(isFloatType(srcType) || isFloatType(dstType));
 
-            js::JaegerSpew(js::JSpew_Insns,
-                           IPFX   "vcvt.%s.%-15s, %s,%s\n", MAYBE_PAD, 
-                           nameType(dstType), nameType(srcType),
-                           nameTypedReg(dstType,dest), nameTypedReg(srcType,src));
+            spew("vcvt.%s.%-15s, %s,%s", 
+                 nameType(dstType), nameType(srcType),
+                 nameTypedReg(dstType,dest), nameTypedReg(srcType,src));
             
             if (isFloatType(srcType) && isFloatType (dstType)) {
                 // doing a float -> float conversion
                 bool dblToFloat = srcType == FloatReg64;
                 emitVFPInst(static_cast<ARMWord>(cc) | VFP_DATA | VFP_FPCVT |
                             (dblToFloat ? VFP_DBL : 0),
                             dblToFloat ? SD(dest) : DD(dest),
                             dblToFloat ? DM(src) : SM(src), 0);
@@ -1592,102 +1544,91 @@ namespace JSC {
                 JS_NOT_REACHED("Other conversions did not seem useful on 2011/08/04");
             }
         }
 
         // does r2:r1 -> dn, dn -> r2:r1, r2:r1 -> s2:s1, etc.
         void vmov64 (bool fromFP, bool isDbl, int r1, int r2, int rFP, Condition cc = AL)
         {
             if (fromFP) {
-                js::JaegerSpew(js::JSpew_Insns,
-                               IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vmov", 
-                               nameGpReg(r1), nameGpReg(r2), nameFpRegD(rFP));
+                spew("%-15s %s, %s, %s", "vmov", 
+                     nameGpReg(r1), nameGpReg(r2), nameFpRegD(rFP));
             } else {
-                js::JaegerSpew(js::JSpew_Insns,
-                               IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vmov",
-                               nameFpRegD(rFP), nameGpReg(r1), nameGpReg(r2));
+                spew("%-15s %s, %s, %s", "vmov",
+                     nameFpRegD(rFP), nameGpReg(r1), nameGpReg(r2));
             }
             emitVFPInst(static_cast<ARMWord>(cc) | VFP_DXFER | VFP_MOV |
                         (fromFP ? DT_LOAD : 0) |
                         (isDbl ? VFP_DBL : 0), RD(r1), RN(r2), isDbl ? DM(rFP) : SM(rFP));
         }
 
         void fcpyd_r(int dd, int dm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                           IPFX   "%-15s %s, %s\n", MAYBE_PAD, "vmov.f64",
-                           nameFpRegD(dd), nameFpRegD(dm));
+            spew("%-15s %s, %s", "vmov.f64",
+                 nameFpRegD(dd), nameFpRegD(dm));
             // TODO: emitInst doesn't work for VFP instructions, though it
             // seems to work for current usage.
             emitVFPInst(static_cast<ARMWord>(cc) | FCPYD, DD(dd), DM(dm), 0);
         }
 
         void faddd_r(int dd, int dn, int dm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vadd.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
+            spew("%-15s %s, %s, %s", "vadd.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
             // TODO: emitInst doesn't work for VFP instructions, though it
             // seems to work for current usage.
             emitVFPInst(static_cast<ARMWord>(cc) | FADDD, DD(dd), DN(dn), DM(dm));
         }
 
         void fnegd_r(int dd, int dm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s\n", MAYBE_PAD, "fnegd", nameFpRegD(dd), nameFpRegD(dm));
+            spew("%-15s %s, %s", "fnegd", nameFpRegD(dd), nameFpRegD(dm));
             m_buffer.putInt(static_cast<ARMWord>(cc) | FNEGD | DD(dd) | DM(dm));
         }
 
         void fdivd_r(int dd, int dn, int dm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vdiv.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
+            spew("%-15s %s, %s, %s", "vdiv.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
             // TODO: emitInst doesn't work for VFP instructions, though it
             // seems to work for current usage.
             emitVFPInst(static_cast<ARMWord>(cc) | FDIVD, DD(dd), DN(dn), DM(dm));
         }
 
         void fsubd_r(int dd, int dn, int dm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vsub.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
+            spew("%-15s %s, %s, %s", "vsub.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
             // TODO: emitInst doesn't work for VFP instructions, though it
             // seems to work for current usage.
             emitVFPInst(static_cast<ARMWord>(cc) | FSUBD, DD(dd), DN(dn), DM(dm));
         }
 
         void fabsd_r(int dd, int dm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s\n", MAYBE_PAD, "fabsd", nameFpRegD(dd), nameFpRegD(dm));
+            spew("%-15s %s, %s", "fabsd", nameFpRegD(dd), nameFpRegD(dm));
             m_buffer.putInt(static_cast<ARMWord>(cc) | FABSD | DD(dd) | DM(dm));
         }
 
         void fmuld_r(int dd, int dn, int dm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vmul.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
+            spew("%-15s %s, %s, %s", "vmul.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
             // TODO: emitInst doesn't work for VFP instructions, though it
             // seems to work for current usage.
             emitVFPInst(static_cast<ARMWord>(cc) | FMULD, DD(dd), DN(dn), DM(dm));
         }
 
         void fcmpd_r(int dd, int dm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s\n", MAYBE_PAD, "vcmp.f64", nameFpRegD(dd), nameFpRegD(dm));
+            spew("%-15s %s, %s", "vcmp.f64", nameFpRegD(dd), nameFpRegD(dm));
             // TODO: emitInst doesn't work for VFP instructions, though it
             // seems to work for current usage.
             emitVFPInst(static_cast<ARMWord>(cc) | FCMPD, DD(dd), 0, DM(dm));
         }
 
         void fsqrtd_r(int dd, int dm, Condition cc = AL)
         {
-            js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s\n", MAYBE_PAD, "vsqrt.f64", nameFpRegD(dd), nameFpRegD(dm));
+            spew("%-15s %s, %s", "vsqrt.f64", nameFpRegD(dd), nameFpRegD(dm));
             // TODO: emitInst doesn't work for VFP instructions, though it
             // seems to work for current usage.
             emitVFPInst(static_cast<ARMWord>(cc) | FSQRTD, DD(dd), 0, DM(dm));
         }
 
         void fmsr_r(int dd, int rn, Condition cc = AL)
         {
             // TODO: emitInst doesn't work for VFP instructions, though it
--- a/js/src/assembler/assembler/AssemblerBuffer.h
+++ b/js/src/assembler/assembler/AssemblerBuffer.h
@@ -34,16 +34,30 @@
 
 #if ENABLE_ASSEMBLER
 
 #include <string.h>
 #include <limits.h>
 #include "assembler/jit/ExecutableAllocator.h"
 #include "assembler/wtf/Assertions.h"
 
+#include <stdarg.h>
+#include "jsopcode.h"
+
+#include "methodjit/Logging.h"
+#include "ion/IonSpewer.h"
+
+#define PRETTY_PRINT_OFFSET(os) (((os)<0)?"-":""), (((os)<0)?-(os):(os))
+
+#define FIXME_INSN_PRINTING                                 \
+    do {                                                    \
+        spew("FIXME insn printing %s:%d",                   \
+             __FILE__, __LINE__);                           \
+    } while (0)
+
 namespace JSC {
 
     class AssemblerBuffer {
         static const int inlineCapacity = 256;
     public:
         AssemblerBuffer()
             : m_buffer(m_inlineBuffer)
             , m_capacity(inlineCapacity)
@@ -229,13 +243,98 @@ namespace JSC {
 
         char m_inlineBuffer[inlineCapacity];
         char* m_buffer;
         int m_capacity;
         int m_size;
         bool m_oom;
     };
 
+    class GenericAssembler
+    {
+        js::Sprinter *printer;
+
+      public:
+
+        bool isOOLPath;
+
+        GenericAssembler()
+          : printer(NULL)
+          , isOOLPath(false)
+        {}
+
+        void setPrinter(js::Sprinter *sp) {
+            printer = sp;
+        }
+
+        void spew(const char *fmt, ...)
+#ifdef __GNUC__
+            __attribute__ ((format (printf, 2, 3)))
+#endif
+        {
+            if (printer ||
+                js::IsJaegerSpewChannelActive(js::JSpew_Insns)
+#ifdef JS_ION
+                || js::ion::IonSpewEnabled(js::ion::IonSpew_Codegen)
+#endif
+                )
+            {
+                // Buffer to hold the formatted string. Note that this may contain
+                // '%' characters, so do not pass it directly to printf functions.
+                char buf[200];
+
+                va_list va;
+                va_start(va, fmt);
+                int i = vsnprintf(buf, sizeof(buf), fmt, va);
+                va_end(va);
+
+                if (i > -1) {
+                    if (printer)
+                        printer->printf("%s\n", buf);
+
+                    // The assembler doesn't know which compiler it is for, so if
+                    // both JM and Ion spew are on, just print via one channel
+                    // (Use JM to pick up isOOLPath).
+                    if (js::IsJaegerSpewChannelActive(js::JSpew_Insns))
+                        js::JaegerSpew(js::JSpew_Insns, "%s       %s\n", isOOLPath ? ">" : " ", buf);
+#ifdef JS_ION
+                    else
+                        js::ion::IonSpew(js::ion::IonSpew_Codegen, "%s", buf);
+#endif
+                }
+            }
+        }
+
+        static void staticSpew(const char *fmt, ...)
+#ifdef __GNUC__
+            __attribute__ ((format (printf, 1, 2)))
+#endif
+        {
+            if (js::IsJaegerSpewChannelActive(js::JSpew_Insns)
+#ifdef JS_ION
+                || js::ion::IonSpewEnabled(js::ion::IonSpew_Codegen)
+#endif
+                )
+            {
+                char buf[200];
+
+                va_list va;
+                va_start(va, fmt);
+                int i = vsnprintf(buf, sizeof(buf), fmt, va);
+                va_end(va);
+
+                if (i > -1) {
+                    if (js::IsJaegerSpewChannelActive(js::JSpew_Insns))
+                        js::JaegerSpew(js::JSpew_Insns, "        %s\n", buf);
+#ifdef JS_ION
+                    else
+                        js::ion::IonSpew(js::ion::IonSpew_Codegen, "%s", buf);
+#endif
+                }
+            }
+        }
+    };
+
 } // namespace JSC
 
 #endif // ENABLE(ASSEMBLER)
 
 #endif // AssemblerBuffer_h
--- a/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h
+++ b/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h
@@ -328,18 +328,18 @@ private:
         ASSERT(m_maxDistance >= 0);
         m_lastConstDelta = constSize;
     }
 
     // place a constant pool after the last instruction placed, and
     // optionally place a jump to ensure we don't start executing the pool.
     void flushConstantPool(bool useBarrier = true)
     {
-        js::JaegerSpew(js::JSpew_Insns, " -- FLUSHING CONSTANT POOL WITH %d CONSTANTS --\n",
-                       m_numConsts);
+        GenericAssembler::staticSpew(" -- FLUSHING CONSTANT POOL WITH %d CONSTANTS --\n",
+                                     m_numConsts);
         if (m_numConsts == 0)
             return;
         m_flushCount++;
         int alignPool = (AssemblerBuffer::size() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1);
 
         if (alignPool)
             alignPool = sizeof(uint64_t) - alignPool;
 
--- a/js/src/assembler/assembler/MIPSAssembler.h
+++ b/js/src/assembler/assembler/MIPSAssembler.h
@@ -35,27 +35,18 @@
 #include "assembler/wtf/Assertions.h"
 #include "assembler/wtf/SegmentedVector.h"
 
 #include "methodjit/Logging.h"
 #define IPFX  "        %s"
 #define ISPFX "        "
 #ifdef JS_METHODJIT_SPEW
 # define MAYBE_PAD (isOOLPath ? ">  " : "")
-# define PRETTY_PRINT_OFFSET(os) (((os)<0)?"-":""), (((os)<0)?-(os):(os))
-# define FIXME_INSN_PRINTING                                \
-    do {                                                    \
-        js::JaegerSpew(js::JSpew_Insns,                     \
-                       ISPFX "FIXME insn printing %s:%d\n", \
-                       __FILE__, __LINE__);                 \
-    } while (0)
 #else
 # define MAYBE_PAD ""
-# define FIXME_INSN_PRINTING ((void) 0)
-# define PRETTY_PRINT_OFFSET(os) "", 0
 #endif
 
 namespace JSC {
 
 typedef uint32_t MIPSWord;
 
 namespace MIPSRegisters {
 typedef enum {
@@ -157,31 +148,24 @@ typedef enum {
     f28,
     f29,
     f30,
     f31
 } FPRegisterID;
 
 } // namespace MIPSRegisters
 
-class MIPSAssembler {
+class MIPSAssembler : public GenericAssembler {
 public:
     typedef MIPSRegisters::RegisterID RegisterID;
     typedef MIPSRegisters::FPRegisterID FPRegisterID;
     typedef SegmentedVector<int, 64> Jumps;
     unsigned char *buffer() const { return m_buffer.buffer(); }
     bool oom() const { return m_buffer.oom(); }
 
-#ifdef JS_METHODJIT_SPEW
-    bool isOOLPath;
-    MIPSAssembler() : isOOLPath(false) { }
-#else
-    MIPSAssembler() { }
-#endif
-
     // MIPS instruction opcode field position
     enum {
         OP_SH_RD = 11,
         OP_SH_RT = 16,
         OP_SH_RS = 21,
         OP_SH_SHAMT = 6,
         OP_SH_CODE = 16,
         OP_SH_FD = 6,
--- a/js/src/assembler/assembler/SparcAssembler.h
+++ b/js/src/assembler/assembler/SparcAssembler.h
@@ -17,27 +17,18 @@
 #include "AssemblerBufferWithConstantPool.h"
 #include <assembler/wtf/Assertions.h>
 
 #include "methodjit/Logging.h"
 #define IPFX  "        %s"
 #define ISPFX "        "
 #ifdef JS_METHODJIT_SPEW
 # define MAYBE_PAD (isOOLPath ? ">  " : "")
-# define PRETTY_PRINT_OFFSET(os) (((os)<0)?"-":""), (((os)<0)?-(os):(os))
-# define FIXME_INSN_PRINTING                                \
-    do {                                                    \
-        js::JaegerSpew(js::JSpew_Insns,                     \
-                       ISPFX "FIXME insn printing %s:%d\n", \
-                       __FILE__, __LINE__);                 \
-    } while (0)
 #else
 # define MAYBE_PAD ""
-# define FIXME_INSN_PRINTING ((void) 0)
-# define PRETTY_PRINT_OFFSET(os) "", 0
 #endif
 
 namespace JSC {
 
     typedef uint32_t SparcWord;
 
     namespace SparcRegisters {
         typedef enum {
@@ -113,30 +104,23 @@ namespace JSC {
             f28 = 28,
             f29 = 29,
             f30 = 30,
             f31 = 31
         } FPRegisterID;
 
     } // namespace SparcRegisters
 
-    class SparcAssembler {
+    class SparcAssembler : public GenericAssembler {
     public:
         typedef SparcRegisters::RegisterID RegisterID;
         typedef SparcRegisters::FPRegisterID FPRegisterID;
         AssemblerBuffer m_buffer;
         bool oom() const { return m_buffer.oom(); }
 
-#ifdef JS_METHODJIT_SPEW
-        bool isOOLPath;
-        SparcAssembler() : isOOLPath(false) { }
-#else
-        SparcAssembler() { }
-#endif
-
         // Sparc conditional constants
         typedef enum {
             ConditionE   = 0x1, // Zero
             ConditionLE  = 0x2,
             ConditionL   = 0x3,
             ConditionLEU = 0x4,
             ConditionCS  = 0x5,
             ConditionNEG = 0x6,
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -25,43 +25,26 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  * 
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef X86Assembler_h
 #define X86Assembler_h
 
+#include <stdarg.h>
+
 #include "assembler/wtf/Platform.h"
 
 #if ENABLE_ASSEMBLER && (WTF_CPU_X86 || WTF_CPU_X86_64)
 
 #include "AssemblerBuffer.h"
 #include "assembler/wtf/Assertions.h"
 #include "js/Vector.h"
 
-#include "methodjit/Logging.h"
-#define IPFX  "        %s"
-#define ISPFX "        "
-#ifdef JS_METHODJIT_SPEW
-# define MAYBE_PAD (isOOLPath ? ">  " : "")
-# define PRETTY_PRINT_OFFSET(os) (((os)<0)?"-":""), (((os)<0)?-(os):(os))
-# define FIXME_INSN_PRINTING                                \
-    do {                                                    \
-        js::JaegerSpew(js::JSpew_Insns,                     \
-                       ISPFX "FIXME insn printing %s:%d\n", \
-                       __FILE__, __LINE__);                 \
-    } while (0)
-#else
-# define MAYBE_PAD ""
-# define FIXME_INSN_PRINTING ((void) 0)
-# define PRETTY_PRINT_OFFSET(os) "", 0
-#endif
-
-
 namespace JSC {
 
 inline bool CAN_SIGN_EXTEND_8_32(int32_t value) { return value == (int32_t)(signed char)value; }
 
 namespace X86Registers {
     typedef enum {
         eax,
         ecx,
@@ -149,17 +132,17 @@ namespace X86Registers {
 #       else
         return nameIReg(4, reg);
 #       endif
     }
 
 } /* namespace X86Registers */
 
 
-class X86Assembler {
+class X86Assembler : public GenericAssembler {
 public:
     typedef X86Registers::RegisterID RegisterID;
     typedef X86Registers::XMMRegisterID XMMRegisterID;
     typedef XMMRegisterID FPRegisterID;
 
     typedef enum {
         ConditionO,
         ConditionNO,
@@ -351,20 +334,16 @@ private:
         FPU6_OP_FSTP = 3,
 
         GROUP11_MOV = 0
     } GroupOpcodeID;
     
     class X86InstructionFormatter;
 public:
 
-#ifdef JS_METHODJIT_SPEW
-    bool isOOLPath;
-#endif
-
     class JmpSrc {
         friend class X86Assembler;
         friend class X86InstructionFormatter;
     public:
         JmpSrc()
             : m_offset(-1)
         {
         }
@@ -409,64 +388,52 @@ public:
         int offset() const {
             return m_offset;
         }
     private:
         signed int m_offset : 31;
         bool m_used : 1;
     };
 
-    X86Assembler()
-#ifdef JS_METHODJIT_SPEW
-      : isOOLPath(false)
-#endif
-    {
-    }
-
     size_t size() const { return m_formatter.size(); }
     unsigned char *buffer() const { return m_formatter.buffer(); }
     bool oom() const { return m_formatter.oom(); }
 
     void nop()
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "nop\n", MAYBE_PAD);
+        spew("nop");
         m_formatter.oneByteOp(OP_NOP);
     }
 
     // Stack operations:
 
     void push_r(RegisterID reg)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "push       %s\n", MAYBE_PAD, nameIReg(reg));
+        spew("push       %s", nameIReg(reg));
         m_formatter.oneByteOp(OP_PUSH_EAX, reg);
     }
 
     void pop_r(RegisterID reg)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "pop        %s\n", MAYBE_PAD, nameIReg(reg));
+        spew("pop        %s", nameIReg(reg));
         m_formatter.oneByteOp(OP_POP_EAX, reg);
     }
 
     void push_i32(int imm)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "pushl      %s$0x%x\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(imm));
+        spew("pushl      %s$0x%x",
+             PRETTY_PRINT_OFFSET(imm));
         m_formatter.oneByteOp(OP_PUSH_Iz);
         m_formatter.immediate32(imm);
     }
 
     void push_m(int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "push       %s0x%x(%s)\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("push       %s0x%x(%s)",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_PUSH, base, offset);
     }
 
     void pop_m(int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_GROUP1A_Ev, GROUP1A_OP_POP, base, offset);
     }
@@ -484,54 +451,50 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADC, addr);
             m_formatter.immediate32(imm);
         }
     }
 #endif
 
     void addl_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "addl       %s, %s\n", MAYBE_PAD,
-                       nameIReg(4,src), nameIReg(4,dst));
+        spew("addl       %s, %s",
+             nameIReg(4,src), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_ADD_EvGv, src, dst);
     }
 
     void addl_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "addl       %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(4,base), nameIReg(4,dst));
+        spew("addl       %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(4,base), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_ADD_GvEv, dst, base, offset);
     }
 
     void addl_rm(RegisterID src, int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_ADD_EvGv, src, base, offset);
     }
 
     void addl_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "addl       $0x%x, %s\n", MAYBE_PAD, imm, nameIReg(4,dst));
+        spew("addl       $0x%x, %s", imm, nameIReg(4,dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst);
             m_formatter.immediate32(imm);
         }
     }
 
     void addl_im(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "addl       %d, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(8,base));
+        spew("addl       %d, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(8,base));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset);
             m_formatter.immediate32(imm);
         }
     }
@@ -540,32 +503,30 @@ public:
     void addq_rr(RegisterID src, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp64(OP_ADD_EvGv, src, dst);
     }
 
     void addq_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "addq       $0x%x, %s\n", MAYBE_PAD, imm, nameIReg(8,dst));
+        spew("addq       $0x%x, %s", imm, nameIReg(8,dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst);
             m_formatter.immediate32(imm);
         }
     }
 
     void addq_im(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "addq       $0x%x, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(8,base));
+        spew("addq       $0x%x, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(8,base));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset);
             m_formatter.immediate32(imm);
         }
     }
@@ -580,40 +541,37 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, addr);
             m_formatter.immediate32(imm);
         }
     }
 #endif
 
     void andl_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "andl       %s, %s\n", MAYBE_PAD,
-                       nameIReg(4,src), nameIReg(4,dst));
+        spew("andl       %s, %s",
+             nameIReg(4,src), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_AND_EvGv, src, dst);
     }
 
     void andl_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "andl       %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(4,base), nameIReg(4,dst));
+        spew("andl       %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(4,base), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_AND_GvEv, dst, base, offset);
     }
 
     void andl_rm(RegisterID src, int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_AND_EvGv, src, base, offset);
     }
 
     void andl_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "andl       $0x%x, %s\n", MAYBE_PAD, imm, nameIReg(4,dst));
+        spew("andl       $0x%x, %s", imm, nameIReg(4,dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, dst);
             m_formatter.immediate32(imm);
         }
     }
@@ -628,42 +586,38 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, base, offset);
             m_formatter.immediate32(imm);
         }
     }
 
 #if WTF_CPU_X86_64
     void andq_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "andq       %s, %s\n", MAYBE_PAD,
-                       nameIReg(8,src), nameIReg(8,dst));
+        spew("andq       %s, %s",
+             nameIReg(8,src), nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_AND_EvGv, src, dst);
     }
 
     void andq_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "andq       %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(8,base), nameIReg(8,dst));
+        spew("andq       %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(8,base), nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_AND_GvEv, dst, base, offset);
     }
 
     void orq_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "orq        %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(8,base), nameIReg(8,dst));
+        spew("orq        %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(8,base), nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_OR_GvEv, dst, base, offset);
     }
 
     void andq_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "andq       $0x%x, %s\n", MAYBE_PAD, imm, nameIReg(8,dst));
+        spew("andq       $0x%x, %s", imm, nameIReg(8,dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_AND, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_AND, dst);
             m_formatter.immediate32(imm);
         }
     }
@@ -683,45 +637,42 @@ public:
 
     void fstp_m(int offset, RegisterID base)
     {
 	   m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FSTP, base, offset);
     }
 
     void negl_r(RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "negl       %s\n", MAYBE_PAD, nameIReg(4,dst));
+        spew("negl       %s", nameIReg(4,dst));
         m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, dst);
     }
 
     void negl_m(int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, base, offset);
     }
 
     void notl_r(RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "notl       %s\n", MAYBE_PAD, nameIReg(4,dst));
+        spew("notl       %s", nameIReg(4,dst));
         m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, dst);
     }
 
     void notl_m(int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, base, offset);
     }
 
     void orl_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "orl        %s, %s\n", MAYBE_PAD,
-                       nameIReg(4,src), nameIReg(4,dst));
+        spew("orl        %s, %s",
+             nameIReg(4,src), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_OR_EvGv, src, dst);
     }
 
     void orl_mr(int offset, RegisterID base, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_OR_GvEv, dst, base, offset);
     }
@@ -729,18 +680,17 @@ public:
     void orl_rm(RegisterID src, int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_OR_EvGv, src, base, offset);
     }
 
     void orl_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "orl        $0x%x, %s\n", MAYBE_PAD, imm, nameIReg(4,dst));
+        spew("orl        $0x%x, %s", imm, nameIReg(4,dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, dst);
             m_formatter.immediate32(imm);
         }
     }
@@ -755,46 +705,42 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, base, offset);
             m_formatter.immediate32(imm);
         }
     }
 
 #if WTF_CPU_X86_64
     void negq_r(RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "negq       %s\n", MAYBE_PAD, nameIReg(8,dst));
+        spew("negq       %s", nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_GROUP3_Ev, GROUP3_OP_NEG, dst);
     }
 
     void orq_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "orq        %s, %s\n", MAYBE_PAD,
-                       nameIReg(8,src), nameIReg(8,dst));
+        spew("orq        %s, %s",
+             nameIReg(8,src), nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_OR_EvGv, src, dst);
     }
 
     void orq_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "orq        $0x%x, %s\n", MAYBE_PAD, imm, nameIReg(8,dst));
+        spew("orq        $0x%x, %s", imm, nameIReg(8,dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_OR, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_OR, dst);
             m_formatter.immediate32(imm);
         }
     }
 
     void notq_r(RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "notq       %s\n", MAYBE_PAD, nameIReg(8,dst));
+        spew("notq       %s", nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_GROUP3_Ev, GROUP3_OP_NOT, dst);
     }
 #else
     void orl_im(int imm, void* addr)
     {
         FIXME_INSN_PRINTING;
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, addr);
@@ -803,54 +749,50 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, addr);
             m_formatter.immediate32(imm);
         }
     }
 #endif
 
     void subl_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "subl       %s, %s\n", MAYBE_PAD,
-                       nameIReg(4,src), nameIReg(4,dst));
+        spew("subl       %s, %s",
+             nameIReg(4,src), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_SUB_EvGv, src, dst);
     }
 
     void subl_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "subl        %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(4,base), nameIReg(4,dst));
+        spew("subl        %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(4,base), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_SUB_GvEv, dst, base, offset);
     }
 
     void subl_rm(RegisterID src, int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_SUB_EvGv, src, base, offset);
     }
 
     void subl_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "subl       $0x%x, %s\n", MAYBE_PAD, imm, nameIReg(4, dst));
+        spew("subl       $0x%x, %s", imm, nameIReg(4, dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst);
             m_formatter.immediate32(imm);
         }
     }
     
     void subl_im(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "subl       $0x%x, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(4, base));
+        spew("subl       $0x%x, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(4, base));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, base, offset);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, base, offset);
             m_formatter.immediate32(imm);
         }
     }
@@ -859,18 +801,17 @@ public:
     void subq_rr(RegisterID src, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp64(OP_SUB_EvGv, src, dst);
     }
 
     void subq_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "subq       $0x%x, %s\n", MAYBE_PAD, imm, nameIReg(8,dst));
+        spew("subq       $0x%x, %s", imm, nameIReg(8,dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst);
             m_formatter.immediate32(imm);
         }
     }
@@ -885,19 +826,18 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, addr);
             m_formatter.immediate32(imm);
         }
     }
 #endif
 
     void xorl_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "xorl       %s, %s\n", MAYBE_PAD,
-                       nameIReg(4,src), nameIReg(4,dst));
+        spew("xorl       %s, %s",
+             nameIReg(4,src), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_XOR_EvGv, src, dst);
     }
 
     void xorl_mr(int offset, RegisterID base, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_XOR_GvEv, dst, base, offset);
     }
@@ -917,235 +857,215 @@ public:
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, base, offset);
             m_formatter.immediate32(imm);
         }
     }
 
     void xorl_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "xorl       %d, %s\n", MAYBE_PAD,
-                       imm, nameIReg(4,dst));
+        spew("xorl       %d, %s",
+             imm, nameIReg(4,dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst);
             m_formatter.immediate32(imm);
         }
     }
 
 #if WTF_CPU_X86_64
     void xorq_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "xorq       %s, %s\n", MAYBE_PAD,
-                       nameIReg(8,src), nameIReg(8, dst));
+        spew("xorq       %s, %s",
+             nameIReg(8,src), nameIReg(8, dst));
         m_formatter.oneByteOp64(OP_XOR_EvGv, src, dst);
     }
 
     void xorq_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "xorq       %d, %s\n", MAYBE_PAD,
-                       imm, nameIReg(8,dst));
+        spew("xorq       %d, %s",
+             imm, nameIReg(8,dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst);
             m_formatter.immediate32(imm);
         }
     }
 #endif
 
     void sarl_i8r(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "sarl       $%d, %s\n", MAYBE_PAD, imm, nameIReg(4, dst));
+        spew("sarl       $%d, %s", imm, nameIReg(4, dst));
         if (imm == 1)
             m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst);
         else {
             m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst);
             m_formatter.immediate8(imm);
         }
     }
 
     void sarl_CLr(RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "sarl       %%cl, %s\n", MAYBE_PAD, nameIReg(4, dst));
+        spew("sarl       %%cl, %s", nameIReg(4, dst));
         m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst);
     }
     
     void shrl_i8r(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "shrl       $%d, %s\n", MAYBE_PAD, imm, nameIReg(4, dst));
+        spew("shrl       $%d, %s", imm, nameIReg(4, dst));
         if (imm == 1)
             m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHR, dst);
         else {
             m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHR, dst);
             m_formatter.immediate8(imm);
         }
     }
     
     void shrl_CLr(RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "shrl       %%cl, %s\n", MAYBE_PAD, nameIReg(4, dst));
+        spew("shrl       %%cl, %s", nameIReg(4, dst));
         m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst);
     }
 
     void shll_i8r(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "shll       $%d, %s\n", MAYBE_PAD, imm, nameIReg(4, dst));
+        spew("shll       $%d, %s", imm, nameIReg(4, dst));
         if (imm == 1)
             m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHL, dst);
         else {
             m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHL, dst);
             m_formatter.immediate8(imm);
         }
     }
 
     void shll_CLr(RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "shll       %%cl, %s\n", MAYBE_PAD, nameIReg(4, dst));
+        spew("shll       %%cl, %s", nameIReg(4, dst));
         m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHL, dst);
     }
 
 #if WTF_CPU_X86_64
     void sarq_CLr(RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp64(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst);
     }
 
     void sarq_i8r(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "sarq       $%d, %s\n", MAYBE_PAD, imm, nameIReg(8, dst));
+        spew("sarq       $%d, %s", imm, nameIReg(8, dst));
         if (imm == 1)
             m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst);
         else {
             m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst);
             m_formatter.immediate8(imm);
         }
     }
 
     void shlq_i8r(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "shlq       $%d, %s\n", MAYBE_PAD, imm, nameIReg(8, dst));
+        spew("shlq       $%d, %s", imm, nameIReg(8, dst));
         if (imm == 1)
             m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_SHL, dst);
         else {
             m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SHL, dst);
             m_formatter.immediate8(imm);
         }
     }
 
     void shrq_i8r(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "shrq       $%d, %s\n", MAYBE_PAD, imm, nameIReg(8, dst));
+        spew("shrq       $%d, %s", imm, nameIReg(8, dst));
         if (imm == 1)
             m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_SHR, dst);
         else {
             m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SHR, dst);
             m_formatter.immediate8(imm);
         }
     }
 #endif
 
     void imull_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "imull       %s, %s\n", MAYBE_PAD, nameIReg(4,src), nameIReg(4, dst));
+        spew("imull       %s, %s", nameIReg(4,src), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, src);
     }
 
     void imull_mr(int offset, RegisterID base, RegisterID dst)
     {
-        FIXME_INSN_PRINTING;
+        spew("imull       %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(4,base), nameIReg(4,dst));
         m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, base, offset);
     }
 
     void imull_i32r(RegisterID src, int32_t value, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "imull      %d, %s, %s\n",
-                       MAYBE_PAD, value, nameIReg(4, src), nameIReg(4, dst));
+        spew("imull      %d, %s, %s",
+             value, nameIReg(4, src), nameIReg(4, dst));
         m_formatter.oneByteOp(OP_IMUL_GvEvIz, dst, src);
         m_formatter.immediate32(value);
     }
 
     void idivl_r(RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "idivl      %s\n", MAYBE_PAD, 
-                       nameIReg(4, dst));
+        spew("idivl      %s", 
+             nameIReg(4, dst));
         m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_IDIV, dst);
     }
 
     // Comparisons:
 
     void cmpl_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpl       %s, %s\n", MAYBE_PAD, 
-                       nameIReg(4, src), nameIReg(4, dst));
+        spew("cmpl       %s, %s", 
+             nameIReg(4, src), nameIReg(4, dst));
         m_formatter.oneByteOp(OP_CMP_EvGv, src, dst);
     }
 
     void cmpl_rm(RegisterID src, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpl       %s, %s0x%x(%s)\n", MAYBE_PAD, 
-                       nameIReg(4, src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("cmpl       %s, %s0x%x(%s)", 
+             nameIReg(4, src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_CMP_EvGv, src, base, offset);
     }
 
     void cmpl_mr(int offset, RegisterID base, RegisterID src)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpl       %s0x%x(%s), %s\n", MAYBE_PAD, 
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(4, base), nameIReg(src));
+        spew("cmpl       %s0x%x(%s), %s", 
+             PRETTY_PRINT_OFFSET(offset), nameIReg(4, base), nameIReg(src));
         m_formatter.oneByteOp(OP_CMP_GvEv, src, base, offset);
     }
 
     void cmpl_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpl       $0x%x, %s\n", MAYBE_PAD, imm, nameIReg(4, dst));
+        spew("cmpl       $0x%x, %s", imm, nameIReg(4, dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst);
             m_formatter.immediate32(imm);
         }
     }
 
     void cmpl_ir_force32(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpl       $0x%x, %s\n", MAYBE_PAD, imm, nameIReg(4, dst));
+        spew("cmpl       $0x%x, %s", imm, nameIReg(4, dst));
         m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst);
         m_formatter.immediate32(imm);
     }
     
     void cmpl_im(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpl       $0x%x, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(4,base));
+        spew("cmpl       $0x%x, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(4,base));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset);
             m_formatter.immediate32(imm);
         }
     }
@@ -1159,67 +1079,61 @@ public:
     void cmpb_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
         m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset);
         m_formatter.immediate8(imm);
     }
 
     void cmpl_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpl       %d, %d(%s,%s,%d)\n", MAYBE_PAD,
-                       imm, offset, nameIReg(4,base), nameIReg(4,index), scale);
+        spew("cmpl       %d, %d(%s,%s,%d)",
+             imm, offset, nameIReg(4,base), nameIReg(4,index), scale);
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset);
             m_formatter.immediate32(imm);
         }
     }
 
     void cmpl_im_force32(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpl       $0x%x, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(4,base));
+        spew("cmpl       $0x%x, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(4,base));
         m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset);
         m_formatter.immediate32(imm);
     }
 
 #if WTF_CPU_X86_64
     void cmpq_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpq       %s, %s\n", MAYBE_PAD,
-                       nameIReg(8, src), nameIReg(8, dst));
+        spew("cmpq       %s, %s",
+             nameIReg(8, src), nameIReg(8, dst));
         m_formatter.oneByteOp64(OP_CMP_EvGv, src, dst);
     }
 
     void cmpq_rm(RegisterID src, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpq       %s, %d(%s)\n", MAYBE_PAD,
-                       nameIReg(8, src), offset, nameIReg(8, base));
+        spew("cmpq       %s, %d(%s)",
+             nameIReg(8, src), offset, nameIReg(8, base));
         m_formatter.oneByteOp64(OP_CMP_EvGv, src, base, offset);
     }
 
     void cmpq_mr(int offset, RegisterID base, RegisterID src)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpq       %d(%s), %s\n", MAYBE_PAD,
-                       offset, nameIReg(8, base), nameIReg(8, src));
+        spew("cmpq       %d(%s), %s",
+             offset, nameIReg(8, base), nameIReg(8, src));
         m_formatter.oneByteOp64(OP_CMP_GvEv, src, base, offset);
     }
 
     void cmpq_ir(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmpq       %d, %s\n", MAYBE_PAD,
-                       imm, nameIReg(8, dst));
+        spew("cmpq       %d, %s",
+             imm, nameIReg(8, dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst);
             m_formatter.immediate32(imm);
         }
     }
@@ -1251,17 +1165,17 @@ public:
     void cmpl_rm(RegisterID reg, void* addr)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_CMP_EvGv, reg, addr);
     }
 
     void cmpl_im(int imm, void* addr)
     {
-        FIXME_INSN_PRINTING;
+        spew("cmpl       $0x%x, 0x%p", imm, addr);
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, addr);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, addr);
             m_formatter.immediate32(imm);
         }
     }
@@ -1285,44 +1199,40 @@ public:
             m_formatter.prefix(PRE_OPERAND_SIZE);
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset);
             m_formatter.immediate16(imm);
         }
     }
 
     void testl_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "testl      %s, %s\n", MAYBE_PAD,
-                       nameIReg(4,src), nameIReg(4,dst));
+        spew("testl      %s, %s",
+             nameIReg(4,src), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_TEST_EvGv, src, dst);
     }
 
     void testb_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "testb      %s, %s\n", MAYBE_PAD,
-                       nameIReg(1,src), nameIReg(1,dst));
+        spew("testb      %s, %s",
+             nameIReg(1,src), nameIReg(1,dst));
         m_formatter.oneByteOp(OP_TEST_EbGb, src, dst);
     }
     
     void testl_i32r(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "testl      $0x%x, %s\n", MAYBE_PAD,
-                       imm, nameIReg(dst));
+        spew("testl      $0x%x, %s",
+             imm, nameIReg(dst));
         m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst);
         m_formatter.immediate32(imm);
     }
 
     void testl_i32m(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "testl      $0x%x, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("testl      $0x%x, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset);
         m_formatter.immediate32(imm);
     }
     
     void testb_im(int imm, int offset, RegisterID base)
     {
         m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, offset);
         m_formatter.immediate8(imm);
@@ -1339,34 +1249,32 @@ public:
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset);
         m_formatter.immediate32(imm);
     }
 
 #if WTF_CPU_X86_64
     void testq_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "testq      %s, %s\n", MAYBE_PAD,
-                       nameIReg(8,src), nameIReg(8,dst));
+        spew("testq      %s, %s",
+             nameIReg(8,src), nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_TEST_EvGv, src, dst);
     }
 
     void testq_i32r(int imm, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst);
         m_formatter.immediate32(imm);
     }
 
     void testq_i32m(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "testq      $0x%x, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("testq      $0x%x, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset);
         m_formatter.immediate32(imm);
     }
 
     void testq_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset);
@@ -1378,28 +1286,26 @@ public:
     {
         FIXME_INSN_PRINTING;
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp(OP_TEST_EvGv, src, dst);
     }
     
     void testb_i8r(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "testb      $0x%x, %s\n", MAYBE_PAD,
-                       imm, nameIReg(1,dst));
+        spew("testb      $0x%x, %s",
+             imm, nameIReg(1,dst));
         m_formatter.oneByteOp8(OP_GROUP3_EbIb, GROUP3_OP_TEST, dst);
         m_formatter.immediate8(imm);
     }
 
     void setCC_r(Condition cond, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "set%s      %s\n", MAYBE_PAD,
-                       nameCC(cond), nameIReg(1,dst));
+        spew("set%s      %s",
+             nameCC(cond), nameIReg(1,dst));
         m_formatter.twoByteOp8(setccOpcode(cond), (GroupOpcodeID)0, dst);
     }
 
     void sete_r(RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp8(setccOpcode(ConditionE), (GroupOpcodeID)0, dst);
     }
@@ -1421,181 +1327,164 @@ public:
         FIXME_INSN_PRINTING;
         setne_r(dst);
     }
 
     // Various move ops:
 
     void cdq()
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cdq              \n", MAYBE_PAD);
+        spew("cdq              ");
         m_formatter.oneByteOp(OP_CDQ);
     }
 
     void xchgl_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "xchgl      %s, %s\n", MAYBE_PAD,
-                       nameIReg(4,src), nameIReg(4,dst));
+        spew("xchgl      %s, %s",
+             nameIReg(4,src), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_XCHG_EvGv, src, dst);
     }
 
 #if WTF_CPU_X86_64
     void xchgq_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "xchgq      %s, %s\n", MAYBE_PAD,
-                       nameIReg(8,src), nameIReg(8,dst));
+        spew("xchgq      %s, %s",
+             nameIReg(8,src), nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_XCHG_EvGv, src, dst);
     }
 #endif
 
     void movl_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movl       %s, %s\n", MAYBE_PAD,
-                       nameIReg(4,src), nameIReg(4,dst));
+        spew("movl       %s, %s",
+             nameIReg(4,src), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_MOV_EvGv, src, dst);
     }
 
     void movw_rm(RegisterID src, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movw       %s, %s0x%x(%s)\n", MAYBE_PAD,
-                       nameIReg(2,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movw       %s, %s0x%x(%s)",
+             nameIReg(2,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset);
     }
     
     void movl_rm(RegisterID src, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movl       %s, %s0x%x(%s)\n", MAYBE_PAD,
-                       nameIReg(4,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movl       %s, %s0x%x(%s)",
+             nameIReg(4,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset);
     }
 
     void movl_rm_disp32(RegisterID src, int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset);
     }
 
     void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movw       %s, %d(%s,%s,%d)\n", MAYBE_PAD, 
-                       nameIReg(2, src), offset, nameIReg(base), nameIReg(index), scale);
+        spew("movw       %s, %d(%s,%s,%d)", 
+             nameIReg(2, src), offset, nameIReg(base), nameIReg(index), scale);
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
     }
 
     void movl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movl       %s, %d(%s,%s,%d)\n", MAYBE_PAD, 
-                       nameIReg(4, src), offset, nameIReg(base), nameIReg(index), scale);
+        spew("movl       %s, %d(%s,%s,%d)", 
+             nameIReg(4, src), offset, nameIReg(base), nameIReg(index), scale);
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
     }
     
     void movl_mEAX(void* addr)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_MOV_EAXOv);
 #if WTF_CPU_X86_64
         m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
 #else
         m_formatter.immediate32(reinterpret_cast<int>(addr));
 #endif
     }
 
     void movl_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movl       %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
+        spew("movl       %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, offset);
     }
 
     void movl_mr_disp32(int offset, RegisterID base, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp_disp32(OP_MOV_GvEv, dst, base, offset);
     }
 
     void movl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movl       %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameIReg(4, dst));
+        spew("movl       %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameIReg(4, dst));
         m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, index, scale, offset);
     }
 
     void movl_i32r(int imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movl       $0x%x, %s\n", MAYBE_PAD,
-                       imm, nameIReg(dst));
+        spew("movl       $0x%x, %s",
+             imm, nameIReg(dst));
         m_formatter.oneByteOp(OP_MOV_EAXIv, dst);
         m_formatter.immediate32(imm);
     }
 
     void movb_i8m(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movb       $0x%x, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movb       $0x%x, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, offset);
         m_formatter.immediate8(imm);
     }
 
     void movb_i8m(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movb       $0x%x, %d(%s,%s,%d)\n", MAYBE_PAD,
-                       imm, offset, nameIReg(base), nameIReg(index), scale);
+        spew("movb       $0x%x, %d(%s,%s,%d)",
+             imm, offset, nameIReg(base), nameIReg(index), scale);
         m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, index, scale, offset);
         m_formatter.immediate8(imm);
     }
 
     void movw_i16m(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movw       $0x%x, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movw       $0x%x, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, offset);
         m_formatter.immediate16(imm);
     }
 
     void movl_i32m(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movl       $0x%x, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movl       $0x%x, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, offset);
         m_formatter.immediate32(imm);
     }
 
     void movw_i16m(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movw       $0x%x, %d(%s,%s,%d)\n", MAYBE_PAD,
-                       imm, offset, nameIReg(base), nameIReg(index), scale);
+        spew("movw       $0x%x, %d(%s,%s,%d)",
+             imm, offset, nameIReg(base), nameIReg(index), scale);
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, index, scale, offset);
         m_formatter.immediate16(imm);
     }
 
     void movl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movl       $0x%x, %d(%s,%s,%d)\n", MAYBE_PAD,
-                       imm, offset, nameIReg(base), nameIReg(index), scale);
+        spew("movl       $0x%x, %d(%s,%s,%d)",
+             imm, offset, nameIReg(base), nameIReg(index), scale);
         m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, index, scale, offset);
         m_formatter.immediate32(imm);
     }
 
     void movl_EAXm(void* addr)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_MOV_OvEAX);
@@ -1604,41 +1493,38 @@ public:
 #else
         m_formatter.immediate32(reinterpret_cast<int>(addr));
 #endif
     }
 
 #if WTF_CPU_X86_64
     void movq_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movq       %s, %s\n", MAYBE_PAD,
-                       nameIReg(8,src), nameIReg(8,dst));
+        spew("movq       %s, %s",
+             nameIReg(8,src), nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_MOV_EvGv, src, dst);
     }
 
     void movq_rm(RegisterID src, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movq       %s, %s0x%x(%s)\n", MAYBE_PAD,
-                       nameIReg(8,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movq       %s, %s0x%x(%s)",
+             nameIReg(8,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, offset);
     }
 
     void movq_rm_disp32(RegisterID src, int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp64_disp32(OP_MOV_EvGv, src, base, offset);
     }
 
     void movq_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movq       %s, %s0x%x(%s)\n", MAYBE_PAD,
-                       nameIReg(8,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movq       %s, %s0x%x(%s)",
+             nameIReg(8,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, index, scale, offset);
     }
 
     void movq_mEAX(void* addr)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp64(OP_MOV_EAXOv);
         m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
@@ -1648,298 +1534,268 @@ public:
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp64(OP_MOV_OvEAX);
         m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
     }
 
     void movq_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX  "movq       %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(8,dst));
+        spew("movq       %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, offset);
     }
 
     void movq_mr_disp32(int offset, RegisterID base, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp64_disp32(OP_MOV_GvEv, dst, base, offset);
     }
 
     void movq_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movq       %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameIReg(8,dst));
+        spew("movq       %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, index, scale, offset);
     }
 
     void leaq_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "leaq       %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameIReg(8,dst)),
+        spew("leaq       %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameIReg(8,dst)),
         m_formatter.oneByteOp64(OP_LEA, dst, base, index, scale, offset);
     }
 
     void movq_i32m(int imm, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX  "movq       $%d, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movq       $%d, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp64(OP_GROUP11_EvIz, GROUP11_MOV, base, offset);
         m_formatter.immediate32(imm);
     }
 
     void movq_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX  "movq       $%d, %s0x%x(%s)\n", MAYBE_PAD,
-                       imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movq       $%d, %s0x%x(%s)",
+             imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp64(OP_GROUP11_EvIz, GROUP11_MOV, base, index, scale, offset);
         m_formatter.immediate32(imm);
     }
 
     void movq_i64r(int64_t imm, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movabsq    $0x%llx, %s\n", MAYBE_PAD,
-                       (unsigned long long int)imm, nameIReg(8,dst));
+        spew("movabsq    $0x%llx, %s",
+             (unsigned long long int)imm, nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_MOV_EAXIv, dst);
         m_formatter.immediate64(imm);
     }
     
     void movsxd_rr(RegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movsxd     %s, %s\n", MAYBE_PAD,
-                       nameIReg(4, src), nameIReg(8, dst));
+        spew("movsxd     %s, %s",
+             nameIReg(4, src), nameIReg(8, dst));
         m_formatter.oneByteOp64(OP_MOVSXD_GvEv, dst, src);
     }
     
     
 #else
     void movl_rm(RegisterID src, void* addr)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movl       %s, 0(%p)\n", MAYBE_PAD,
-                       nameIReg(4, src), addr);
+        spew("movl       %s, 0(%p)",
+             nameIReg(4, src), addr);
         if (src == X86Registers::eax)
             movl_EAXm(addr);
         else 
             m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
     }
     
     void movl_mr(void* addr, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movl       0(%p), %s\n", MAYBE_PAD,
-                       addr, nameIReg(4, dst));
+        spew("movl       0(%p), %s",
+             addr, nameIReg(4, dst));
         if (dst == X86Registers::eax)
             movl_mEAX(addr);
         else
             m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr);
     }
 
     void movl_i32m(int imm, void* addr)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, addr);
         m_formatter.immediate32(imm);
     }
 #endif
 
     void movb_rm(RegisterID src, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movb       %s, %s0x%x(%s)\n", MAYBE_PAD,
-                       nameIReg(1, src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movb       %s, %s0x%x(%s)",
+             nameIReg(1, src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp8(OP_MOV_EbGv, src, base, offset);
     }
 
     void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movb       %s, %d(%s,%s,%d)\n", MAYBE_PAD,
-                       nameIReg(1, src), offset, nameIReg(base), nameIReg(index), scale);
+        spew("movb       %s, %d(%s,%s,%d)",
+             nameIReg(1, src), offset, nameIReg(base), nameIReg(index), scale);
         m_formatter.oneByteOp8(OP_MOV_EbGv, src, base, index, scale, offset);
     }
 
     void movzbl_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movzbl     %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
+        spew("movzbl     %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset);
     }
 
     void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movzbl     %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameIReg(dst));
+        spew("movzbl     %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset);
     }
 
     void movxbl_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movxbl     %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
+        spew("movxbl     %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset);
     }
 
     void movxbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movxbl     %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameIReg(dst));
+        spew("movxbl     %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset);
     }
 
     void movzwl_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movzwl     %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
+        spew("movzwl     %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, offset);
     }
 
     void movzwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movzwl     %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameIReg(dst));
+        spew("movzwl     %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, index, scale, offset);
     }
 
     void movxwl_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movxwl     %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
+        spew("movxwl     %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, offset);
     }
 
     void movxwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movxwl     %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameIReg(dst));
+        spew("movxwl     %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, index, scale, offset);
     }
 
     void movzbl_rr(RegisterID src, RegisterID dst)
     {
         // In 64-bit, this may cause an unnecessary REX to be planted (if the dst register
         // is in the range ESP-EDI, and the src would not have required a REX).  Unneeded
         // REX prefixes are defined to be silently ignored by the processor.
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movzbl      %s, %s\n", MAYBE_PAD,
-                       nameIReg(4,src), nameIReg(4,dst));
+        spew("movzbl      %s, %s",
+             nameIReg(4,src), nameIReg(4,dst));
         m_formatter.twoByteOp8(OP2_MOVZX_GvEb, dst, src);
     }
 
     void leal_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "leal       %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameIReg(dst));
+        spew("leal       %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameIReg(dst));
         m_formatter.oneByteOp(OP_LEA, dst, base, index, scale, offset);
     }
 
     void leal_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "leal       %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4,dst));
+        spew("leal       %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4,dst));
         m_formatter.oneByteOp(OP_LEA, dst, base, offset);
     }
 #if WTF_CPU_X86_64
     void leaq_mr(int offset, RegisterID base, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "leaq       %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(8,dst));
+        spew("leaq       %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_LEA, dst, base, offset);
     }
 #endif
 
     // Flow control:
 
     JmpSrc call()
     {
         m_formatter.oneByteOp(OP_CALL_rel32);
         JmpSrc r = m_formatter.immediateRel32();
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "call       ((%d))\n", MAYBE_PAD, r.m_offset);
+        spew("call       ((%d))", r.m_offset);
         return r;
     }
     
     JmpSrc call(RegisterID dst)
     {
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, dst);
         JmpSrc r = JmpSrc(m_formatter.size());
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "call       *%s\n", MAYBE_PAD, nameIReg(dst));
+        spew("call       *%s", nameIReg(dst));
         return r;
     }
     
     void call_m(int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "call       %s0x%x(%s)\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("call       %s0x%x(%s)",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, base, offset);
     }
 
     // Comparison of EAX against a 32-bit immediate. The immediate is patched
     // in as if it were a jump target. The intention is to toggle the first
     // byte of the instruction between a CMP and a JMP to produce a pseudo-NOP.
     JmpSrc cmp_eax()
     {
         m_formatter.oneByteOp(OP_CMP_EAXIv);
         JmpSrc r = m_formatter.immediateRel32();
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cmp        eax, ((%d))\n", MAYBE_PAD, r.m_offset);
+        spew("cmp        eax, ((%d))", r.m_offset);
         return r;
     }
 
     JmpSrc jmp()
     {
         m_formatter.oneByteOp(OP_JMP_rel32);
         JmpSrc r = m_formatter.immediateRel32();
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "jmp        ((%d))\n", MAYBE_PAD, r.m_offset);
+        spew("jmp        ((%d))", r.m_offset);
         return r;
     }
     
     // Return a JmpSrc so we have a label to the jump, so we can use this
     // To make a tail recursive call on x86-64.  The MacroAssembler
     // really shouldn't wrap this as a Jump, since it can't be linked. :-/
     JmpSrc jmp_r(RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "jmp        ((%s))\n", MAYBE_PAD,
-           nameIReg(dst));
+        spew("jmp        ((%s))",
+             nameIReg(dst));
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, dst);
         return JmpSrc(m_formatter.size());
     }
     
     void jmp_m(int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, base, offset);
     }
 
     void jmp_m(int offset, RegisterID base, RegisterID index, int scale) {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "jmp       ((%d(%s,%s,%d)))\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale);
+        spew("jmp       ((%d(%s,%s,%d)))",
+             offset, nameIReg(base), nameIReg(index), scale);
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, base, index, scale, offset);
     }
 
 #if WTF_CPU_X86_64
     void jmp_rip(int ripOffset) {
         // rip-relative addressing.
         m_formatter.oneByteRipOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, ripOffset);
     }
@@ -2048,577 +1904,521 @@ public:
         m_formatter.twoByteOp(jccRel32(ConditionS));
         return m_formatter.immediateRel32();
     }
 
     JmpSrc jCC(Condition cond)
     {
         m_formatter.twoByteOp(jccRel32(cond));
         JmpSrc r = m_formatter.immediateRel32();
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "j%s        ((%d))\n", MAYBE_PAD,
-                       nameCC(cond), r.m_offset);
+        spew("j%s        ((%d))",
+             nameCC(cond), r.m_offset);
         return r;
     }
 
     // SSE operations:
 
     void pcmpeqw_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "pcmpeqw    %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(dst));
+        spew("pcmpeqw    %s, %s",
+             nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_PCMPEQW, (RegisterID)dst, (RegisterID)src); /* right order ? */
     }
 
     void addsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "addsd      %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(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 addsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "addsd      %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(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);
     }
 
 #if !WTF_CPU_X86_64
     void addsd_mr(const void* address, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "addsd      %p, %s\n", MAYBE_PAD,
-                       address, nameFPReg(dst));
+        spew("addsd      %p, %s",
+             address, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address);
     }
 #endif
 
     void cvtss2sd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cvtps2pd   %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(dst));
+        spew("cvtps2pd   %s, %s",
+             nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_CVTSS2SD_VsdEd, (RegisterID)dst, (RegisterID)src);
     }
 
     void cvtsd2ss_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cvtps2pd   %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(dst));
+        spew("cvtps2pd   %s, %s",
+             nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSD2SS_VsdEd, (RegisterID)dst, (RegisterID)src);
     }
 
     void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cvtsi2sd   %s, %s\n", MAYBE_PAD,
-                       nameIReg(src), nameFPReg(dst));
+        spew("cvtsi2sd   %s, %s",
+             nameIReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src);
     }
 
 #if WTF_CPU_X86_64
     void cvtsq2sd_rr(RegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cvtsq2sd   %s, %s\n", MAYBE_PAD,
-                       nameIReg(src), nameFPReg(dst));
+        spew("cvtsq2sd   %s, %s",
+             nameIReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp64(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src);
     }
 #endif
 
     void cvtsi2sd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cvtsi2sd   %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        spew("cvtsi2sd   %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, offset);
     }
 
     void cvtsi2sd_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cvtsi2sd   %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameFPReg(dst));
+        spew("cvtsi2sd   %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, index, scale, offset);
     }
 
 #if !WTF_CPU_X86_64
     void cvtsi2sd_mr(void* address, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cvtsi2sd   %p, %s\n", MAYBE_PAD,
-                       address, nameFPReg(dst));
+        spew("cvtsi2sd   %p, %s",
+             address, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, address);
     }
 #endif
 
     void cvttsd2si_rr(XMMRegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cvttsd2si  %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameIReg(dst));
+        spew("cvttsd2si  %s, %s",
+             nameFPReg(src), nameIReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
     }
 
 #if WTF_CPU_X86_64
     void cvttsd2sq_rr(XMMRegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "cvttsd2sq  %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameIReg(dst));
+        spew("cvttsd2sq  %s, %s",
+             nameFPReg(src), nameIReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
     }
 #endif
 
     void unpcklps_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "unpcklps   %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(dst));
+        spew("unpcklps   %s, %s",
+             nameFPReg(src), nameFPReg(dst));
         m_formatter.twoByteOp(OP2_UNPCKLPS_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
     void movd_rr(RegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movd       %s, %s\n", MAYBE_PAD,
-                       nameIReg(src), nameFPReg(dst));
+        spew("movd       %s, %s",
+             nameIReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_MOVD_VdEd, (RegisterID)dst, src);
     }
 
     void psrldq_rr(XMMRegisterID dest, int shift)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "psrldq     %s, %d\n", MAYBE_PAD,
-                       nameFPReg(dest), shift);
+        spew("psrldq     %s, %d",
+             nameFPReg(dest), shift);
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_PSRLDQ_Vd, (RegisterID)3, (RegisterID)dest);
         m_formatter.immediate8(shift);
     }
 
     void psllq_rr(XMMRegisterID dest, int shift)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "psllq     %s, %d\n", MAYBE_PAD,
-                       nameFPReg(dest), shift);
+        spew("psllq     %s, %d",
+             nameFPReg(dest), shift);
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_PSRLDQ_Vd, (RegisterID)6, (RegisterID)dest);
         m_formatter.immediate8(shift);
     }
 
     void psrlq_rr(XMMRegisterID dest, int shift)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "psrlq     %s, %d\n", MAYBE_PAD,
-                       nameFPReg(dest), shift);
+        spew("psrlq     %s, %d",
+             nameFPReg(dest), shift);
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_PSRLDQ_Vd, (RegisterID)2, (RegisterID)dest);
         m_formatter.immediate8(shift);
     }
 
     void movmskpd_rr(XMMRegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movmskpd   %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameIReg(dst));
+        spew("movmskpd   %s, %s",
+             nameFPReg(src), nameIReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_MOVMSKPD_EdVd, dst, (RegisterID)src);
     }
 
     void ptest_rr(XMMRegisterID lhs, XMMRegisterID rhs) {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "ptest      %s, %s\n", MAYBE_PAD,
-                       nameFPReg(lhs), nameFPReg(rhs));
+        spew("ptest      %s, %s",
+             nameFPReg(lhs), nameFPReg(rhs));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.threeByteOp(OP3_PTEST_VdVd, ESCAPE_PTEST, (RegisterID)rhs, (RegisterID)lhs);
     }
 
     void movd_rr(XMMRegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movd       %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameIReg(dst));
+        spew("movd       %s, %s",
+             nameFPReg(src), nameIReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_MOVD_EdVd, (RegisterID)src, dst);
     }
 
 #if WTF_CPU_X86_64
     void movq_rr(XMMRegisterID src, RegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movq       %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameIReg(dst));
+        spew("movq       %s, %s",
+             nameFPReg(src), nameIReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp64(OP2_MOVD_EdVd, (RegisterID)src, dst);
     }
 
     void movq_rr(RegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movq       %s, %s\n", MAYBE_PAD,
-                       nameIReg(src), nameFPReg(dst));
+        spew("movq       %s, %s",
+             nameIReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp64(OP2_MOVD_VdEd, (RegisterID)dst, src);
     }
 #endif
 
     void movsd_rm(XMMRegisterID src, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movsd      %s, %s0x%x(%s)\n", MAYBE_PAD,
-                       nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movsd      %s, %s0x%x(%s)",
+             nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset);
     }
 
     void movss_rm(XMMRegisterID src, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movss      %s, %s0x%x(%s)\n", MAYBE_PAD,
-                       nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movss      %s, %s0x%x(%s)",
+             nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset);
     }
 
     void movss_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movss      %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(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 movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movsd       %s, %d(%s,%s,%d)\n", MAYBE_PAD, 
-                       nameFPReg(src), offset, nameIReg(base), nameIReg(index), scale);
+        spew("movsd       %s, %d(%s,%s,%d)", 
+             nameFPReg(src), offset, nameIReg(base), nameIReg(index), scale);
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
     }
 
     void movss_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movss       %s, %d(%s,%s,%d)\n", MAYBE_PAD, 
-                       nameFPReg(src), offset, nameIReg(base), nameIReg(index), scale);
+        spew("movss       %s, %d(%s,%s,%d)", 
+             nameFPReg(src), offset, nameIReg(base), nameIReg(index), scale);
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
     }
 
     void movss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movss      %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameFPReg(dst));
+        spew("movss      %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, index, scale, offset);
     }
 
     void movsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movsd      %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        spew("movsd      %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
     void movsd_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movsd      %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameFPReg(dst));
+        spew("movsd      %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, index, scale, offset);
     }
 
     void movsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movsd      %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(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);
     }
 
 #if !WTF_CPU_X86_64
     void movsd_mr(const void* address, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movsd      %p, %s\n", MAYBE_PAD,
-                       address, nameFPReg(dst));
+        spew("movsd      %p, %s",
+             address, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
     }
 #endif
 
     void movdqa_rm(XMMRegisterID src, int offset, RegisterID base)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movdqa     %s, %s0x%x(%s)\n", MAYBE_PAD,
-                       nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        spew("movdqa     %s, %s0x%x(%s)",
+             nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_MOVDQA_WsdVsd, (RegisterID)src, base, offset);
     }
 
     void movdqa_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movdqa      %s, %d(%s,%s,%d)\n", MAYBE_PAD, 
-                       nameFPReg(src), offset, nameIReg(base), nameIReg(index), scale);
+        spew("movdqa      %s, %d(%s,%s,%d)", 
+             nameFPReg(src), offset, nameIReg(base), nameIReg(index), scale);
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_MOVDQA_WsdVsd, (RegisterID)src, base, index, scale, offset);
     }
 
     void movdqa_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movdqa     %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        spew("movdqa     %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_MOVDQA_VsdWsd, (RegisterID)dst, base, offset);
     }
 
     void movdqa_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "movdqa     %d(%s,%s,%d), %s\n", MAYBE_PAD,
-                       offset, nameIReg(base), nameIReg(index), scale, nameFPReg(dst));
+        spew("movdqa     %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), scale, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_MOVDQA_VsdWsd, (RegisterID)dst, base, index, scale, offset);
     }
 
     void mulsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "mulsd      %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(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 mulsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "mulsd      %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(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 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)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "subsd      %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(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 subsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "subsd      %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(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 ucomisd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "ucomisd    %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(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);
     }
 
     void ucomisd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "ucomisd    %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        spew("ucomisd    %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
     void divsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "divsd      %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(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 divsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "divsd      %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(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 xorpd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "xorpd      %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(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 orpd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "orpd       %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(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);
     }
 
     void andpd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "andpd      %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(dst));
+        spew("andpd      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_ANDPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
     }
 
     void sqrtsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "sqrtsd     %s, %s\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(dst));
+        spew("sqrtsd     %s, %s",
+             nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_SQRTSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
     void roundsd_rr(XMMRegisterID src, XMMRegisterID dst, RoundingMode mode)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "roundsd     %s, %s, %d\n", MAYBE_PAD,
-                       nameFPReg(src), nameFPReg(dst), (int)mode);
+        spew("roundsd     %s, %s, %d",
+             nameFPReg(src), nameFPReg(dst), (int)mode);
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.threeByteOp(OP3_ROUNDSD_VsdWsd, ESCAPE_ROUNDSD, (RegisterID)dst, (RegisterID)src);
         m_formatter.immediate8(mode);
     }
 
     void pinsrd_rr(RegisterID src, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "pinsrd     $1, %s, %s\n", MAYBE_PAD,
-                       nameIReg(src), nameFPReg(dst));
+        spew("pinsrd     $1, %s, %s",
+             nameIReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.threeByteOp(OP3_PINSRD_VsdWsd, ESCAPE_PINSRD, (RegisterID)dst, (RegisterID)src);
         m_formatter.immediate8(0x01); // the $1
     }
 
     void pinsrd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "pinsrd     $1, %s0x%x(%s), %s\n", MAYBE_PAD,
-                       PRETTY_PRINT_OFFSET(offset),
-                       nameIReg(base), nameFPReg(dst));
+        spew("pinsrd     $1, %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset),
+             nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.threeByteOp(OP3_PINSRD_VsdWsd, ESCAPE_PINSRD, (RegisterID)dst, base, offset);
         m_formatter.immediate8(0x01); // the $1
     }
 
     // Misc instructions:
 
     void int3()
     {
-        js::JaegerSpew(js::JSpew_Insns, IPFX "int3\n", MAYBE_PAD);
+        spew("int3");
         m_formatter.oneByteOp(OP_INT3);
     }
 
     void ret()
     {
-        js::JaegerSpew(js::JSpew_Insns, IPFX "ret\n", MAYBE_PAD);
+        spew("ret");
         m_formatter.oneByteOp(OP_RET);
     }
 
     void ret(int imm)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "ret        %d\n", MAYBE_PAD,
-                       imm);
+        spew("ret        %d",
+             imm);
         m_formatter.oneByteOp(OP_RET_Iz);
         m_formatter.immediate16(imm);
     }
 
     void predictNotTaken()
     {
         FIXME_INSN_PRINTING;
         m_formatter.prefix(PRE_PREDICT_BRANCH_NOT_TAKEN);
     }
 
 #if WTF_CPU_X86
     void pusha()
     {
-        js::JaegerSpew(js::JSpew_Insns, IPFX "pusha\n", MAYBE_PAD);
+        spew("pusha");
         m_formatter.oneByteOp(OP_PUSHA);
     }
 
     void popa()
     {
-        js::JaegerSpew(js::JSpew_Insns, IPFX "popa\n", MAYBE_PAD);
+        spew("popa");
         m_formatter.oneByteOp(OP_POPA);
     }
 #endif
 
     // Assembler admin methods:
 
     JmpDst label()
     {
         JmpDst r = JmpDst(m_formatter.size());
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "#label     ((%d))\n", MAYBE_PAD, r.m_offset);
+        spew("#label     ((%d))", r.m_offset);
         return r;
     }
 
     size_t currentOffset() const {
         return m_formatter.size();
     }
     
     static JmpDst labelFor(JmpSrc jump, intptr_t offset = 0)
     {
-        FIXME_INSN_PRINTING;
         return JmpDst(jump.m_offset + offset);
     }
     
     JmpDst align(int alignment)
     {
         FIXME_INSN_PRINTING;
         while (!m_formatter.isAligned(alignment))
             m_formatter.oneByteOp(OP_HLT);
@@ -2662,106 +2462,98 @@ public:
         ASSERT(from.m_offset != -1);
         ASSERT(to.m_offset != -1);
 
         // Sanity check - if the assembler has OOM'd, it will start overwriting
         // its internal buffer and thus our links could be garbage.
         if (oom())
             return;
 
-        js::JaegerSpew(js::JSpew_Insns,
-                       IPFX "##link     ((%d)) jumps to ((%d))\n", MAYBE_PAD,
-                       from.m_offset, to.m_offset);
+        spew("##link     ((%d)) jumps to ((%d))",
+             from.m_offset, to.m_offset);
         char* code = reinterpret_cast<char*>(m_formatter.data());
         setRel32(code + from.m_offset, code + to.m_offset);
     }
     
     static void linkJump(void* code, JmpSrc from, void* to)
     {
         ASSERT(from.m_offset != -1);
 
-        js::JaegerSpew(js::JSpew_Insns,
-                       ISPFX "##link     ((%d)) jumps to ((%p))\n",
-                       from.m_offset, to);
+        staticSpew("##link     ((%d)) jumps to ((%p))",
+                   from.m_offset, to);
         setRel32(reinterpret_cast<char*>(code) + from.m_offset, to);
     }
 
     static void linkCall(void* code, JmpSrc from, void* to)
     {
         ASSERT(from.m_offset != -1);
 
-        FIXME_INSN_PRINTING;
+        staticSpew("##linkCall");
         setRel32(reinterpret_cast<char*>(code) + from.m_offset, to);
     }
 
     static void linkPointer(void* code, JmpDst where, void* value)
     {
         ASSERT(where.m_offset != -1);
 
-        FIXME_INSN_PRINTING;
+        staticSpew("##linkPointer");
         setPointer(reinterpret_cast<char*>(code) + where.m_offset, value);
     }
 
     static void relinkJump(void* from, void* to)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       ISPFX "##relinkJump ((from=%p)) ((to=%p))\n",
-                       from, to);
+        staticSpew("##relinkJump ((from=%p)) ((to=%p))",
+                   from, to);
         setRel32(from, to);
     }
 
     static bool canRelinkJump(void* from, void* to)
     {
         intptr_t offset = reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from);
         return (offset == static_cast<int32_t>(offset));
     }
     
     static void relinkCall(void* from, void* to)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       ISPFX "##relinkCall ((from=%p)) ((to=%p))\n",
-                       from, to);
+        staticSpew("##relinkCall ((from=%p)) ((to=%p))",
+                   from, to);
         setRel32(from, to);
     }
 
     static void repatchInt32(void* where, int32_t value)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       ISPFX "##relinkInt32 ((where=%p)) ((value=%d))\n",
-                       where, value);
+        staticSpew("##relinkInt32 ((where=%p)) ((value=%d))",
+                   where, value);
         setInt32(where, value);
     }
 
     static void repatchPointer(void* where, void* value)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       ISPFX "##repatchPtr ((where=%p)) ((value=%p))\n",
-                       where, value);
+        staticSpew("##repatchPtr ((where=%p)) ((value=%p))",
+                   where, value);
         setPointer(where, value);
     }
 
     static void repatchLoadPtrToLEA(void* where)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       ISPFX "##repatchLoadPtrToLEA ((where=%p))\n",
-                       where);
+        staticSpew("##repatchLoadPtrToLEA ((where=%p))",
+                   where);
 
 #if WTF_CPU_X86_64
         // On x86-64 pointer memory accesses require a 64-bit operand, and as such a REX prefix.
         // Skip over the prefix byte.
         where = reinterpret_cast<char*>(where) + 1;
 #endif
         *reinterpret_cast<unsigned char*>(where) = static_cast<unsigned char>(OP_LEA);
     }
     
     static void repatchLEAToLoadPtr(void* where)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       ISPFX "##repatchLEAToLoadPtr ((where=%p))\n",
-                       where);
+        staticSpew("##repatchLEAToLoadPtr ((where=%p))",
+                   where);
 #if WTF_CPU_X86_64
         // On x86-64 pointer memory accesses require a 64-bit operand, and as such a REX prefix.
         // Skip over the prefix byte.
         where = reinterpret_cast<char*>(where) + 1;
 #endif
         *reinterpret_cast<unsigned char*>(where) = static_cast<unsigned char>(OP_MOV_GvEv);
     }
 
@@ -2814,18 +2606,17 @@ public:
     {
         intptr_t offset = reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from);
         ASSERT(offset == static_cast<int32_t>(offset));
 #define JS_CRASH(x) *(int *)x = 0
         if (offset != static_cast<int32_t>(offset))
             JS_CRASH(0xC0DE);
 #undef JS_CRASH
 
-        js::JaegerSpew(js::JSpew_Insns,
-                       ISPFX "##setRel32 ((from=%p)) ((to=%p))\n", from, to);
+        staticSpew("##setRel32 ((from=%p)) ((to=%p))", from, to);
         setInt32(from, offset);
     }
 
     static void *getRel32Target(void* where)
     {
         int32_t rel = getInt32(where);
         return (char *)where + rel;
     }
@@ -2837,18 +2628,17 @@ public:
 
     static void **getPointerRef(void* where)
     {
         return &reinterpret_cast<void **>(where)[-1];
     }
 
     static void setPointer(void* where, const void* value)
     {
-        js::JaegerSpew(js::JSpew_Insns,
-                       ISPFX "##setPtr     ((where=%p)) ((value=%p))\n", where, value);
+        staticSpew("##setPtr     ((where=%p)) ((value=%p))", where, value);
         reinterpret_cast<const void**>(where)[-1] = value;
     }
 
 private:
 
     static int32_t getInt32(void* where)
     {
         return reinterpret_cast<int32_t*>(where)[-1];
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2660,17 +2660,17 @@ frontend::EmitFunctionScript(JSContext *
     bool singleton =
         cx->typeInferenceEnabled() &&
         bce->parent &&
         bce->parent->checkSingletonContext();
 
     /* Initialize fun->script() so that the debugger has a valid fun->script(). */
     RootedFunction fun(cx, bce->script->function());
     JS_ASSERT(fun->isInterpreted());
-    JS_ASSERT(!fun->script().unsafeGet());
+    JS_ASSERT(!fun->hasScript());
     fun->setScript(bce->script);
     if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
         return false;
 
     bce->tellDebuggerAboutCompiledScript(cx);
 
     return true;
 }
@@ -4830,17 +4830,17 @@ EmitFor(JSContext *cx, BytecodeEmitter *
 }
 
 static JS_NEVER_INLINE bool
 EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     AssertCanGC();
     RootedFunction fun(cx, pn->pn_funbox->function());
     JS_ASSERT(fun->isInterpreted());
-    if (fun->script().unsafeGet()) {
+    if (fun->hasScript()) {
         /*
          * This second pass is needed to emit JSOP_NOP with a source note
          * for the already-emitted function definition prolog opcode. See
          * comments in EmitStatementList.
          */
         JS_ASSERT(pn->functionIsHoisted());
         JS_ASSERT(bce->sc->isFunction);
         return EmitFunctionDefNop(cx, bce, pn->pn_index);
--- a/js/src/ion/C1Spewer.h
+++ b/js/src/ion/C1Spewer.h
@@ -5,18 +5,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef DEBUG
 
 #ifndef jsion_c1spewer_h__
 #define jsion_c1spewer_h__
 
-#include "jscntxt.h"
-
 namespace js {
 namespace ion {
 
 class MDefinition;
 class MInstruction;
 class MBasicBlock;
 class MIRGraph;
 class LinearScanAllocator;
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -770,29 +770,29 @@ CodeGenerator::visitCallDOMNative(LCallD
     // Move the StackPointer back to its original location, unwinding the native exit frame.
     masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack);
     JS_ASSERT(masm.framePushed() == initialStack);
 
     dropArguments(call->numStackArgs() + 1);
     return true;
 }
 
+typedef bool (*GetIntrinsicValueFn)(JSContext *cx, HandlePropertyName, MutableHandleValue);
+static const VMFunction GetIntrinsicValueInfo =
+    FunctionInfo<GetIntrinsicValueFn>(GetIntrinsicValue);
+
 bool
 CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir)
 {
-    typedef bool (*pf)(JSContext *cx, HandlePropertyName, MutableHandleValue);
-    static const VMFunction Info = FunctionInfo<pf>(GetIntrinsicValue);
-
     pushArg(ImmGCPtr(lir->mir()->name()));
-    return callVM(Info, lir);
+    return callVM(GetIntrinsicValueInfo, lir);
 }
 
 typedef bool (*InvokeFunctionFn)(JSContext *, JSFunction *, uint32, Value *, Value *);
-static const VMFunction InvokeFunctionInfo =
-    FunctionInfo<InvokeFunctionFn>(InvokeFunction);
+static const VMFunction InvokeFunctionInfo = FunctionInfo<InvokeFunctionFn>(InvokeFunction);
 
 bool
 CodeGenerator::emitCallInvokeFunction(LInstruction *call, Register calleereg,
                                       uint32 argc, uint32 unusedStack)
 {
     // Nestle %esp up to the argument vector.
     // Each path must account for framePushed_ separately, for callVM to be valid.
     masm.freeStack(unusedStack);
@@ -841,18 +841,18 @@ CodeGenerator::visitCallGeneric(LCallGen
     masm.checkStackAlignment();
 
     // Guard that calleereg is actually a function object.
     masm.loadObjClass(calleereg, nargsreg);
     masm.cmpPtr(nargsreg, ImmWord(&js::FunctionClass));
     if (!bailoutIf(Assembler::NotEqual, call->snapshot()))
         return false;
 
-    // Guard that calleereg is a non-native function:
-    masm.branchIfFunctionIsNative(calleereg, &invoke);
+    // Guard that calleereg is an interpreted function with a JSScript:
+    masm.branchIfFunctionHasNoScript(calleereg, &invoke);
 
     // Knowing that calleereg is a non-native function, load the JSScript.
     masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
     ExecutionMode executionMode = gen->info().executionMode();
     masm.loadPtr(Address(objreg, ionOffset(executionMode)), objreg);
 
     // Guard that the IonScript has been compiled.
     masm.branchPtr(Assembler::BelowOrEqual, objreg, ImmWord(ION_COMPILING_SCRIPT), &invoke);
@@ -1144,19 +1144,19 @@ CodeGenerator::visitApplyArgsGeneric(LAp
                 return false;
             emitPopArguments(apply, copyreg);
             return true;
         }
     }
 
     Label end, invoke;
 
-    // Guard that calleereg is a non-native function:
+    // Guard that calleereg is an interpreted function with a JSScript:
     if (!apply->hasSingleTarget()) {
-        masm.branchIfFunctionIsNative(calleereg, &invoke);
+        masm.branchIfFunctionHasNoScript(calleereg, &invoke);
     } else {
         // Native single targets are handled by LCallNative.
         JS_ASSERT(!apply->getSingleTarget()->isNative());
     }
 
     // Knowing that calleereg is a non-native function, load the JSScript.
     masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
     masm.loadPtr(Address(objreg, ionOffset(executionMode)), objreg);
@@ -1452,16 +1452,17 @@ CodeGenerator::generateBody()
         iter++;
 
         mozilla::Maybe<Sprinter> printer;
         if (counts) {
             masm.inc64(AbsoluteAddress(counts->block(i).addressOfHitCount()));
             printer.construct(GetIonContext()->cx);
             if (!printer.ref().init())
                 return false;
+            masm.setPrinter(printer.addr());
         }
 
         for (; iter != current->end(); iter++) {
             IonSpew(IonSpew_Codegen, "instruction %s", iter->opName());
             if (counts)
                 printer.ref().printf("[%s]\n", iter->opName());
 
             if (iter->safepoint() && pushedArgumentSlots_.length()) {
@@ -1470,18 +1471,20 @@ CodeGenerator::generateBody()
             }
 
             if (!iter->accept(this))
                 return false;
         }
         if (masm.oom())
             return false;
 
-        if (counts)
+        if (counts) {
             counts->block(i).setCode(printer.ref().string());
+            masm.setPrinter(NULL);
+        }
     }
 
     JS_ASSERT(pushedArgumentSlots_.empty());
     return true;
 }
 
 // Out-of-line object allocation for LNewArray.
 class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator>
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -270,17 +270,17 @@ class MacroAssembler : public MacroAssem
     }
     void PopRegsInMask(GeneralRegisterSet set) {
         PopRegsInMask(RegisterSet(set, FloatRegisterSet()));
     }
     void PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore);
 
     void branchTestValueTruthy(const ValueOperand &value, Label *ifTrue, FloatRegister fr);
 
-    void branchIfFunctionIsNative(Register fun, Label *label) {
+    void branchIfFunctionHasNoScript(Register fun, Label *label) {
         // 16-bit loads are slow and unaligned 32-bit loads may be too so
         // perform an aligned 32-bit load and adjust the bitmask accordingly.
         JS_STATIC_ASSERT(offsetof(JSFunction, nargs) % sizeof(uint32_t) == 0);
         JS_STATIC_ASSERT(offsetof(JSFunction, flags) == offsetof(JSFunction, nargs) + 2);
         JS_STATIC_ASSERT(IS_LITTLE_ENDIAN);
         Address address(fun, offsetof(JSFunction, nargs));
         uint32_t bit = JSFunction::INTERPRETED << 16;
         branchTest32(Assembler::Zero, address, Imm32(bit), label);
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -45,16 +45,22 @@ ShouldMonitorReturnType(JSFunction *fun)
             !fun->script()->analysis()->ranInference());
 }
 
 bool
 InvokeFunction(JSContext *cx, JSFunction *fun, uint32 argc, Value *argv, Value *rval)
 {
     Value fval = ObjectValue(*fun);
 
+    if (fun->isInterpretedLazy()) {
+        Rooted<JSFunction*> rootedFun(cx, fun);
+        if (!InitializeLazyFunctionScript(cx, rootedFun))
+            return false;
+    }
+
     // In order to prevent massive bouncing between Ion and JM, see if we keep
     // hitting functions that are uncompilable.
     
     if (fun->isInterpreted() && !fun->script()->canIonCompile()) {
         JSScript *script = GetTopIonJSScript(cx);
         if (script->hasIonScript() && ++script->ion->slowCallCount >= js_IonOptions.slowCallLimit) {
             AutoFlushCache afc("InvokeFunction");
 
@@ -84,17 +90,28 @@ InvokeFunction(JSContext *cx, JSFunction
 }
 
 bool
 InvokeConstructor(JSContext *cx, JSObject *obj, uint32 argc, Value *argv, Value *rval)
 {
     Value fval = ObjectValue(*obj);
 
     // See the comment in InvokeFunction.
-    bool needsMonitor = !obj->isFunction() || ShouldMonitorReturnType(obj->toFunction());
+    bool needsMonitor;
+
+    if (obj->isFunction()) {
+        if (obj->toFunction()->isInterpretedLazy()) {
+            Rooted<JSFunction*> rootedFun(cx, obj->toFunction());
+            if (!InitializeLazyFunctionScript(cx, rootedFun))
+                return false;
+        }
+        needsMonitor = ShouldMonitorReturnType(obj->toFunction());
+    } else {
+        needsMonitor = true;
+    }
 
     // Data in the argument vector is arranged for a JIT -> JIT call.
     Value *argvWithoutThis = argv + 1;
 
     bool ok = js::InvokeConstructor(cx, fval, argc, argvWithoutThis, rval);
     if (ok && needsMonitor)
         types::TypeScript::Monitor(cx, *rval);
 
--- a/js/src/ion/arm/Assembler-arm.h
+++ b/js/src/ion/arm/Assembler-arm.h
@@ -1264,16 +1264,20 @@ class Assembler
     template <class Iter>
     static const uint32 * getCF32Target(Iter *iter);
 
     static uintptr_t getPointer(uint8 *);
     template <class Iter>
     static const uint32 * getPtr32Target(Iter *iter, Register *dest = NULL, RelocStyle *rs = NULL);
 
     bool oom() const;
+
+    void setPrinter(Sprinter *sp) {
+    }
+
   private:
     bool isFinished;
   public:
     void finish();
     void executableCopy(void *buffer);
     void processDeferredData(IonCode *code, uint8 *data);
     void processCodeLabels(IonCode *code);
     void copyJumpRelocationTable(uint8 *buffer);
--- a/js/src/ion/arm/Trampoline-arm.cpp
+++ b/js/src/ion/arm/Trampoline-arm.cpp
@@ -1,21 +1,21 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=99:
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ion/IonSpewer.h"
 #include "jscompartment.h"
 #include "assembler/assembler/MacroAssembler.h"
 #include "ion/IonCompartment.h"
 #include "ion/IonLinker.h"
 #include "ion/IonFrames.h"
+#include "ion/IonSpewer.h"
 #include "ion/Bailouts.h"
 #include "ion/VMFunctions.h"
 
 using namespace js;
 using namespace js::ion;
 
 static void
 GenerateReturn(MacroAssembler &masm, int returnCode)
--- a/js/src/ion/shared/Assembler-x86-shared.h
+++ b/js/src/ion/shared/Assembler-x86-shared.h
@@ -123,16 +123,20 @@ class AssemblerX86Shared
 
     bool oom() const {
         return masm.oom() ||
                !enoughMemory_ ||
                jumpRelocations_.oom() ||
                dataRelocations_.oom();
     }
 
+    void setPrinter(Sprinter *sp) {
+        masm.setPrinter(sp);
+    }
+
     void executableCopy(void *buffer);
     void processDeferredData(IonCode *code, uint8 *data);
     void processCodeLabels(IonCode *code);
     void copyJumpRelocationTable(uint8 *buffer);
     void copyDataRelocationTable(uint8 *buffer);
 
     bool addDeferredData(DeferredData *data, size_t bytes) {
         data->setOffset(dataBytesNeeded_);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/self-hosting/alternate-static-and-instance-array-extras.js
@@ -0,0 +1,3 @@
+actual = Array.indexOf([]);
+actual += [].indexOf();
+actual += Array.indexOf([]);
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/self-hosting/for-each-in-over-uncloned-method.js
@@ -0,0 +1,1 @@
+for each(e in [].some) {}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/self-hosting/invoke-self-hosted-function.js
@@ -0,0 +1,9 @@
+var callees = [function a() {}, function b() {}, function c() {}, function d() {}, Array.prototype.forEach];
+
+function f() {
+    for (var i = 0; i < callees.length; ++i) {
+    	callees[i](function(){});
+    }
+}
+
+f();
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/self-hosting/invoke-self-hosted-with-primitive-this.js
@@ -0,0 +1,7 @@
+try {
+	[0,0].sort(Array.some)
+	"".replace(RegExp(), Array.reduce)
+} catch (error) {
+	if (!(error instanceof TypeError && error.message == "0 is not a function"))
+		throw error;
+}
\ No newline at end of file
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -867,16 +867,17 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
 #endif
     inOOMReport(0),
     jitHardening(false),
     ionTop(NULL),
     ionJSContext(NULL),
     ionStackLimit(0),
     ionActivation(NULL),
     ionPcScriptCache(NULL),
+    threadPool(this),
     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
     useHelperThreads_(useHelperThreads)
 {
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&debuggerList);
     JS_INIT_CLIST(&onNewGlobalObjectWatchers);
 
     PodZero(&debugHooks);
@@ -932,16 +933,19 @@ JSRuntime::init(uint32_t maxbytes)
         return false;
 
     if (!stackSpace.init())
         return false;
 
     if (!scriptFilenameTable.init())
         return false;
 
+    if (!threadPool.init())
+        return false;
+
 #ifdef JS_THREADSAFE
     if (!sourceCompressorThread.init())
         return false;
 #endif
 
     if (!evalCache.init())
         return false;
 
@@ -4996,18 +5000,18 @@ JS_DefineFunctions(JSContext *cx, JSObje
             if (!ctor) {
                 ctor = JS_GetConstructor(cx, obj);
                 if (!ctor)
                     return JS_FALSE;
             }
 
             flags &= ~JSFUN_GENERIC_NATIVE;
             JSFunction *fun = js_DefineFunction(cx, ctor, id, js_generic_native_method_dispatcher,
-                                    fs->nargs + 1, flags, NullPtr(),
-                                    JSFunction::ExtendedFinalizeKind);
+                                                fs->nargs + 1, flags,
+                                                JSFunction::ExtendedFinalizeKind);
             if (!fun)
                 return JS_FALSE;
 
             /*
              * As jsapi.h notes, fs must point to storage that lives as long
              * as fun->object lives.
              */
             fun->setExtendedSlot(0, PrivateValue(fs));
@@ -5018,29 +5022,46 @@ JS_DefineFunctions(JSContext *cx, JSObje
          * self-hosted functions, as that means we're currently setting up
          * the global object that that the self-hosted code is then compiled
          * in. Self-hosted functions can access each other via their names,
          * but not via the builtin classes they get installed into.
          */
         if (fs->selfHostedName && cx->runtime->isSelfHostedGlobal(cx->global()))
             return JS_TRUE;
 
-        Rooted<PropertyName*> selfHostedPropertyName(cx);
+        /*
+         * Delay cloning self-hosted functions until they are called. This is
+         * achieved by passing js_DefineFunction a NULL JSNative which
+         * produces an interpreted JSFunction where !hasScript. Interpreted
+         * call paths then call InitializeLazyFunctionScript if !hasScript.
+         */
         if (fs->selfHostedName) {
-            JSAtom *selfHostedAtom = Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName));
-            if (!selfHostedAtom)
+            RootedFunction fun(cx, js_DefineFunction(cx, obj, id, /* native = */ NULL, fs->nargs, 0,
+                                                     JSFunction::ExtendedFinalizeKind));
+            if (!fun)
+                return JS_FALSE;
+            JSFunction::setSingletonType(cx, fun);
+            fun->setIsSelfHostedBuiltin();
+            fun->setExtendedSlot(0, PrivateValue(fs));
+            RootedAtom shAtom(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
+            if (!shAtom)
                 return JS_FALSE;
-            selfHostedPropertyName = selfHostedAtom->asPropertyName();
+            RootedObject holder(cx, cx->global()->intrinsicsHolder());
+            if (!JS_DefinePropertyById(cx,holder, AtomToId(shAtom),
+                                       ObjectValue(*fun), NULL, NULL, 0))
+            {
+                return JS_FALSE;
+            }
+        } else {
+            JSFunction *fun = js_DefineFunction(cx, obj, id, fs->call.op, fs->nargs, flags);
+            if (!fun)
+                return JS_FALSE;
+            if (fs->call.info)
+                fun->setJitInfo(fs->call.info);
         }
-        JSFunction *fun = js_DefineFunction(cx, obj, id, fs->call.op, fs->nargs, flags,
-                                            selfHostedPropertyName);
-        if (!fun)
-            return JS_FALSE;
-        if (fs->call.info)
-            fun->setJitInfo(fs->call.info);
     }
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_DefineFunction(JSContext *cx, JSObject *objArg, const char *name, JSNative call,
                   unsigned nargs, unsigned attrs)
 {
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -84,16 +84,18 @@
 #include "jsgc.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsscope.h"
 #include "jswrapper.h"
+#include "vm/threadpool.h"
+#include "vm/forkjoin.h"
 #include "methodjit/MethodJIT.h"
 #include "methodjit/StubCalls.h"
 #include "methodjit/StubCalls-inl.h"
 
 #include "gc/Marking.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/NumericConversions.h"
 #include "vm/StringBuffer.h"
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -293,31 +293,62 @@ intrinsic_ThrowError(JSContext *cx, unsi
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(args.length() >= 1);
     uint32_t errorNumber = args[0].toInt32();
 
     char *errorArgs[3] = {NULL, NULL, NULL};
     for (unsigned i = 1; i < 4 && i < args.length(); i++) {
         RootedValue val(cx, args[i]);
-        if (val.isInt32() || val.isString()) {
+        if (val.isInt32()) {
+            JSString *str = ToString(cx, val);
+            if (!str)
+                return false;
+            errorArgs[i - 1] = JS_EncodeString(cx, str);
+        } else if (val.isString()) {
             errorArgs[i - 1] = JS_EncodeString(cx, ToString(cx, val));
         } else {
-            ptrdiff_t spIndex = cx->stack.spIndexOf(val.address());
-            errorArgs[i - 1] = DecompileValueGenerator(cx, spIndex, val, NullPtr(), 1);
+            errorArgs[i - 1] = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr());
         }
+        if (!errorArgs[i - 1])
+            return false;
     }
 
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber,
                          errorArgs[0], errorArgs[1], errorArgs[2]);
     for (unsigned i = 0; i < 3; i++)
         js_free(errorArgs[i]);
     return false;
 }
 
+/*
+ * Used to decompile values in the nearest non-builtin stack frame, falling
+ * back to decompiling in the current frame. Helpful for printing higher-order
+ * function arguments.
+ * 
+ * The user must supply the argument number of the value in question; it
+ * _cannot_ be automatically determined.
+ */
+static JSBool
+intrinsic_DecompileArg(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(args.length() == 2);
+
+    RootedValue value(cx, args[1]);
+    ScopedFreePtr<char> str(DecompileArgument(cx, args[0].toInt32(), value));
+    if (!str)
+        return false;
+    RootedAtom atom(cx, Atomize(cx, str, strlen(str)));
+    if (!atom)
+        return false;
+    args.rval().setString(atom);
+    return true;
+}
+
 static JSBool
 intrinsic_MakeConstructible(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(args.length() >= 1);
     JS_ASSERT(args[0].isObject());
     RootedObject obj(cx, &args[0].toObject());
     JS_ASSERT(obj->isFunction());
@@ -326,16 +357,17 @@ intrinsic_MakeConstructible(JSContext *c
 }
 
 JSFunctionSpec intrinsic_functions[] = {
     JS_FN("ToObject",           intrinsic_ToObject,             1,0),
     JS_FN("ToInteger",          intrinsic_ToInteger,            1,0),
     JS_FN("IsCallable",         intrinsic_IsCallable,           1,0),
     JS_FN("ThrowError",         intrinsic_ThrowError,           4,0),
     JS_FN("_MakeConstructible", intrinsic_MakeConstructible,    1,0),
+    JS_FN("_DecompileArg",      intrinsic_DecompileArg,         2,0),
     JS_FS_END
 };
 bool
 JSRuntime::initSelfHosting(JSContext *cx)
 {
     JS_ASSERT(!selfHostedGlobal_);
     RootedObject savedGlobal(cx, JS_GetGlobalObject(cx));
     if (!(selfHostedGlobal_ = JS_NewGlobalObject(cx, &self_hosting_global_class, NULL)))
@@ -376,52 +408,69 @@ JSRuntime::initSelfHosting(JSContext *cx
 }
 
 void
 JSRuntime::markSelfHostedGlobal(JSTracer *trc)
 {
     MarkObjectRoot(trc, &selfHostedGlobal_, "self-hosting global");
 }
 
-JSFunction *
-JSRuntime::getSelfHostedFunction(JSContext *cx, Handle<PropertyName*> name)
+bool
+JSRuntime::getUnclonedSelfHostedValue(JSContext *cx, Handle<PropertyName*> name,
+                                      MutableHandleValue vp)
 {
-    RootedObject holder(cx, cx->global()->getIntrinsicsHolder());
-    RootedId id(cx, NameToId(name));
-    RootedValue funVal(cx, NullValue());
-    if (!cloneSelfHostedValueById(cx, id, holder, &funVal))
-        return NULL;
-    return funVal.toObject().toFunction();
+    RootedObject shg(cx, selfHostedGlobal_);
+    AutoCompartment ac(cx, shg);
+    return JS_GetPropertyById(cx, shg, NameToId(name), vp.address());
 }
 
 bool
-JSRuntime::cloneSelfHostedValueById(JSContext *cx, HandleId id, HandleObject holder, MutableHandleValue vp)
+JSRuntime::cloneSelfHostedFunctionScript(JSContext *cx, Handle<PropertyName*> name,
+                                         Handle<JSFunction*> targetFun)
 {
-    Value funVal;
-    {
-        RootedObject shg(cx, selfHostedGlobal_);
-        AutoCompartment ac(cx, shg);
-        if (!JS_GetPropertyById(cx, shg, id, &funVal) || !funVal.isObject())
-            return false;
-    }
+    RootedValue funVal(cx);
+    if (!getUnclonedSelfHostedValue(cx, name, &funVal))
+        return false;
+
+    RootedFunction sourceFun(cx, funVal.toObject().toFunction());
+    Rooted<JSScript*> sourceScript(cx, sourceFun->script());
+    JS_ASSERT(!sourceScript->enclosingStaticScope());
+    RawScript cscript = CloneScript(cx, NullPtr(), targetFun, sourceScript);
+    if (!cscript)
+        return false;
+    targetFun->setScript(cscript);
+    cscript->setFunction(targetFun);
+    JS_ASSERT(sourceFun->nargs == targetFun->nargs);
+    targetFun->flags = sourceFun->flags | JSFunction::EXTENDED;
+    return true;
+}
+
+bool
+JSRuntime::cloneSelfHostedValue(JSContext *cx, Handle<PropertyName*> name, HandleObject holder,
+                                MutableHandleValue vp)
+{
+    RootedValue funVal(cx);
+    if (!getUnclonedSelfHostedValue(cx, name, &funVal))
+        return false;
 
     /*
      * We don't clone if we're operating in the self-hosting global, as that
      * means we're currently executing the self-hosting script while
      * initializing the runtime (see JSRuntime::initSelfHosting).
      */
     if (cx->global() == selfHostedGlobal_) {
-        vp.set(ObjectValue(funVal.toObject()));
-    } else {
-        RootedObject clone(cx, JS_CloneFunctionObject(cx,  &funVal.toObject(), cx->global()));
+        vp.set(funVal);
+    } else if (funVal.toObject().isFunction()){
+        RootedFunction fun(cx, funVal.toObject().toFunction());
+        RootedObject clone(cx, CloneFunctionObject(cx, fun, cx->global(), fun->getAllocKind()));
         if (!clone)
             return false;
         vp.set(ObjectValue(*clone));
     }
-    DebugOnly<bool> ok = JS_DefinePropertyById(cx, holder, id, vp, NULL, NULL, 0);
+    DebugOnly<bool> ok = JS_DefinePropertyById(cx, holder, NameToId(name), vp, NULL, NULL, 0);
     JS_ASSERT(ok);
     return true;
 }
 
 JSContext *
 js::NewContext(JSRuntime *rt, size_t stackChunkSize)
 {
     JS_AbortIfWrongThread(rt);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -21,16 +21,17 @@
 #include "jsatom.h"
 #include "jsclist.h"
 #include "jsgc.h"
 #include "jspropertycache.h"
 #include "jspropertytree.h"
 #include "jsprototypes.h"
 #include "jsutil.h"
 #include "prmjtime.h"
+#include "vm/threadpool.h"
 
 #include "ds/LifoAlloc.h"
 #include "gc/Statistics.h"
 #include "js/HashTable.h"
 #include "js/Vector.h"
 #include "vm/Stack.h"
 #include "vm/SPSProfiler.h"
 
@@ -539,19 +540,22 @@ struct JSRuntime : js::RuntimeFriendFiel
         return ionRuntime_ ? ionRuntime_ : createIonRuntime(cx);
     }
 
     bool initSelfHosting(JSContext *cx);
     void markSelfHostedGlobal(JSTracer *trc);
     bool isSelfHostedGlobal(js::HandleObject global) {
         return global == selfHostedGlobal_;
     }
-    JSFunction *getSelfHostedFunction(JSContext *cx, js::Handle<js::PropertyName*> name);
-    bool cloneSelfHostedValueById(JSContext *cx, js::HandleId id, js::HandleObject holder,
-                                  js::MutableHandleValue vp);
+    bool getUnclonedSelfHostedValue(JSContext *cx, js::Handle<js::PropertyName*> name,
+                                    js::MutableHandleValue vp);
+    bool cloneSelfHostedFunctionScript(JSContext *cx, js::Handle<js::PropertyName*> name,
+                                       js::Handle<JSFunction*> targetFun);
+    bool cloneSelfHostedValue(JSContext *cx, js::Handle<js::PropertyName*> name,
+                              js::HandleObject holder, js::MutableHandleValue vp);
 
     /* Base address of the native stack for the current thread. */
     uintptr_t           nativeStackBase;
 
     /* The native stack size limit that runtime should not exceed. */
     size_t              nativeStackQuota;
 
     /*
@@ -1017,16 +1021,18 @@ struct JSRuntime : js::RuntimeFriendFiel
     }
 
     // This points to the most recent Ion activation running on the thread.
     js::ion::IonActivation  *ionActivation;
 
     // Cache for ion::GetPcScript().
     js::ion::PcScriptCache *ionPcScriptCache;
 
+    js::ThreadPool threadPool;
+
   private:
     // In certain cases, we want to optimize certain opcodes to typed instructions,
     // to avoid carrying an extra register to feed into an unbox. Unfortunately,
     // that's not always possible. For example, a GetPropertyCacheT could return a
     // typed double, but if it takes its out-of-line path, it could return an
     // object, and trigger invalidation. The invalidation bailout will consider the
     // return value to be a double, and create a garbage Value.
     //
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -397,17 +397,17 @@ js::DefineFunctionWithReserved(JSContext
     RootedObject obj(cx, objArg);
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return NULL;
     Rooted<jsid> id(cx, AtomToId(atom));
-    return js_DefineFunction(cx, obj, id, call, nargs, attrs, NullPtr(), JSFunction::ExtendedFinalizeKind);
+    return js_DefineFunction(cx, obj, id, call, nargs, attrs, JSFunction::ExtendedFinalizeKind);
 }
 
 JS_FRIEND_API(JSFunction *)
 js::NewFunctionWithReserved(JSContext *cx, JSNative native, unsigned nargs, unsigned flags,
                             JSObject *parentArg, const char *name)
 {
     RootedObject parent(cx, parentArg);
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -315,17 +315,18 @@ fun_resolve(JSContext *cx, HandleObject 
         return true;
     }
 
     if (JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().name)) {
         JS_ASSERT(!IsInternalFunctionObject(obj));
 
         RootedValue v(cx);
         if (JSID_IS_ATOM(id, cx->names().length)) {
-            uint16_t defaults = fun->isInterpreted() ? fun->script()->ndefaults : 0;
+            //FIXME: bug 810715 - deal with lazy interpreted functions with default args
+            uint16_t defaults = fun->hasScript() ? fun->script()->ndefaults : 0;
             v.setInt32(fun->nargs - defaults - fun->hasRest());
         } else {
             v.setString(fun->atom() == NULL ?  cx->runtime->emptyString : fun->atom());
         }
 
         if (!DefineNativeProperty(cx, fun, id, v, JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
             return false;
@@ -338,16 +339,18 @@ fun_resolve(JSContext *cx, HandleObject 
         const uint16_t offset = poisonPillProps[i];
 
         if (JSID_IS_ATOM(id, OFFSET_TO_NAME(cx->runtime, offset))) {
             JS_ASSERT(!IsInternalFunctionObject(fun));
 
             PropertyOp getter;
             StrictPropertyOp setter;
             unsigned attrs = JSPROP_PERMANENT;
+            if (fun->isInterpretedLazy() && !InitializeLazyFunctionScript(cx, fun))
+                return false;
             if (fun->isInterpreted() ? fun->inStrictMode() : fun->isBoundFunction()) {
                 JSObject *throwTypeError = fun->global().getThrowTypeError();
 
                 getter = CastAsPropertyOp(throwTypeError);
                 setter = CastAsStrictPropertyOp(throwTypeError);
                 attrs |= JSPROP_GETTER | JSPROP_SETTER;
             } else {
                 getter = fun_getProperty;
@@ -462,16 +465,30 @@ js::CloneInterpretedFunction(JSContext *
     if (!JSFunction::setTypeForScriptedFunction(cx, clone))
         return NULL;
 
     RootedScript cloneScript(cx, clone->script());
     js_CallNewScriptHook(cx, cloneScript, clone);
     return clone;
 }
 
+bool
+js::InitializeLazyFunctionScript(JSContext *cx, HandleFunction fun)
+{
+    JS_ASSERT(fun->isInterpretedLazy());
+    JSFunctionSpec *fs = static_cast<JSFunctionSpec *>(fun->getExtendedSlot(0).toPrivate());
+    RootedAtom funAtom(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
+    if (!funAtom)
+        return false;
+    Rooted<PropertyName *> funName(cx, funAtom->asPropertyName());
+    if (!cx->runtime->cloneSelfHostedFunctionScript(cx, funName, fun))
+        return false;
+    return true;
+}
+
 /*
  * [[HasInstance]] internal method for Function objects: fetch the .prototype
  * property of its 'this' parameter, and walks the prototype chain of v (only
  * if v is an object) returning true if .prototype is found.
  */
 static JSBool
 fun_hasInstance(JSContext *cx, HandleObject objArg, MutableHandleValue v, JSBool *bp)
 {
@@ -512,17 +529,17 @@ JSFunction::trace(JSTracer *trc)
         MarkValueRange(trc, ArrayLength(toExtended()->extendedSlots),
                        toExtended()->extendedSlots, "nativeReserved");
     }
 
     if (atom_)
         MarkString(trc, &atom_, "atom");
 
     if (isInterpreted()) {
-        if (u.i.script_)
+        if (hasScript())
             MarkScriptUnbarriered(trc, &u.i.script_, "script");
         if (u.i.env_)
             MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope");
     }
 }
 
 static void
 fun_trace(JSTracer *trc, RawObject obj)
@@ -603,26 +620,27 @@ FindBody(JSContext *cx, HandleFunction f
 
 JSString *
 js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen)
 {
     AssertCanGC();
     StringBuffer out(cx);
     RootedScript script(cx);
 
-    if (fun->isInterpreted())
+    if (fun->hasScript()) {
         script = fun->script();
-
-    if (fun->isInterpreted() && script->isGeneratorExp) {
-        if ((!bodyOnly && !out.append("function genexp() {")) ||
-            !out.append("\n    [generator expression]\n") ||
-            (!bodyOnly && !out.append("}"))) {
-            return NULL;
+        if (script->isGeneratorExp) {
+            if ((!bodyOnly && !out.append("function genexp() {")) ||
+                !out.append("\n    [generator expression]\n") ||
+                (!bodyOnly && !out.append("}")))
+            {
+                return NULL;
+            }
+            return out.finishString();
         }
-        return out.finishString();
     }
     if (!bodyOnly) {
         // If we're not in pretty mode, put parentheses around lambda functions.
         if (fun->isInterpreted() && !lambdaParen && fun->isLambda()) {
             if (!out.append("("))
                 return NULL;
         }
         if (!out.append("function "))
@@ -1092,17 +1110,17 @@ fun_isGenerator(JSContext *cx, unsigned 
 
     RawFunction fun;
     if (!IsFunctionObject(vp[1], &fun)) {
         JS_SET_RVAL(cx, vp, BooleanValue(false));
         return true;
     }
 
     bool result = false;
-    if (fun->isInterpreted()) {
+    if (fun->hasScript()) {
         RawScript script = fun->script().get(nogc);
         JS_ASSERT(script->length != 0);
         result = script->isGenerator;
     }
 
     JS_SET_RVAL(cx, vp, BooleanValue(result));
     return true;
 }
@@ -1495,17 +1513,16 @@ js_CloneFunctionObject(JSContext *cx, Ha
         /*
          * Across compartments we have to clone the script for interpreted
          * functions. Cross-compartment cloning only happens via JSAPI
          * (JS_CloneFunctionObject) which dynamically ensures that 'script' has
          * no enclosing lexical scope (only the global scope).
          */
         if (clone->isInterpreted()) {
             RootedScript script(cx, clone->script());
-            JS_ASSERT(script);
             JS_ASSERT(script->compartment() == fun->compartment());
             JS_ASSERT_IF(script->compartment() != cx->compartment,
                          !script->enclosingStaticScope());
 
             RootedObject scope(cx, script->enclosingStaticScope());
 
             clone->mutableScript().init(NULL);
 
@@ -1523,17 +1540,17 @@ js_CloneFunctionObject(JSContext *cx, Ha
             Debugger::onNewScript(cx, script, global);
         }
     }
     return clone;
 }
 
 JSFunction *
 js_DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
-                  unsigned nargs, unsigned flags, Handle<PropertyName*> selfHostedName, AllocKind kind)
+                  unsigned nargs, unsigned flags, AllocKind kind)
 {
     PropertyOp gop;
     StrictPropertyOp sop;
 
     RootedFunction fun(cx);
 
     if (flags & JSFUN_STUB_GSOPS) {
         /*
@@ -1545,33 +1562,23 @@ js_DefineFunction(JSContext *cx, HandleO
         flags &= ~JSFUN_STUB_GSOPS;
         gop = JS_PropertyStub;
         sop = JS_StrictPropertyStub;
     } else {
         gop = NULL;
         sop = NULL;
     }
 
-    /*
-     * To support specifying both native and self-hosted functions using
-     * JSFunctionSpec, js_DefineFunction can be invoked with either native
-     * or selfHostedName set. It is assumed that selfHostedName is set if
-     * native isn't.
-     */
-    if (native) {
-        JS_ASSERT(!selfHostedName);
-        RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL);
-        JSFunction::Flags funFlags = JSAPIToJSFunctionFlags(flags);
-        fun = js_NewFunction(cx, NullPtr(), native, nargs,
-                             funFlags, obj, atom, kind);
-    } else {
-        JS_ASSERT(!cx->runtime->isSelfHostedGlobal(cx->global()));
-        fun = cx->runtime->getSelfHostedFunction(cx, selfHostedName);
-        fun->initAtom(JSID_TO_ATOM(id));
-    }
+    JSFunction::Flags funFlags;
+    if (!native)
+        funFlags = JSFunction::INTERPRETED_LAZY;
+    else
+        funFlags = JSAPIToJSFunctionFlags(flags);
+    RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL);
+    fun = js_NewFunction(cx, NullPtr(), native, nargs, funFlags, obj, atom, kind);
     if (!fun)
         return NULL;
 
     RootedValue funVal(cx, ObjectValue(*fun));
     if (!JSObject::defineGeneric(cx, obj, id, funVal, gop, sop, flags & ~JSFUN_FLAGS_MASK))
         return NULL;
 
     return fun;
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -34,16 +34,17 @@ struct JSFunction : public JSObject
         LAMBDA           = 0x0080,  /* function comes from a FunctionExpression or Function() call
                                        (not a FunctionDeclaration or nonstandard function-statement) */
         SELF_HOSTED      = 0x0100,  /* function is self-hosted builtin and must not be
                                        decompilable nor constructible. */
         SELF_HOSTED_CTOR = 0x0200,  /* function is self-hosted builtin constructor and
                                        must be constructible but not decompilable. */
         HAS_REST         = 0x0400,  /* function has a rest (...) parameter */
         HAS_DEFAULTS     = 0x0800,  /* function has at least one default parameter */
+        INTERPRETED_LAZY = 0x1000,  /* function is interpreted but doesn't have a script yet */
 
         /* Derived Flags values for convenience: */
         NATIVE_FUN = 0,
         INTERPRETED_LAMBDA = INTERPRETED | LAMBDA
     };
 
     static void staticAsserts() {
         JS_STATIC_ASSERT(INTERPRETED == JS_FUNCTION_INTERPRETED_BIT);
@@ -70,25 +71,27 @@ struct JSFunction : public JSObject
         } i;
         void            *nativeOrScript;
     } u;
   private:
     js::HeapPtrAtom  atom_;       /* name for diagnostics and decompiling */
   public:
 
     /* A function can be classified as either native (C++) or interpreted (JS): */
-    bool isInterpreted()            const { return flags & INTERPRETED; }
-    bool isNative()                 const { return !(flags & INTERPRETED); }
+    bool isInterpreted()            const { return flags & (INTERPRETED | INTERPRETED_LAZY); }
+    bool isNative()                 const { return !isInterpreted(); }
 
     /* Possible attributes of a native function: */
     bool isNativeConstructor()      const { return flags & NATIVE_CTOR; }
 
     /* Possible attributes of an interpreted function: */
     bool isHeavyweight()            const { return flags & HEAVYWEIGHT; }
     bool isFunctionPrototype()      const { return flags & IS_FUN_PROTO; }
+    bool isInterpretedLazy()        const { return flags & INTERPRETED_LAZY; }
+    bool hasScript()                const { return isInterpreted() && u.i.script_; }
     bool isExprClosure()            const { return flags & EXPR_CLOSURE; }
     bool hasGuessedAtom()           const { return flags & HAS_GUESSED_ATOM; }
     bool isLambda()                 const { return flags & LAMBDA; }
     bool isSelfHostedBuiltin()      const { return flags & SELF_HOSTED; }
     bool isSelfHostedConstructor()  const { return flags & SELF_HOSTED_CTOR; }
     bool hasRest()                  const { return flags & HAS_REST; }
     bool hasDefaults()              const { return flags & HAS_DEFAULTS; }
 
@@ -141,16 +144,23 @@ struct JSFunction : public JSObject
         flags |= HEAVYWEIGHT;
     }
 
     void setIsExprClosure() {
         JS_ASSERT(!isExprClosure());
         flags |= EXPR_CLOSURE;
     }
 
+    void markNotLazy() {
+        JS_ASSERT(isInterpretedLazy());
+        JS_ASSERT(hasScript());
+        flags |= INTERPRETED;
+        flags &= ~INTERPRETED_LAZY;
+    }
+
     JSAtom *atom() const { return hasGuessedAtom() ? NULL : atom_.get(); }
     inline void initAtom(JSAtom *atom);
     JSAtom *displayAtom() const { return atom_; }
 
     inline void setGuessedAtom(JSAtom *atom);
 
     /* uint16_t representation bounds number of call object dynamic slots. */
     enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
@@ -162,17 +172,17 @@ struct JSFunction : public JSObject
     inline JSObject *environment() const;
     inline void setEnvironment(JSObject *obj);
     inline void initEnvironment(JSObject *obj);
 
     static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
     static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); }
 
     js::Return<JSScript*> script() const {
-        JS_ASSERT(isInterpreted());
+        JS_ASSERT(hasScript());
         return JS::HandleScript::fromMarkedLocation(&u.i.script_);
     }
 
     js::HeapPtrScript &mutableScript() {
         JS_ASSERT(isInterpreted());
         return *(js::HeapPtrScript *)&u.i.script_;
     }
 
@@ -283,17 +293,17 @@ js_NewFunction(JSContext *cx, js::Handle
 
 extern JSFunction * JS_FASTCALL
 js_CloneFunctionObject(JSContext *cx, js::HandleFunction fun,
                        js::HandleObject parent, js::HandleObject proto,
                        js::gc::AllocKind kind = JSFunction::FinalizeKind);
 
 extern JSFunction *
 js_DefineFunction(JSContext *cx, js::HandleObject obj, js::HandleId id, JSNative native,
-                  unsigned nargs, unsigned flags, js::Handle<js::PropertyName*> selfHostedName = JS::NullPtr(),
+                  unsigned nargs, unsigned flags,
                   js::gc::AllocKind kind = JSFunction::FinalizeKind);
 
 namespace js {
 
 /*
  * Function extended with reserved slots for use by various kinds of functions.
  * Most functions do not have these extensions, but enough are that efficient
  * storage is required (no malloc'ed reserved slots).
@@ -329,16 +339,19 @@ JSString *FunctionToString(JSContext *cx
 template<XDRMode mode>
 bool
 XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope,
                        HandleScript enclosingScript, MutableHandleObject objp);
 
 extern JSObject *
 CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction fun);
 
+bool
+InitializeLazyFunctionScript(JSContext *cx, HandleFunction fun);
+
 /*
  * Report an error that call.thisv is not compatible with the specified class,
  * assuming that the method (clasp->name).prototype.<name of callee function>
  * is what was called.
  */
 extern void
 ReportIncompatibleMethod(JSContext *cx, CallReceiver call, Class *clasp);
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1315,16 +1315,22 @@ TypeConstraintCall::newType(JSContext *c
         callee = type.typeObject()->interpretedFunction;
         if (!callee)
             return;
     } else {
         /* Calls on non-objects are dynamically monitored. */
         return;
     }
 
+    if (callee->isInterpretedLazy()) {
+        RootedFunction fun(cx, callee);
+        if (!InitializeLazyFunctionScript(cx, fun))
+            return;
+    }
+
     RootedScript calleeScript(cx, callee->script());
     if (!calleeScript->ensureHasTypes(cx))
         return;
 
     unsigned nargs = callee->nargs;
 
     /* Add bindings for the arguments of the call. */
     for (unsigned i = 0; i < callsite->argumentCount && i < nargs; i++) {
@@ -1392,16 +1398,22 @@ TypeConstraintPropagateThis::newType(JSC
         if (!object->interpretedFunction)
             return;
         callee = object->interpretedFunction;
     } else {
         /* Ignore calls to primitives, these will go through a stub. */
         return;
     }
 
+    if (callee->isInterpretedLazy()) {
+        RootedFunction fun(cx, callee);
+        if (!InitializeLazyFunctionScript(cx, fun))
+            return;
+    }
+
     if (!callee->script()->ensureHasTypes(cx))
         return;
 
     TypeSet *thisTypes = TypeScript::ThisTypes(callee->script().unsafeGet());
     if (this->types)
         this->types->addSubset(cx, thisTypes);
     else
         thisTypes->addType(cx, this->type);
@@ -1465,16 +1477,25 @@ TypeConstraintTransformThis::newType(JSC
     if (type.isUnknown() || type.isAnyObject() || type.isObject() || script_->strictModeCode) {
         target->addType(cx, type);
         return;
     }
 
     RootedScript script(cx, script_);
 
     /*
+     * Builtin scripts do not adhere to normal assumptions about transforming
+     * 'this'.
+     */
+    if (script->function() && script->function()->isSelfHostedBuiltin()) {
+        target->addType(cx, type);
+        return;
+    }
+
+    /*
      * Note: if |this| is null or undefined, the pushed value is the outer window. We
      * can't use script->getGlobalType() here because it refers to the inner window.
      */
     if (!script->compileAndGo ||
         type.isPrimitive(JSVAL_TYPE_NULL) ||
         type.isPrimitive(JSVAL_TYPE_UNDEFINED)) {
         target->addType(cx, Type::UnknownType());
         return;
@@ -5668,16 +5689,22 @@ JSObject::splicePrototype(JSContext *cx,
 
 TypeObject *
 JSObject::makeLazyType(JSContext *cx)
 {
     JS_ASSERT(hasLazyType());
     JS_ASSERT(cx->compartment == compartment());
 
     RootedObject self(cx, this);
+    /* De-lazification of functions can GC, so we need to do it up here. */
+    if (self->isFunction() && self->toFunction()->isInterpretedLazy()) {
+        RootedFunction fun(cx, self->toFunction());
+        if (!InitializeLazyFunctionScript(cx, fun))
+            return NULL;
+    }
     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(getClass());
     Rooted<TaggedProto> proto(cx, getTaggedProto());
     TypeObject *type = cx->compartment->types.newTypeObject(cx, key, proto);
     AutoAssertNoGC nogc;
     if (!type) {
         if (cx->typeInferenceEnabled())
             cx->compartment->types.setPendingNukeTypes(cx);
         return self->type_;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -363,16 +363,19 @@ js::InvokeKernel(JSContext *cx, CallArgs
     }
 
     /* Invoke native functions. */
     RootedFunction fun(cx, callee.toFunction());
     JS_ASSERT_IF(construct, !fun->isNativeConstructor());
     if (fun->isNative())
         return CallJSNative(cx, fun->native(), args);
 
+    if (fun->isInterpretedLazy() && !InitializeLazyFunctionScript(cx, fun))
+        return false;
+
     if (!TypeMonitorCall(cx, args, construct))
         return false;
 
     /* Get pointer to new frame/slots, prepare arguments. */
     InvokeFrameGuard ifg;
     if (!cx->stack.pushInvokeFrame(cx, args, initial, &ifg))
         return false;
 
@@ -2338,16 +2341,18 @@ BEGIN_CASE(JSOP_FUNCALL)
         DO_NEXT_OP(len);
     }
 
     if (!TypeMonitorCall(cx, args, construct))
         goto error;
 
     InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
     bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
+    if (fun->isInterpretedLazy() && !InitializeLazyFunctionScript(cx, fun))
+        goto error;
     RawScript funScript = fun->script().unsafeGet();
     if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, funScript, initial))
         goto error;
 
     SET_SCRIPT(regs.fp()->script());
 #ifdef JS_METHODJIT
     script->resetLoopCount();
 #endif
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -1014,17 +1014,17 @@ class FastInvokeGuard
 #endif
     {
         initFunction(fval);
     }
 
     void initFunction(const Value &fval) {
         if (fval.isObject() && fval.toObject().isFunction()) {
             JSFunction *fun = fval.toObject().toFunction();
-            if (fun->isInterpreted()) {
+            if (fun->hasScript()) {
                 fun_ = fun;
                 script_ = fun->script();
             }
         }
     }
 
     InvokeArgsGuard &args() {
         return args_;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5234,17 +5234,17 @@ dumpValue(const Value &v)
     else if (v.isObject() && v.toObject().isFunction()) {
         JSFunction *fun = v.toObject().toFunction();
         if (fun->displayAtom()) {
             fputs("<function ", stderr);
             FileEscapedString(stderr, fun->displayAtom(), 0);
         } else {
             fputs("<unnamed function", stderr);
         }
-        if (fun->isInterpreted()) {
+        if (fun->hasScript()) {
             JSScript *script = fun->script().get(nogc);
             fprintf(stderr, " (%s:%u)",
                     script->filename ? script->filename : "", script->lineno);
         }
         fprintf(stderr, " at %p>", (void *) fun);
     } else if (v.isObject()) {
         JSObject *obj = &v.toObject();
         Class *clasp = obj->getClass();
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1123,17 +1123,17 @@ js_NewPrinter(JSContext *cx, const char 
     jp->grouped = !!grouped;
     jp->strict = !!strict;
     jp->script = NULL;
     jp->dvgfence = NULL;
     jp->pcstack = NULL;
     jp->fun = fun;
     jp->localNames = NULL;
     jp->decompiledOpcodes = NULL;
-    if (fun && fun->isInterpreted()) {
+    if (fun && fun->hasScript()) {
         if (!SetPrinterLocalNames(cx, fun->script().unsafeGet(), jp)) {
             js_DestroyPrinter(jp);
             return NULL;
         }
     }
     return jp;
 }
 
@@ -6264,21 +6264,110 @@ js::DecompileValueGenerator(JSContext *c
     }
     if (!fallback) {
         if (v.isUndefined())
             return JS_strdup(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
         fallback = js_ValueToSource(cx, v);
         if (!fallback)
             return NULL;
     }
-    size_t length = fallback->length();
-    const jschar *chars = fallback->getChars(cx);
-    if (!chars)
+
+    Rooted<JSStableString *> stable(cx, fallback->ensureStable(cx));
+    if (!stable)
         return NULL;
-    return DeflateString(cx, chars, length);
+    return DeflateString(cx, stable->chars().get(), stable->length());
+}
+
+static bool
+DecompileArgumentFromStack(JSContext *cx, int formalIndex, char **res)
+{
+    JS_ASSERT(formalIndex >= 0);
+
+    *res = NULL;
+
+#ifdef JS_MORE_DETERMINISTIC
+    /* See note in DecompileExpressionFromStack. */
+    return true;
+#endif
+
+    /*
+     * Settle on the nearest script frame, which should be the builtin that
+     * called the intrinsic.
+     */
+    StackIter frameIter(cx);
+    while (!frameIter.done() && !frameIter.isScript())
+        ++frameIter;
+    JS_ASSERT(!frameIter.done());
+
+    /*
+     * Get the second-to-top frame, the caller of the builtin that called the
+     * intrinsic.
+     */
+    ++frameIter;
+
+    /*
+     * If this frame isn't a script, we can't decompile. Even if it is a
+     * script but we popped a call frame during the last bump, assume that we
+     * just came from a frameless native and bail conservatively.
+     */
+    if (frameIter.done() || frameIter.poppedCallDuringSettle() || !frameIter.isScript())
+        return true;
+
+    RootedScript script(cx, frameIter.script());
+    jsbytecode *current = frameIter.pc();
+    RootedFunction fun(cx, frameIter.isFunctionFrame()
+                       ? frameIter.callee()
+                       : NULL);
+
+    JS_ASSERT(script->code <= current && current < script->code + script->length);
+
+    if (current < script->main())
+        return true;
+
+    PCStack pcStack;
+    if (!pcStack.init(cx, script, current))
+        return false;
+
+    uint32_t formalStackIndex = pcStack.depth() - GET_ARGC(current) + formalIndex;
+    if (formalStackIndex >= pcStack.depth())
+        return true;
+
+    ExpressionDecompiler ed(cx, script, fun);
+    if (!ed.init())
+        return false;
+    if (!ed.decompilePC(pcStack[formalStackIndex]))
+        return false;
+
+    return ed.getOutput(res);
+}
+
+char *
+js::DecompileArgument(JSContext *cx, int formalIndex, HandleValue v)
+{
+    AssertCanGC();
+    {
+        char *result;
+        if (!DecompileArgumentFromStack(cx, formalIndex, &result))
+            return NULL;
+        if (result) {
+            if (strcmp(result, "(intermediate value)"))
+                return result;
+            js_free(result);
+        }
+    }
+    if (v.isUndefined())
+        return JS_strdup(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
+    RootedString fallback(cx, js_ValueToSource(cx, v));
+    if (!fallback)
+        return NULL;
+
+    Rooted<JSStableString *> stable(cx, fallback->ensureStable(cx));
+    if (!stable)
+        return NULL;
+    return DeflateString(cx, stable->chars().get(), stable->length());
 }
 
 static char *
 DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *pc)
 {
     JS_ASSERT(script->code <= pc && pc < script->code + script->length);
 
     JSOp op = (JSOp) *pc;
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -358,16 +358,23 @@ namespace js {
  *
  * The caller must call JS_free on the result after a succsesful call.
  */
 char *
 DecompileValueGenerator(JSContext *cx, int spindex, HandleValue v,
                         HandleString fallback, int skipStackHits = 0);
 
 /*
+ * Decompile the formal argument at formalIndex in the nearest non-builtin
+ * stack frame, falling back with converting v to source.
+ */
+char *
+DecompileArgument(JSContext *cx, int formalIndex, HandleValue v);
+
+/*
  * Sprintf, but with unlimited and automatically allocated buffering.
  */
 class Sprinter
 {
   public:
     struct InvariantChecker
     {
         const Sprinter *parent;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1854,30 +1854,28 @@ JSScript::isShortRunning()
            && getMaxLoopCount() < 40
 #endif
            ;
 }
 
 bool
 JSScript::enclosingScriptsCompiledSuccessfully() const
 {
-    AutoAssertNoGC nogc;
-
     /*
      * When a nested script is succesfully compiled, it is eagerly given the
      * static JSFunction of its enclosing script. The enclosing function's
      * 'script' field will be NULL until the enclosing script successfully
      * compiles. Thus, we can detect failed compilation by looking for
      * JSFunctions in the enclosingScope chain without scripts.
      */
     RawObject enclosing = enclosingScope_;
     while (enclosing) {
         if (enclosing->isFunction()) {
             RawFunction fun = enclosing->toFunction();
-            if (!fun->script().get(nogc))
+            if (!fun->hasScript())
                 return false;
             enclosing = fun->script()->enclosingScope_;
         } else {
             enclosing = enclosing->asStaticBlock().enclosingStaticScope();
         }
     }
     return true;
 }
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2361,17 +2361,17 @@ static JSObject *
 LambdaIsGetElem(JSObject &lambda)
 {
     AutoAssertNoGC nogc;
 
     if (!lambda.isFunction())
         return NULL;
 
     JSFunction *fun = lambda.toFunction();
-    if (!fun->isInterpreted())
+    if (!fun->hasScript())
         return NULL;
 
     RawScript script = fun->script().get(nogc);
     jsbytecode *pc = script->code;
 
     /*
      * JSOP_GETALIASEDVAR tells us exactly where to find the base object 'b'.
      * Rule out the (unlikely) possibility of a heavyweight function since it
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -279,16 +279,20 @@ ShouldJaegerCompileCallee(JSContext *cx,
 static inline bool
 UncachedInlineCall(VMFrame &f, InitialFrameFlags initial,
                    void **pret, bool *unjittable, uint32_t argc)
 {
     AssertCanGC();
     JSContext *cx = f.cx;
     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     RootedFunction newfun(cx, args.callee().toFunction());
+
+    if (newfun->isInterpretedLazy() && !InitializeLazyFunctionScript(cx, newfun))
+        return false;
+
     RootedScript newscript(cx, newfun->script());
 
     bool construct = InitialFrameFlagsAreConstructing(initial);
 
     RootedScript fscript(cx, f.script());
     bool newType = construct && cx->typeInferenceEnabled() &&
         types::UseNewType(cx, fscript, f.pc());
 
--- a/js/src/methodjit/Logging.h
+++ b/js/src/methodjit/Logging.h
@@ -80,16 +80,21 @@ struct Profiler {
 
     inline uint32_t time_us() {
         return uint32_t(t_stop - t_start);
     }
 };
 
 #else
 
+static inline bool IsJaegerSpewChannelActive(JaegerSpewChannel channel)
+{
+    return false;
+}
+
 static inline void JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...)
 {
 }
 
 #endif
 
 }
 
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -1245,17 +1245,17 @@ class CallCompiler : public BaseCompiler
             // If the following conditions pass, try to inline a call into
             // an IonMonkey JIT'd function.
             if (!callingNew &&
                 fun &&
                 !ic.hasJMStub() &&
                 !ic.hasIonStub() &&
                 ic.frameSize.isStatic() &&
                 ic.frameSize.staticArgc() <= ion::SNAPSHOT_MAX_NARGS &&
-                fun->script()->hasIonScript())
+                fun->hasScript() && fun->script()->hasIonScript())
             {
                 if (!generateIonStub())
                     THROWV(NULL);
             }
 #endif
             return NULL;
         }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2207,17 +2207,17 @@ Clone(JSContext *cx, unsigned argc, jsva
             JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
             if (!fun)
                 return false;
             funobj = JS_GetFunctionObject(fun);
         }
     }
     if (funobj->compartment() != cx->compartment) {
         JSFunction *fun = funobj->toFunction();
-        if (fun->isInterpreted() && fun->script()->compileAndGo) {
+        if (fun->hasScript() && fun->script()->compileAndGo) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
                                  "function", "compile-and-go");
             return false;
         }
     }
 
     if (argc > 1) {
         if (!JS_ValueToObject(cx, argv[1], parent.address()))
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -328,17 +328,17 @@ class GlobalObject : public JSObject
     JSObject *getOrCreateDateTimeFormatPrototype(JSContext *cx) {
         return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initDateTimeFormatProto);
     }
 
     JSObject *getIteratorPrototype() {
         return &getPrototype(JSProto_Iterator).toObject();
     }
 
-    JSObject *getIntrinsicsHolder() {
+    JSObject *intrinsicsHolder() {
         JS_ASSERT(!getSlotRef(INTRINSICS).isUndefined());
         return &getSlotRef(INTRINSICS).toObject();
     }
 
   private:
     typedef bool (*ObjectInitOp)(JSContext *cx, Handle<GlobalObject*> global);
 
     JSObject *getOrCreateObject(JSContext *cx, unsigned slot, ObjectInitOp init) {
@@ -387,23 +387,18 @@ class GlobalObject : public JSObject
         return HasDataProperty(cx, self, NameToId(name), &fun);
     }
 
     bool getIntrinsicValue(JSContext *cx, PropertyName *name, MutableHandleValue value) {
         RootedObject holder(cx, &getSlotRef(INTRINSICS).toObject());
         RootedId id(cx, NameToId(name));
         if (HasDataProperty(cx, holder, id, value.address()))
             return true;
-        bool ok = cx->runtime->cloneSelfHostedValueById(cx, id, holder, value);
-        if (!ok)
-            return false;
-
-        ok = JS_DefinePropertyById(cx, holder, id, value, NULL, NULL, 0);
-        JS_ASSERT(ok);
-        return true;
+        Rooted<PropertyName*> rootedName(cx, name);
+        return cx->runtime->cloneSelfHostedValue(cx, rootedName, holder, value);
     }
 
     inline RegExpStatics *getRegExpStatics() const;
 
     JSObject *getThrowTypeError() const {
         JS_ASSERT(functionObjectClassesInitialized());
         return &getSlot(THROWTYPEERROR).toObject();
     }
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1297,16 +1297,19 @@ StackIter::startOnSegment(StackSegment *
  *  - a native call's 'callee' argument is clobbered on return while the
  *    CallArgsList element is still visible.
  */
 void
 StackIter::settleOnNewState()
 {
     AutoAssertNoGC nogc;
 
+    /* Reset whether or we popped a call last time we settled. */
+    poppedCallDuringSettle_ = false;
+
     /*
      * There are elements of the calls_ and fp_ chains that we want to skip
      * over so iterate until we settle on one or until there are no more.
      */
     while (true) {
         if (!fp_ && !calls_) {
             if (savedOption_ == GO_THROUGH_SAVED && seg_->prevInContext()) {
                 startOnSegment(seg_->prevInContext());
@@ -1394,24 +1397,26 @@ StackIter::settleOnNewState()
         if (calls_->active() && IsNativeFunction(calls_->calleev())) {
             state_ = NATIVE;
             args_ = *calls_;
             return;
         }
 
         /* Pop the call and keep looking. */
         popCall();
+        poppedCallDuringSettle_ = true;
     }
 }
 
 StackIter::StackIter(JSContext *cx, SavedOption savedOption)
   : perThread_(&cx->runtime->mainThread),
     maybecx_(cx),
     savedOption_(savedOption),
-    script_(cx, NULL)
+    script_(cx, NULL),
+    poppedCallDuringSettle_(false)
 #ifdef JS_ION
     , ionActivations_(cx),
     ionFrames_((uint8_t *)NULL),
     ionInlineFrames_((js::ion::IonFrameIterator*) NULL)
 #endif
 {
 #ifdef JS_METHODJIT
     CompartmentVector &v = cx->runtime->compartments;
@@ -1426,17 +1431,18 @@ StackIter::StackIter(JSContext *cx, Save
         state_ = DONE;
     }
 }
 
 StackIter::StackIter(JSRuntime *rt, StackSegment &seg)
   : perThread_(&rt->mainThread),
     maybecx_(NULL),
     savedOption_(STOP_AT_SAVED),
-    script_(rt, NULL)
+    script_(rt, NULL),
+    poppedCallDuringSettle_(false)
 #ifdef JS_ION
     , ionActivations_(rt),
     ionFrames_((uint8_t *)NULL),
     ionInlineFrames_((js::ion::IonFrameIterator*) NULL)
 #endif
 {
 #ifdef JS_METHODJIT
     CompartmentVector &v = rt->compartments;
@@ -1452,17 +1458,18 @@ StackIter::StackIter(const StackIter &ot
     maybecx_(other.maybecx_),
     savedOption_(other.savedOption_),
     state_(other.state_),
     fp_(other.fp_),
     calls_(other.calls_),
     seg_(other.seg_),
     pc_(other.pc_),
     script_(perThread_, other.script_),
-    args_(other.args_)
+    args_(other.args_),
+    poppedCallDuringSettle_(other.poppedCallDuringSettle_)
 #ifdef JS_ION
     , ionActivations_(other.ionActivations_),
     ionFrames_(other.ionFrames_),
     ionInlineFrames_(other.ionInlineFrames_)
 #endif
 {
 }
 
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1726,16 +1726,18 @@ class StackIter
     StackFrame   *fp_;
     CallArgsList *calls_;
 
     StackSegment *seg_;
     jsbytecode   *pc_;
     RootedScript  script_;
     CallArgs      args_;
 
+    bool          poppedCallDuringSettle_;
+
 #ifdef JS_ION
     ion::IonActivationIterator ionActivations_;
     ion::IonFrameIterator ionFrames_;
     ion::InlineFrameIterator ionInlineFrames_;
 #endif
 
     void poisonRegs();
     void popFrame();
@@ -1755,16 +1757,18 @@ class StackIter
     bool done() const { return state_ == DONE; }
     StackIter &operator++();
 
     bool operator==(const StackIter &rhs) const;
     bool operator!=(const StackIter &rhs) const { return !(*this == rhs); }
 
     JSCompartment *compartment() const;
 
+    bool poppedCallDuringSettle() const { return poppedCallDuringSettle_; }
+
     bool isScript() const {
         JS_ASSERT(!done());
 #ifdef JS_ION
         if (state_ == ION)
             return ionFrames_.isScripted();
 #endif
         return state_ == SCRIPTED;
     }
new file mode 100644
--- /dev/null
+++ b/js/src/vm/forkjoin.cpp
@@ -0,0 +1,496 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * 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 "forkjoin.h"
+#include "monitor.h"
+#include "jscntxt.h"
+#include "jscompartment.h"
+#include "prthread.h"
+#include "forkjoininlines.h"
+
+namespace js {
+
+class ForkJoinShared
+    : public TaskExecutor,
+      public Monitor
+{
+    ////////////////////////////////////////////////////////////////////////
+    // Constant fields
+
+    JSContext *const cx_;          // Current context
+    ThreadPool *const threadPool_; // The thread pool.
+    ForkJoinOp &op_;               // User-defined operations to be perf. in par.
+    const size_t numThreads_;      // Total number of threads.
+    PRCondVar *rendezvousEnd_;     // Cond. var used to signal end of rendezvous.
+
+    ////////////////////////////////////////////////////////////////////////
+    // Per-thread arenas
+    //
+    // Each worker thread gets an arena to use when allocating.
+
+    Vector<gc::ArenaLists *, 16> arenaListss_;
+
+    ////////////////////////////////////////////////////////////////////////
+    // Locked Fields
+    //
+    // Only to be accessed while holding the lock.
+
+    size_t uncompleted_;     // Number of uncompleted worker threads.
+    size_t blocked_;         // Number of threads that have joined the rendezvous.
+    size_t rendezvousIndex_; // Number of rendezvous attempts
+
+    ////////////////////////////////////////////////////////////////////////
+    // Asynchronous Flags
+    //
+    // These can be read without the lock (hence the |volatile| declaration).
+
+    // A thread has bailed and others should follow suit.  Set and
+    // read asynchronously.  After setting abort, workers will acquire
+    // the lock, decrement uncompleted, and then notify if uncompleted
+    // has reached blocked.
+    volatile bool abort_;
+
+    // Set to true when a worker bails for a fatal reason.
+    volatile bool fatal_;
+
+    // A thread has request a rendezvous.  Only *written* with the
+    // lock (in |initiateRendezvous()| and |endRendezvous()|) but may
+    // be *read* without the lock.
+    volatile bool rendezvous_;
+
+    // Invoked only from the main thread:
+    void executeFromMainThread(uintptr_t stackLimit);
+
+    // Executes slice #threadId of the work, either from a worker or
+    // the main thread.
+    void executePortion(PerThreadData *perThread, size_t threadId, uintptr_t stackLimit);
+
+    // Rendezvous protocol:
+    //
+    // Use AutoRendezvous rather than invoking initiateRendezvous()
+    // and endRendezvous() directly.
+
+    friend class AutoRendezvous;
+
+    // Requests that the other threads stop.  Must be invoked from the
+    // main thread.
+    void initiateRendezvous(ForkJoinSlice &threadCx);
+
+    // If a rendezvous has been requested, blocks until the main
+    // thread says we may continue.
+    void joinRendezvous(ForkJoinSlice &threadCx);
+
+    // Permits other threads to resume execution.  Must be invoked
+    // from the main thread after a call to initiateRendezvous().
+    void endRendezvous(ForkJoinSlice &threadCx);
+
+public:
+    ForkJoinShared(JSContext *cx,
+                   ThreadPool *threadPool,
+                   ForkJoinOp &op,
+                   size_t numThreads,
+                   size_t uncompleted);
+    ~ForkJoinShared();
+
+    bool init();
+
+    ParallelResult execute();
+
+    // Invoked from parallel worker threads:
+    virtual void executeFromWorker(size_t threadId, uintptr_t stackLimit);
+
+    // Moves all the per-thread arenas into the main compartment.
+    // This can only safely be invoked on the main thread, either
+    // during a rendezvous or after the workers have completed.
+    void transferArenasToCompartment();
+
+    // Invoked during processing by worker threads to "check in"
+    bool check(ForkJoinSlice &threadCx);
+
+    // See comment on |ForkJoinSlice::setFatal()| in forkjoin.h
+    bool setFatal();
+
+    JSRuntime *runtime() { return cx_->runtime; }
+};
+
+class AutoRendezvous {
+private:
+    ForkJoinSlice &threadCx;
+
+public:
+    AutoRendezvous(ForkJoinSlice &threadCx)
+        : threadCx(threadCx)
+    {
+        threadCx.shared->initiateRendezvous(threadCx);
+    }
+
+    ~AutoRendezvous()
+    {
+        threadCx.shared->endRendezvous(threadCx);
+    }
+};
+
+PRUintn ForkJoinSlice::ThreadPrivateIndex;
+
+class AutoSetForkJoinSlice
+{
+public:
+    AutoSetForkJoinSlice(ForkJoinSlice *threadCx)
+    {
+        PR_SetThreadPrivate(ForkJoinSlice::ThreadPrivateIndex, threadCx);
+    }
+
+    ~AutoSetForkJoinSlice()
+    {
+        PR_SetThreadPrivate(ForkJoinSlice::ThreadPrivateIndex, NULL);
+    }
+};
+
+bool
+ForkJoinSlice::Initialize()
+{
+    PRStatus status = PR_NewThreadPrivateIndex(&ThreadPrivateIndex, NULL);
+    return status == PR_SUCCESS;
+}
+
+ParallelResult ExecuteForkJoinOp(JSContext *cx, ForkJoinOp &op)
+{
+#   ifndef JS_THREADSAFE_ION
+    return TP_RETRY_SEQUENTIALLY;
+#   else
+    JS_ASSERT(!InParallelSection()); // Recursive use of the ThreadPool is not supported.
+
+    ThreadPool *threadPool = &cx->runtime->threadPool;
+    size_t numThreads = threadPool->numWorkers() + 1; // parallel workers plus this main thread
+
+    ForkJoinShared shared(cx, threadPool, op, numThreads, numThreads - 1);
+    if (!shared.init())
+        return TP_RETRY_SEQUENTIALLY;
+
+    return shared.execute();
+#   endif
+}
+
+/****************************************************************************
+ * ForkJoinShared
+ */
+
+ForkJoinShared::ForkJoinShared(JSContext *cx,
+                               ThreadPool *threadPool,
+                               ForkJoinOp &op,
+                               size_t numThreads,
+                               size_t uncompleted)
+    : cx_(cx),
+      threadPool_(threadPool),
+      op_(op),
+      numThreads_(numThreads),
+      arenaListss_(cx),
+      uncompleted_(uncompleted),
+      blocked_(0),
+      rendezvousIndex_(0),
+      abort_(false),
+      fatal_(false),
+      rendezvous_(false)
+{}
+
+bool
+ForkJoinShared::init()
+{
+    // Create temporary arenas to hold the data allocated during the
+    // parallel code.
+    //
+    // Note: you might think (as I did, initially) that we could use
+    // compartment ArenaLists for the main thread.  This is not true,
+    // because when executing parallel code we sometimes check what
+    // arena list an object is in to decide if it is writable.  If we
+    // used the compartment ArenaLists for the main thread, then the
+    // main thread would be permitted to write to any object it wants.
+
+    if (!Monitor::init())
+        return false;
+
+    rendezvousEnd_ = PR_NewCondVar(lock_);
+    if (!rendezvousEnd_)
+        return false;
+
+    for (unsigned i = 0; i < numThreads_; i++) {
+        gc::ArenaLists *arenaLists = cx_->new_<gc::ArenaLists>();
+        if (!arenaLists)
+            return false;
+
+        if (!arenaListss_.append(arenaLists)) {
+            delete arenaLists;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+ForkJoinShared::~ForkJoinShared()
+{
+    PR_DestroyCondVar(rendezvousEnd_);
+
+    while (arenaListss_.length() > 0) {
+        delete arenaListss_.popCopy();
+    }
+}
+
+ParallelResult
+ForkJoinShared::execute()
+{
+    AutoLockMonitor lock(*this);
+
+    // give the task set a chance to prepare for parallel workload
+    if (!op_.pre(numThreads_))
+        return TP_RETRY_SEQUENTIALLY;
+
+    // notify workers to start and execute one portion on this thread
+    {
+        AutoUnlockMonitor unlock(*this);
+        threadPool_->submitAll(this);
+        executeFromMainThread(cx_->runtime->ionStackLimit);
+    }
+
+    // wait for workers to complete
+    while (uncompleted_ > 0)
+        lock.wait();
+
+    // check if any of the workers failed
+    if (abort_) {
+        if (fatal_)
+            return TP_FATAL;
+        else
+            return TP_RETRY_SEQUENTIALLY;
+    }
+
+    transferArenasToCompartment();
+
+    // give task set a chance to cleanup after parallel execution
+    if (!op_.post(numThreads_))
+        return TP_RETRY_SEQUENTIALLY;
+
+    return TP_SUCCESS; // everything went swimmingly. give yourself a pat on the back.
+}
+
+void
+ForkJoinShared::transferArenasToCompartment()
+{
+#if 0
+    // This code will become relevant once other
+    // bugs are merged down.
+
+    JSRuntime *rt = cx_->runtime;
+    JSCompartment *comp = cx_->compartment;
+    for (unsigned i = 0; i < numThreads_; i++) {
+        comp->arenas.adoptArenas(rt, arenaListss_[i]);
+    }
+#endif
+}
+
+void
+ForkJoinShared::executeFromWorker(size_t workerId, uintptr_t stackLimit)
+{
+    JS_ASSERT(workerId < numThreads_ - 1);
+
+    PerThreadData thisThread(cx_->runtime);
+    TlsPerThreadData.set(&thisThread);
+    executePortion(&thisThread, workerId, stackLimit);
+    TlsPerThreadData.set(NULL);
+
+    AutoLockMonitor lock(*this);
+    uncompleted_ -= 1;
+    if (blocked_ == uncompleted_) {
+        // Signal the main thread that we have terminated.  It will be
+        // either working, arranging a rendezvous, or waiting for
+        // workers to complete.
+        lock.notify();
+    }
+}
+
+void
+ForkJoinShared::executeFromMainThread(uintptr_t stackLimit)
+{
+    executePortion(&cx_->runtime->mainThread, numThreads_ - 1, stackLimit);
+}
+
+void
+ForkJoinShared::executePortion(PerThreadData *perThread,
+                               size_t threadId,
+                               uintptr_t stackLimit)
+{
+    gc::ArenaLists *arenaLists = arenaListss_[threadId];
+    ForkJoinSlice slice(perThread, threadId, numThreads_,
+                        stackLimit, arenaLists, this);
+    AutoSetForkJoinSlice autoContext(&slice);
+
+    if (!op_.parallel(slice))
+        abort_ = true;
+}
+
+bool
+ForkJoinShared::setFatal()
+{
+    // Might as well set the abort flag to true, it will make
+    // propagation faster:
+    abort_ = true;
+    fatal_ = true;
+    return false;
+}
+
+bool
+ForkJoinShared::check(ForkJoinSlice &slice)
+{
+    if (abort_)
+        return false;
+
+    if (slice.isMainThread()) {
+        if (cx_->runtime->interrupt) {
+            // If interrupt is requested, bring worker threads to a
+            // halt, service the interrupt, then let them start back
+            // up again.
+            AutoRendezvous autoRendezvous(slice);
+            if (!js_HandleExecutionInterrupt(cx_)) {
+                return setFatal();
+            }
+        }
+    } else if (rendezvous_) {
+        joinRendezvous(slice);
+    }
+
+    return true;
+}
+
+void
+ForkJoinShared::initiateRendezvous(ForkJoinSlice &slice) {
+    /*
+      The rendezvous protocol is always initiated by the main thread.
+      The main thread sets the rendezvous flag to true.  Seeing this
+      flag, other threads will invoke |joinRendezvous()|, which causes
+      them to (1) read |rendezvousIndex| and (2) increment the
+      |blocked| counter.  Once the |blocked| counter is equal to
+      |uncompleted|, all parallel threads have joined the rendezvous,
+      and so the main thread is signaled.  That will cause this
+      function to return.
+
+      Some subtle points:
+
+      - Worker threads may potentially terminate their work before
+        they see the rendezvous flag.  In this case, they would
+        decrement |uncompleted| rather than incrementing |blocked|.
+        Either way, if the two variables become equal, the main thread
+        will be notified
+
+      - The |rendezvousIndex| counter is used to detect the case where
+        the main thread signals the end of the rendezvous and then
+        starts another rendezvous before the workers have a chance to
+        exit.  We circumvent this by having the workers read the
+        |rendezvousIndex| counter as they enter the rendezvous, and
+        then they only block until that counter is incremented.
+        Another alternative would be for the main thread to block in
+        |endRendezvous()| until all workers have exited, but that
+        would be slower and involve unnecessary synchronization.
+
+        Note that the main thread cannot ever get more than one
+        rendezvous ahead of the workers, because it must wait for all
+        of them to enter the rendezvous before it can end it, so the
+        solution of using a counter is perfectly general and we need
+        not fear rollover.
+     */
+
+
+    JS_ASSERT(slice.isMainThread());
+    JS_ASSERT(!rendezvous_ && blocked_ == 0);
+
+    AutoLockMonitor lock(*this);
+
+    // signal other threads we want to start a rendezvous
+    rendezvous_ = true;
+
+    // wait until all the other threads blocked themselves
+    while (blocked_ != uncompleted_) {
+        lock.wait();
+    }
+}
+
+void
+ForkJoinShared::joinRendezvous(ForkJoinSlice &slice) {
+    JS_ASSERT(!slice.isMainThread());
+    JS_ASSERT(rendezvous_);
+
+    AutoLockMonitor lock(*this);
+    const size_t index = rendezvousIndex_;
+    blocked_ += 1;
+
+    // If we're the last to arrive, let the main thread know about it.
+    if (blocked_ == uncompleted_) {
+        lock.notify();
+    }
+
+    // Wait until the main thread terminates the rendezvous.  We use a
+    // separate condition variable here to distinguish between workers
+    // notifying the main thread that they have completed and the main
+    // thread notifying the workers to resume.
+    while (rendezvousIndex_ == index) {
+        PR_WaitCondVar(rendezvousEnd_, PR_INTERVAL_NO_TIMEOUT);
+    }
+}
+
+void
+ForkJoinShared::endRendezvous(ForkJoinSlice &slice) {
+    JS_ASSERT(slice.isMainThread());
+
+    AutoLockMonitor lock(*this);
+    rendezvous_ = false;
+    blocked_ = 0;
+    rendezvousIndex_ += 1;
+
+    // signal other threads that rendezvous is over
+    PR_NotifyAllCondVar(rendezvousEnd_);
+}
+
+/****************************************************************************
+ * ForkJoinSlice
+ */
+
+ForkJoinSlice::ForkJoinSlice(PerThreadData *perThreadData,
+                             size_t sliceId, size_t numSlices,
+                             uintptr_t stackLimit, gc::ArenaLists *arenaLists,
+                             ForkJoinShared *shared)
+    : perThreadData(perThreadData),
+      sliceId(sliceId),
+      numSlices(numSlices),
+      ionStackLimit(stackLimit),
+      arenaLists(arenaLists),
+      shared(shared)
+{}
+
+bool
+ForkJoinSlice::isMainThread()
+{
+    return perThreadData == &shared->runtime()->mainThread;
+}
+
+JSRuntime *
+ForkJoinSlice::runtime()
+{
+    return shared->runtime();
+}
+
+bool
+ForkJoinSlice::check()
+{
+    return shared->check(*this);
+}
+
+bool
+ForkJoinSlice::setFatal()
+{
+    return shared->setFatal();
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/forkjoin.h
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * 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 jstaskset_h___
+#define jstaskset_h___
+
+#include "threadpool.h"
+
+/*
+ * ForkJoin
+ *
+ * This is the building block for executing multi-threaded JavaScript
+ * with shared memory (as distinct from Web Workers).  The idea is
+ * that you have some (typically data-parallel) operation which you
+ * wish to execute in parallel across as many threads as you have
+ * available.  An example might be applying |map()| to a vector in
+ * parallel. To implement such a thing, you would define a subclass of
+ * |ForkJoinOp| to implement the operation and then invoke
+ * |ExecuteForkJoinOp()|, as follows:
+ *
+ * > class MyForkJoinOp {
+ * >   ... define callbacks as appropriate for your operation ...
+ * > };
+ * > MyForkJoinOp op;
+ * > ExecuteForkJoinOp(cx, op);
+ *
+ * |ExecuteForkJoinOp()| will fire up the workers in the runtime's
+ * thread pool, have them execute the callbacks defined in the
+ * |ForkJoinOp| class, and then return once all the workers have
+ * completed.
+ *
+ * There are three callbacks defined in |ForkJoinOp|.  The first,
+ * |pre()|, is invoked before the parallel section begins.  It informs
+ * you how many slices your problem will be divided into (effectively,
+ * how many worker threads there will be).  This is often useful for
+ * allocating an array for the workers to store their result or
+ * something like that.
+ *
+ * Next, you will receive |N| calls to the |parallel()| callback,
+ * where |N| is the number of slices that were specified in |pre()|.
+ * Each callback will be supplied with a |ForkJoinSlice| instance
+ * providing some context.
+ *
+ * Typically there will be one call to |parallel()| from each worker
+ * thread, but that is not something you should rely upon---if we
+ * implement work-stealing, for example, then it could be that a
+ * single worker thread winds up handling multiple slices.
+ *
+ * Finally, after the operation is complete the |post()| callback is
+ * invoked, giving you a chance to collect the various results.
+ *
+ * Operation callback:
+ *
+ * During parallel execution, you should periodically invoke
+ * |slice.check()|, which will handle the operation callback.  If the
+ * operation callback is necessary, |slice.check()| will arrange a
+ * rendezvous---that is, as each active worker invokes |check()|, it
+ * will come to a halt until everyone is blocked (Stop The World).  At
+ * this point, we perform the callback on the main thread, and then
+ * resume execution.  If a worker thread terminates before calling
+ * |check()|, that's fine too.  We assume that you do not do unbounded
+ * work without invoking |check()|.
+ *
+ * Sequential Fallback:
+ *
+ * It is assumed that anyone using this API must be prepared for a
+ * sequential fallback.  Therefore, the |ExecuteForkJoinOp()| returns
+ * a status code indicating whether a fatal error occurred (in which
+ * case you should just stop) or whether you should retry the
+ * operation, but executing sequentially.  An example of where the
+ * fallback would be useful is if the parallel code encountered an
+ * unexpected path that cannot safely be executed in parallel (writes
+ * to shared state, say).
+ *
+ * Current Limitations:
+ *
+ * - The API does not support recursive or nested use.  That is, the
+ *   |parallel()| callback of a |ForkJoinOp| may not itself invoke
+ *   |ExecuteForkJoinOp()|.  We may lift this limitation in the
+ *   future.
+ *
+ * - No load balancing is performed between worker threads.  That
+ *   means that the fork-join system is best suited for problems that
+ *   can be slice into uniform bits.
+ */
+
+namespace js {
+
+// Parallel operations in general can have one of three states.  They
+// may succeed, fail, or "bail", where bail indicates that the code
+// encountered an unexpected condition and should be re-run
+// sequentially.
+enum ParallelResult { TP_SUCCESS, TP_RETRY_SEQUENTIALLY, TP_FATAL };
+
+struct ForkJoinOp;
+
+// Executes the given |TaskSet| in parallel using the runtime's
+// |ThreadPool|, returning upon completion.  In general, if there are
+// |N| workers in the threadpool, the problem will be divided into
+// |N+1| slices, as the main thread will also execute one slice.
+ParallelResult ExecuteForkJoinOp(JSContext *cx, ForkJoinOp &op);
+
+class ForkJoinShared;
+class AutoRendezvous;
+class AutoSetForkJoinSlice;
+namespace gc { struct ArenaLists; }
+
+struct ForkJoinSlice
+{
+public:
+    // PerThreadData corresponding to the current worker thread.
+    PerThreadData *perThreadData;
+
+    // Which slice should you process? Ranges from 0 to |numSlices|.
+    const size_t sliceId;
+
+    // How many slices are there in total?
+    const size_t numSlices;
+
+    // Top of the stack.  This should move into |perThreadData|.
+    uintptr_t ionStackLimit;
+
+    // Arenas to use when allocating on this thread.  See
+    // |ion::ParFunctions::ParNewGCThing()|.  This should move
+    // into |perThreadData|.
+    gc::ArenaLists *const arenaLists;
+
+    ForkJoinSlice(PerThreadData *perThreadData, size_t sliceId, size_t numSlices,
+                  uintptr_t stackLimit, gc::ArenaLists *arenaLists,
+                  ForkJoinShared *shared);
+
+    // True if this is the main thread, false if it is one of the parallel workers
+    bool isMainThread();
+
+    // Generally speaking, if a thread returns false, that is
+    // interpreted as a "bailout"---meaning, a recoverable error.  If
+    // however you call this function before returning false, then the
+    // error will be interpreted as *fatal*.  This doesn't strike me
+    // as the most elegant solution here but I don't know what'd be better.
+    //
+    // For convenience, *always* returns false.
+    bool setFatal();
+
+    // During the parallel phase, this method should be invoked
+    // periodically, for example on every backedge, similar to the
+    // interrupt check.  If it returns false, then the parallel phase
+    // has been aborted and so you should bailout.  The function may
+    // also rendesvous to perform GC or do other similar things.
+    bool check();
+
+    // Be wary, the runtime is shared between all threads!
+    JSRuntime *runtime();
+
+    static inline ForkJoinSlice *current();
+    static bool Initialize();
+
+private:
+    friend class AutoRendezvous;
+    friend class AutoSetForkJoinSlice;
+
+    static PRUintn ThreadPrivateIndex; // initialized by Initialize()
+
+    ForkJoinShared *const shared;
+};
+
+// Generic interface for specifying divisible operations that can be
+// executed in a fork-join fashion.
+struct ForkJoinOp
+{
+public:
+    // Invoked before parallel phase begins; informs the task set how
+    // many slices there will be and gives it a chance to initialize
+    // per-slice data structures.
+    //
+    // Returns true on success, false to halt parallel execution.
+    virtual bool pre(size_t numSlices) = 0;
+
+    // Invoked from each parallel thread to process one slice.  The
+    // |ForkJoinSlice| which is supplied will also be available using
+    // TLS.
+    //
+    // Returns true on success, false to halt parallel execution.
+    virtual bool parallel(ForkJoinSlice &slice) = 0;
+
+    // Invoked after parallel phase ends if execution was successful
+    // (not aborted)
+    //
+    // Returns true on success, false to halt parallel execution.
+    virtual bool post(size_t numSlices) = 0;
+};
+
+/* True if this thread is currently executing a ParallelArray
+   operation across multiple threads. */
+static inline bool InParallelSection() {
+#   ifdef JS_THREADSAFE_ION
+    return ForkJoinSlice::current() != NULL;
+#   else
+    return false;
+#   endif
+}
+
+#endif
+
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/forkjoininlines.h
@@ -0,0 +1,12 @@
+namespace js {
+
+ForkJoinSlice *
+ForkJoinSlice::current() {
+#ifdef JS_THREADSAFE_ION
+    return (ForkJoinSlice*) PR_GetThreadPrivate(ThreadPrivateIndex);
+#else
+    return NULL;
+#endif
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/monitor.cpp
@@ -0,0 +1,30 @@
+#include "vm/monitor.h"
+
+namespace js {
+
+Monitor::Monitor()
+    : lock_(NULL), condVar_(NULL)
+{
+}
+
+Monitor::~Monitor()
+{
+    PR_DestroyLock(lock_);
+    PR_DestroyCondVar(condVar_);
+}
+
+bool
+Monitor::init()
+{
+    lock_ = PR_NewLock();
+    if (!lock_)
+        return false;
+
+    condVar_ = PR_NewCondVar(lock_);
+    if (!condVar_)
+        return false;
+
+    return true;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/monitor.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * 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 jsmonitor_h___
+#define jsmonitor_h___
+
+#include <stdlib.h>
+#include "mozilla/Util.h"
+#include "js/Utility.h"
+#include "prlock.h"
+#include "prcvar.h"
+
+namespace js {
+
+/*
+ * A base class used for types intended to be used in a parallel
+ * fashion, such as the workers in the |ThreadPool| class.  Combines a
+ * lock and a condition variable.  You can acquire the lock or signal
+ * the condition variable using the |AutoLockMonitor| type.
+ */
+class Monitor
+{
+protected:
+    friend class AutoLockMonitor;
+    friend class AutoUnlockMonitor;
+
+    PRLock *lock_;
+    PRCondVar *condVar_;
+
+public:
+    Monitor();
+    ~Monitor();
+
+    bool init();
+};
+
+class AutoLockMonitor
+{
+private:
+    Monitor &monitor;
+
+public:
+    AutoLockMonitor(Monitor &monitor) : monitor(monitor) {
+        PR_Lock(monitor.lock_);
+    }
+
+    ~AutoLockMonitor() {
+        PR_Unlock(monitor.lock_);
+    }
+
+    void wait() {
+        mozilla::DebugOnly<PRStatus> status =
+          PR_WaitCondVar(monitor.condVar_, PR_INTERVAL_NO_TIMEOUT);
+        JS_ASSERT(status == PR_SUCCESS);
+    }
+
+    void notify() {
+        PR_NotifyCondVar(monitor.condVar_);
+    }
+
+    void notifyAll() {
+        PR_NotifyAllCondVar(monitor.condVar_);
+    }
+};
+
+class AutoUnlockMonitor
+{
+  private:
+    Monitor &monitor;
+
+  public:
+    AutoUnlockMonitor(Monitor &monitor) : monitor(monitor) { PR_Unlock(monitor.lock_); }
+    ~AutoUnlockMonitor() { PR_Lock(monitor.lock_); }
+};
+
+}
+
+#endif /* ndef jsmonitor_h___ */
new file mode 100644
--- /dev/null
+++ b/js/src/vm/threadpool.cpp
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- */
+/* vim: set ts=4 sw=4 et tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jscntxt.h"
+#include "jslock.h"
+#include "vm/threadpool.h"
+#include "prthread.h"
+#include "monitor.h"
+
+namespace js {
+
+/****************************************************************************
+ * ThreadPoolWorker
+ *
+ * Each |ThreadPoolWorker| just hangs around waiting for items to be added
+ * to its |worklist_|.  Whenever something is added, it gets executed.
+ * Once the worker's state is set to |TERMINATING|, the worker will
+ * exit as soon as its queue is empty.
+ */
+
+#define WORKER_THREAD_STACK_SIZE (1*1024*1024)
+
+enum WorkerState {
+    CREATED, ACTIVE, TERMINATING, TERMINATED
+};
+
+class ThreadPoolWorker : public Monitor
+{
+    const size_t workerId_;
+    ThreadPool *const threadPool_;
+
+    /* Currrent point in the worker's lifecycle.
+     *
+     * Modified only while holding the ThreadPoolWorker's lock */
+    WorkerState state_;
+
+    /* Worklist for this thread.
+     *
+     * Modified only while holding the ThreadPoolWorker's lock */
+    js::Vector<TaskExecutor*, 4, SystemAllocPolicy> worklist_;
+
+    /* The thread's main function */
+    static void ThreadMain(void *arg);
+    void run();
+
+public:
+    ThreadPoolWorker(size_t workerId, ThreadPool *tp);
+    ~ThreadPoolWorker();
+
+    bool init();
+
+    /* Invoked from main thread; signals worker to start */
+    bool start();
+
+    /* Submit work to be executed. If this returns true, you are
+       guaranteed that the task will execute before the thread-pool
+       terminates (barring an infinite loop in some prior task) */
+    bool submit(TaskExecutor *task);
+
+    /* Invoked from main thread; signals worker to terminate
+     * and blocks until termination completes */
+    void terminate();
+};
+
+ThreadPoolWorker::ThreadPoolWorker(size_t workerId, ThreadPool *tp)
+    : workerId_(workerId), threadPool_(tp), state_(CREATED), worklist_()
+{}
+
+ThreadPoolWorker::~ThreadPoolWorker()
+{}
+
+bool
+ThreadPoolWorker::init()
+{
+    return Monitor::init();
+}
+
+bool
+ThreadPoolWorker::start()
+{
+    JS_ASSERT(state_ == CREATED);
+
+    // Set state to active now, *before* the thread starts:
+    state_ = ACTIVE;
+
+    if (!PR_CreateThread(PR_USER_THREAD,
+                         ThreadMain, this,
+                         PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+                         PR_UNJOINABLE_THREAD,
+                         WORKER_THREAD_STACK_SIZE))
+    {
+        // If the thread failed to start, call it TERMINATED.
+        state_ = TERMINATED;
+        return false;
+    }
+
+    return true;
+}
+
+void
+ThreadPoolWorker::ThreadMain(void *arg)
+{
+    ThreadPoolWorker *thread = (ThreadPoolWorker*) arg;
+    thread->run();
+}
+
+void
+ThreadPoolWorker::run()
+{
+    // This is hokey in the extreme.  To compute the stack limit,
+    // subtract the size of the stack from the address of a local
+    // variable and give a 2k buffer.  Is there a better way?
+    uintptr_t stackLimitOffset = WORKER_THREAD_STACK_SIZE - 2*1024;
+    uintptr_t stackLimit = (((uintptr_t)&stackLimitOffset) +
+                             stackLimitOffset * JS_STACK_GROWTH_DIRECTION);
+
+    AutoLockMonitor lock(*this);
+
+    for (;;) {
+        while (!worklist_.empty()) {
+            TaskExecutor *task = worklist_.popCopy();
+            {
+                // Unlock so that new things can be added to the
+                // worklist while we are processing the current item:
+                AutoUnlockMonitor unlock(*this);
+                task->executeFromWorker(workerId_, stackLimit);
+            }
+        }
+
+        if (state_ == TERMINATING)
+            break;
+
+        JS_ASSERT(state_ == ACTIVE);
+
+        lock.wait();
+    }
+
+    JS_ASSERT(worklist_.empty() && state_ == TERMINATING);
+    state_ = TERMINATED;
+    lock.notify();
+}
+
+bool
+ThreadPoolWorker::submit(TaskExecutor *task)
+{
+    AutoLockMonitor lock(*this);
+    JS_ASSERT(state_ == ACTIVE);
+    if (!worklist_.append(task))
+        return false;
+    lock.notify();
+    return true;
+}
+
+void
+ThreadPoolWorker::terminate()
+{
+    AutoLockMonitor lock(*this);
+
+    if (state_ == CREATED) {
+        state_ = TERMINATED;
+        return;
+    } else if (state_ == ACTIVE) {
+        state_ = TERMINATING;
+        lock.notify();
+        while (state_ != TERMINATED) {
+            lock.wait();
+        }
+    } else {
+        JS_ASSERT(state_ == TERMINATED);
+    }
+}
+
+/****************************************************************************
+ * ThreadPool
+ *
+ * The |ThreadPool| starts up workers, submits work to them, and shuts
+ * them down when requested.
+ */
+
+ThreadPool::ThreadPool(JSRuntime *rt)
+    : runtime_(rt),
+      nextId_(0)
+{
+}
+
+ThreadPool::~ThreadPool() {
+    terminateWorkers();
+    while (workers_.length() > 0) {
+        ThreadPoolWorker *worker = workers_.popCopy();
+        js_delete(worker);
+    }
+}
+
+bool
+ThreadPool::init()
+{
+#ifdef JS_THREADSAFE_ION
+    // Compute desired number of workers based on env var or # of CPUs.
+    size_t numWorkers = 0;
+    char *pathreads = getenv("PATHREADS");
+    if (pathreads != NULL) {
+        numWorkers = strtol(pathreads, NULL, 10);
+    } else {
+        numWorkers = GetCPUCount() - 1;
+    }
+
+    // Allocate workers array and then start the worker threads.
+    // Ensure that the field numWorkers_ always tracks the number of
+    // *successfully initialized* workers.
+    for (size_t workerId = 0; workerId < numWorkers; workerId++) {
+        ThreadPoolWorker *worker = js_new<ThreadPoolWorker>(workerId, this);
+        if (!worker->init()) {
+            js_delete(worker);
+            return false;
+        }
+        if (!workers_.append(worker)) {
+            js_delete(worker);
+            return false;
+        }
+        if (!worker->start()) {
+            return false;
+        }
+    }
+#endif
+
+    return true;
+}
+
+void
+ThreadPool::terminateWorkers()
+{
+    for (size_t i = 0; i < workers_.length(); i++) {
+        workers_[i]->terminate();
+    }
+}
+
+bool
+ThreadPool::submitOne(TaskExecutor *executor) {
+    runtime_->assertValidThread();
+
+    if (numWorkers() == 0)
+        return false;
+
+    // Find next worker in round-robin fashion.
+    size_t id = JS_ATOMIC_INCREMENT(&nextId_) % workers_.length();
+    return workers_[id]->submit(executor);
+}
+
+bool
+ThreadPool::submitAll(TaskExecutor *executor) {
+    for (size_t id = 0; id < workers_.length(); id++) {
+        if (!workers_[id]->submit(executor))
+            return false;
+    }
+    return true;
+}
+
+bool
+ThreadPool::terminate() {
+    terminateWorkers();
+    return true;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/threadpool.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * 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 jsthreadpool_h___
+#define jsthreadpool_h___
+
+#if defined(JS_THREADSAFE) && defined(JS_ION)
+# define JS_THREADSAFE_ION
+#endif
+
+#include <stddef.h>
+#include "mozilla/StandardInteger.h"
+#include "prtypes.h"
+#include "js/Vector.h"
+#include "jsalloc.h"
+#include "prlock.h"
+#include "prcvar.h"
+
+struct JSContext;
+struct JSRuntime;
+struct JSCompartment;
+struct JSScript;
+
+namespace js {
+
+class ThreadPoolWorker;
+
+typedef void (*TaskFun)(void *userdata, size_t workerId, uintptr_t stackLimit);
+
+class TaskExecutor
+{
+public:
+    virtual void executeFromWorker(size_t workerId, uintptr_t stackLimit) = 0;
+};
+
+/*
+ * ThreadPool used for parallel JavaScript execution as well as
+ * parallel compilation.  Unless you are building a new kind of
+ * parallel service, it is very likely that you do not wish to
+ * interact with the threadpool directly.  In particular, if you wish
+ * to execute JavaScript in parallel, you probably want to look at
+ * |js::ForkJoin| in |forkjoin.cpp|.
+ *
+ * The ThreadPool always maintains a fixed pool of worker threads.
+ * You can query the number of worker threads via the method
+ * |numWorkers()|.  Note that this number may be zero (generally if
+ * threads are disabled, or when manually specified for benchmarking
+ * purposes).
+ *
+ * You can either submit jobs in one of two ways.  The first is
+ * |submitOne()|, which submits a job to be executed by one worker
+ * thread (this will fail if there are no worker threads).  The job
+ * will be enqueued and executed by some worker (the current scheduler
+ * uses round-robin load balancing; something more sophisticated,
+ * e.g. a central queue or work stealing, might be better).
+ *
+ * The second way to submit a job is using |submitAll()|---in this
+ * case, the job will be executed by all worker threads.  This does
+ * not fail if there are no worker threads, it simply does nothing.
+ * Of course, each thread may have any number of previously submitted
+ * things that they are already working on, and so they will finish
+ * those before they get to this job.  Therefore it is possible to
+ * have some worker threads pick up (and even finish) their piece of
+ * the job before others have even started.
+ */
+class ThreadPool
+{
+private:
+    friend class ThreadPoolWorker;
+
+    // Initialized at startup only:
+    JSRuntime *const runtime_;
+    js::Vector<ThreadPoolWorker*, 8, SystemAllocPolicy> workers_;
+
+    // Next worker for |submitOne()|. Atomically modified.
+    size_t nextId_;
+
+    void terminateWorkers();
+
+public:
+    ThreadPool(JSRuntime *rt);
+    ~ThreadPool();
+
+    bool init();
+
+    // Return number of worker threads in the pool.
+    size_t numWorkers() { return workers_.length(); }
+
+    // See comment on class:
+    bool submitOne(TaskExecutor *executor);
+    bool submitAll(TaskExecutor *executor);
+
+    // Wait until all worker threads have finished their current set
+    // of jobs and then return.  You must not submit new jobs after
+    // invoking |terminate()|.
+    bool terminate();
+};
+
+}
+
+
+
+#endif
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -528,37 +528,33 @@ class XPCIncrementalReleaseRunnable : pu
     virtual ~XPCIncrementalReleaseRunnable();
 
     void ReleaseNow(bool limited);
 
     NS_DECL_NSIRUNNABLE
 };
 
 bool
-ReleaseSliceNow(int32_t slice, void* data)
+ReleaseSliceNow(uint32_t slice, void* data)
 {
+    MOZ_ASSERT(slice > 0, "nonsensical/useless call with slice == 0");
     nsTArray<nsISupports *>* items =
         static_cast<nsTArray<nsISupports *>*>(data);
-    int32_t counter = 0;
-    while (1) {
-        uint32_t count = items->Length();
-        if (!count) {
-            break;
-        }
-
-        nsISupports *wrapper = items->ElementAt(count - 1);
-        items->RemoveElementAt(count - 1);
+
+    slice = NS_MIN(slice, items->Length());
+    for (uint32_t i = 0; i < slice; ++i) {
+        // Remove (and NS_RELEASE) the last entry in "items":
+        uint32_t lastItemIdx = items->Length() - 1;
+
+        nsISupports *wrapper = items->ElementAt(lastItemIdx);
+        items->RemoveElementAt(lastItemIdx);
         NS_RELEASE(wrapper);
-
-        if (slice > 0 && ++counter == slice) {
-            return items->IsEmpty();
-        }
     }
 
-    return true;
+    return items->IsEmpty();
 }
 
 
 XPCIncrementalReleaseRunnable::XPCIncrementalReleaseRunnable(XPCJSRuntime *rt,
                                                              nsTArray<nsISupports *> &items)
   : runtime(rt),
     finalizeFunctionToRun(0)
 {
@@ -609,17 +605,17 @@ XPCIncrementalReleaseRunnable::ReleaseNo
             }
             if (done) {
                 ++finalizeFunctionToRun;
             }
             if (timeout) {
                 break;
             }
         } else {
-            function.run(-1, function.data);
+            function.run(UINT32_MAX, function.data);
             MOZ_ASSERT(!items.Length());
             ++finalizeFunctionToRun;
         }
     } while (finalizeFunctionToRun < deferredFinalizeFunctions.Length());
 
     if (finalizeFunctionToRun == deferredFinalizeFunctions.Length()) {
         MOZ_ASSERT(runtime->mReleaseRunnable == this);
         runtime->mReleaseRunnable = nullptr;
@@ -709,17 +705,17 @@ XPCJSRuntime::GCCallback(JSRuntime *rt, 
             // Do any deferred releases of native objects.
             if (js::WasIncrementalGC(rt)) {
                 self->ReleaseIncrementally(self->mNativesToReleaseArray);
             } else {
                 DoDeferredRelease(self->mNativesToReleaseArray);
                 for (uint32_t i = 0; i < self->mDeferredFinalizeFunctions.Length(); ++i) {
                     void* data = self->mDeferredFinalizeFunctions[i].start();
                     if (data) {
-                        self->mDeferredFinalizeFunctions[i].run(-1, data);
+                        self->mDeferredFinalizeFunctions[i].run(UINT32_MAX, data);
                     }
                 }
             }
             self->GetXPConnect()->ClearGCBeforeCC();
             break;
         }
     }
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -725,21 +725,21 @@ public:
      * until after the GC has run, for example for objects that we don't want to
      * destroy during the GC.
      */
 
     // Called once before the deferred finalization starts. Should hand off the
     // buffer with things to finalize in the return value.
     typedef void* (*DeferredFinalizeStartFunction)();
 
-    // Called to finalize a number of objects. Slice is the number of objects to
-    // finalize, if it's -1 all objects should be finalized. data is the pointer
-    // returned by DeferredFinalizeStartFunction. Should return if it finalized
-    // all objects remaining in the buffer.
-    typedef bool (*DeferredFinalizeFunction)(int32_t slice, void* data);
+    // Called to finalize a number of objects. Slice is the number of objects
+    // to finalize, or if it's UINT32_MAX, all objects should be finalized.
+    // data is the pointer returned by DeferredFinalizeStartFunction.
+    // Return value indicates whether it finalized all objects in the buffer.
+    typedef bool (*DeferredFinalizeFunction)(uint32_t slice, void* data);
 
 private:
     struct DeferredFinalizeFunctions
     {
         DeferredFinalizeStartFunction start;
         DeferredFinalizeFunction run;
     };
     nsAutoTArray<DeferredFinalizeFunctions, 16> mDeferredFinalizeFunctions;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bidi/dirAuto/dir_auto-EN-L-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>HTML Test: dir=auto, start with EN, then L</title>
+    <link rel="reference" href="dir_auto-EN-L-ref.html" />
+    <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+    <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+    <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+    <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#the-dir-attribute" />
+    <meta name="assert" content="
+      When dir='auto', the direction is set according to the first strong character
+      of the text.
+      In this test, it is the Latin letter A since digits are not strongly
+      directional, thus the direction must be resolved as LTR." />
+    <style>
+      input, textarea {
+        font-size:1em;
+      }
+      body {
+        font-size:2em;
+      }
+      .test, .ref {
+        border: medium solid gray;
+        width: 400px;
+        margin: 20px;
+      }
+      .comments {
+        display: none;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+    <div class="comments">
+      Key to entities used below:
+      &#x05D0; - The Hebrew letter Alef (strongly RTL).
+      &#x05D1; - The Hebrew letter Bet (strongly RTL).
+      &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+    </div>
+    <div class="test">
+      <div dir="ltr">
+        <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+      </div>
+      <div dir="rtl">
+        <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+      </div>
+    </div>
+    <div class="ref">
+      <div dir="ltr">
+        <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+      </div>
+      <div dir="rtl">
+        <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+      </div>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bidi/dirAuto/dir_auto-EN-L.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>HTML Test: dir=auto, start with EN, then L</title>
+    <link rel="reference" href="dir_auto-EN-L-ref.html" />
+    <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+    <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+    <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+    <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#the-dir-attribute" />
+    <meta name="assert" content="
+      When dir='auto', the direction is set according to the first strong character
+      of the text.
+      In this test, it is the Latin letter A since digits are not strongly
+      directional, thus the direction must be resolved as LTR." />
+    <style>
+      input, textarea {
+        font-size:1em;
+      }
+      body {
+        font-size:2em;
+      }
+      .test, .ref {
+        border: medium solid gray;
+        width: 400px;
+        margin: 20px;
+      }
+      .comments {
+        display: none;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+    <div class="comments">
+      Key to entities used below:
+      &#x05D0; - The Hebrew letter Alef (strongly RTL).
+      &#x05D1; - The Hebrew letter Bet (strongly RTL).
+      &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+    </div>
+    <div class="test">
+      <div dir="ltr">
+        <p dir="auto">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+      </div>
+      <div dir="rtl">
+        <p dir="auto">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+      </div>
+    </div>
+    <div class="ref">
+      <div dir="ltr">
+        <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+      </div>
+      <div dir="rtl">
+        <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+      </div>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bidi/dirAuto/dir_auto-EN-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>HTML Test: dir=auto, start with EN, then R</title>
+    <link rel="reference" href="dir_auto-EN-R-ref.html" />
+    <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+    <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+    <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+    <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#the-dir-attribute" />
+    <meta name="assert" content="
+      When dir='auto', the direction is set according to the first strong character
+      of the text.
+      In this test, it is the Hebrew letter Alef since digits are not strongly
+      directional, thus the direction must be resolved as RTL." />
+    <style>
+      input, textarea {
+        font-size:1em;
+      }
+      body {
+        font-size:2em;
+      }
+      .test, .ref {
+        border: medium solid gray;
+        width: 400px;
+        margin: 20px;
+      }
+      .comments {
+        display: none;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+    <div class="comments">
+      Key to entities used below:
+      &#x05D0; - The Hebrew letter Alef (strongly RTL).
+      &#x05D1; - The Hebrew letter Bet (strongly RTL).
+      &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+    </div>
+    <div class="test">
+      <div dir="ltr">
+        <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+      </div>
+      <div dir="rtl">
+        <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+      </div>
+    </div>
+    <div class="ref">
+      <div dir="ltr">
+        <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+      </div>
+      <div dir="rtl">
+        <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+      </div>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bidi/dirAuto/dir_auto-EN-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>HTML Test: dir=auto, start with EN, then R</title>
+    <link rel="reference" href="dir_auto-EN-R-ref.html" />
+    <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+    <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+    <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+    <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#the-dir-attribute" />
+    <meta name="assert" content="
+      When dir='auto', the direction is set according to the first strong character
+      of the text.
+      In this test, it is the Hebrew letter Alef since digits are not strongly
+      directional, thus the direction must be resolved as RTL." />
+    <style>
+      input, textarea {
+        font-size:1em;
+      }
+      body {
+        font-size:2em;
+      }
+      .test, .ref {
+        border: medium solid gray;
+        width: 400px;
+        margin: 20px;
+      }
+      .comments {
+        display: none;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+    <div class="comments">
+      Key to entities used below:
+      &#x05D0; - The Hebrew letter Alef (strongly RTL).
+      &#x05D1; - The Hebrew letter Bet (strongly RTL).
+      &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+    </div>
+    <div class="test">
+      <div dir="ltr">
+        <p dir="auto">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+      </div>
+      <div dir="rtl">
+        <p dir="auto">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+      </div>
+    </div>
+    <div class="ref">
+      <div dir="ltr">
+        <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+      </div>
+      <div dir="rtl">
+        <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+      </div>
+    </div>
+  </body>
+</html>