Merge the last green changeset on mozilla-inbound to mozilla-central
authorEhsan Akhgari <ehsan@mozilla.com>
Fri, 14 Oct 2011 10:37:35 -0400
changeset 78736 349f3d4b2d877b99502b6aff9e826fe5250de028
parent 78727 fef552fcd2fcab54b2598f76118e7f5869ae117b (current diff)
parent 78735 fa65bb9a09092e9cffa8cf352ddd4eaf8d3bef00 (diff)
child 78737 80d7e83b765985cae8aeff60b47b311d0422bbe1
child 81541 7603c5ce42602e6246e02690fa7e43d2a0c4985a
push id21329
push usereakhgari@mozilla.com
push dateFri, 14 Oct 2011 14:37:50 +0000
treeherdermozilla-central@349f3d4b2d87 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge the last green changeset on mozilla-inbound to mozilla-central
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -184,16 +184,17 @@ struct nsShortcutCandidate {
   }
   PRUint32 mCharCode;
   bool     mIgnoreShift;
 };
 
 class nsContentUtils
 {
   friend class nsAutoScriptBlockerSuppressNodeRemoved;
+  friend class mozilla::AutoRestore<bool>;
   typedef mozilla::dom::Element Element;
   typedef mozilla::TimeDuration TimeDuration;
 
 public:
   static nsresult Init();
 
   /**
    * Get a JSContext from the document's scope object.
@@ -1056,32 +1057,36 @@ public:
    * @param aSourceBuffer the string being set as innerHTML
    * @param aTargetNode the target container
    * @param aContextLocalName local name of context node
    * @param aContextNamespace namespace of context node
    * @param aQuirks true to make <table> not close <p>
    * @param aPreventScriptExecution true to prevent scripts from executing;
    *        don't set to false when parsing into a target node that has been
    *        bound to tree.
+   * @return NS_ERROR_DOM_INVALID_STATE_ERR if a re-entrant attempt to parse
+   *         fragments is made and NS_OK otherwise.
    */
-  static void ParseFragmentHTML(const nsAString& aSourceBuffer,
-                                nsIContent* aTargetNode,
-                                nsIAtom* aContextLocalName,
-                                PRInt32 aContextNamespace,
-                                bool aQuirks,
-                                bool aPreventScriptExecution);
+  static nsresult ParseFragmentHTML(const nsAString& aSourceBuffer,
+                                    nsIContent* aTargetNode,
+                                    nsIAtom* aContextLocalName,
+                                    PRInt32 aContextNamespace,
+                                    bool aQuirks,
+                                    bool aPreventScriptExecution);
 
   /**
    * Invoke the fragment parsing algorithm (innerHTML) using the XML parser.
    *
    * @param aSourceBuffer the string being set as innerHTML
    * @param aTargetNode the target container
    * @param aTagStack the namespace mapping context
    * @param aPreventExecution whether to mark scripts as already started
    * @param aReturn the result fragment
+   * @return NS_ERROR_DOM_INVALID_STATE_ERR if a re-entrant attempt to parse
+   *         fragments is made, a return code from the XML parser.
    */
   static nsresult ParseFragmentXML(const nsAString& aSourceBuffer,
                                    nsIDocument* aDocument,
                                    nsTArray<nsString>& aTagStack,
                                    bool aPreventScriptExecution,
                                    nsIDOMDocumentFragment** aReturn);
 
   /**
@@ -1906,16 +1911,21 @@ private:
   static bool sTrustedFullScreenOnly;
   static bool sFullScreenKeyInputRestricted;
   static PRUint32 sHandlingInputTimeout;
 
   static nsHtml5Parser* sHTMLFragmentParser;
   static nsIParser* sXMLFragmentParser;
   static nsIFragmentContentSink* sXMLFragmentSink;
 
+  /**
+   * True if there's a fragment parser activation on the stack.
+   */
+  static bool sFragmentParsingActive;
+
   static nsString* sShiftText;
   static nsString* sControlText;
   static nsString* sMetaText;
   static nsString* sAltText;
   static nsString* sModifierSeparator;
 };
 
 #define NS_HOLD_JS_OBJECTS(obj, clazz)                                         \
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -267,16 +267,17 @@ bool nsContentUtils::sIsFullScreenApiEna
 bool nsContentUtils::sTrustedFullScreenOnly = true;
 bool nsContentUtils::sFullScreenKeyInputRestricted = true;
 
 PRUint32 nsContentUtils::sHandlingInputTimeout = 1000;
 
 nsHtml5Parser* nsContentUtils::sHTMLFragmentParser = nsnull;
 nsIParser* nsContentUtils::sXMLFragmentParser = nsnull;
 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nsnull;
+bool nsContentUtils::sFragmentParsingActive = false;
 
 static PLDHashTable sEventListenerManagersHash;
 
 class EventListenerManagerMapEntry : public PLDHashEntryHdr
 {
 public:
   EventListenerManagerMapEntry(const void *aKey)
     : mKey(aKey)
@@ -3504,37 +3505,38 @@ nsContentUtils::CreateContextualFragment
     if (contextAsContent && !contextAsContent->IsElement()) {
       contextAsContent = contextAsContent->GetParent();
       if (contextAsContent && !contextAsContent->IsElement()) {
         // can this even happen?
         contextAsContent = nsnull;
       }
     }
     
+    nsresult rv;
     nsCOMPtr<nsIContent> fragment = do_QueryInterface(frag);
     if (contextAsContent && !contextAsContent->IsHTML(nsGkAtoms::html)) {
-      ParseFragmentHTML(aFragment,
-                        fragment,
-                        contextAsContent->Tag(),
-                        contextAsContent->GetNameSpaceID(),
-                        (document->GetCompatibilityMode() ==
-                            eCompatibility_NavQuirks),
-                        aPreventScriptExecution);
+      rv = ParseFragmentHTML(aFragment,
+                             fragment,
+                             contextAsContent->Tag(),
+                             contextAsContent->GetNameSpaceID(),
+                             (document->GetCompatibilityMode() ==
+                               eCompatibility_NavQuirks),
+                             aPreventScriptExecution);
     } else {
-      ParseFragmentHTML(aFragment,
-                        fragment,
-                        nsGkAtoms::body,
-                        kNameSpaceID_XHTML,
-                        (document->GetCompatibilityMode() ==
-                            eCompatibility_NavQuirks),
-                        aPreventScriptExecution);
+      rv = ParseFragmentHTML(aFragment,
+                             fragment,
+                             nsGkAtoms::body,
+                             kNameSpaceID_XHTML,
+                             (document->GetCompatibilityMode() ==
+                               eCompatibility_NavQuirks),
+                             aPreventScriptExecution);
     }
 
     frag.forget(aReturn);
-    return NS_OK;
+    return rv;
   }
 
   nsAutoTArray<nsString, 32> tagStack;
   nsAutoString uriStr, nameStr;
   nsCOMPtr<nsIContent> content = do_QueryInterface(aContextNode);
   // just in case we have a text node
   if (content && !content->IsElement())
     content = content->GetParent();
@@ -3606,46 +3608,59 @@ nsContentUtils::DropFragmentParsers()
 /* static */
 void
 nsContentUtils::XPCOMShutdown()
 {
   nsContentUtils::DropFragmentParsers();
 }
 
 /* static */
-void
+nsresult
 nsContentUtils::ParseFragmentHTML(const nsAString& aSourceBuffer,
                                   nsIContent* aTargetNode,
                                   nsIAtom* aContextLocalName,
                                   PRInt32 aContextNamespace,
                                   bool aQuirks,
                                   bool aPreventScriptExecution)
 {
+  if (nsContentUtils::sFragmentParsingActive) {
+    NS_NOTREACHED("Re-entrant fragment parsing attempted.");
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+  mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
+  nsContentUtils::sFragmentParsingActive = true;
   if (!sHTMLFragmentParser) {
     sHTMLFragmentParser =
       static_cast<nsHtml5Parser*>(nsHtml5Module::NewHtml5Parser().get());
     // Now sHTMLFragmentParser owns the object
   }
   sHTMLFragmentParser->ParseHtml5Fragment(aSourceBuffer,
                                           aTargetNode,
                                           aContextLocalName,
                                           aContextNamespace,
                                           aQuirks,
                                           aPreventScriptExecution);
   sHTMLFragmentParser->Reset();
+  return NS_OK;
 }
 
 /* static */
 nsresult
 nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
                                  nsIDocument* aDocument,
                                  nsTArray<nsString>& aTagStack,
                                  bool aPreventScriptExecution,
                                  nsIDOMDocumentFragment** aReturn)
 {
+  if (nsContentUtils::sFragmentParsingActive) {
+    NS_NOTREACHED("Re-entrant fragment parsing attempted.");
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+  mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
+  nsContentUtils::sFragmentParsingActive = true;
   if (!sXMLFragmentParser) {
     nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID);
     parser.forget(&sXMLFragmentParser);
     // sXMLFragmentParser now owns the parser
   }
   if (!sXMLFragmentSink) {
     NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
     // sXMLFragmentSink now owns the sink
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -765,23 +765,23 @@ nsGenericHTMLElement::SetInnerHTML(const
   }
 
   nsAutoScriptLoaderDisabler sld(doc);
   
   nsCOMPtr<nsIDOMDocumentFragment> df;
 
   if (doc->IsHTML()) {
     PRInt32 oldChildCount = GetChildCount();
-    nsContentUtils::ParseFragmentHTML(aInnerHTML,
-                                      this,
-                                      Tag(),
-                                      GetNameSpaceID(),
-                                      doc->GetCompatibilityMode() ==
-                                          eCompatibility_NavQuirks,
-                                      PR_TRUE);
+    rv = nsContentUtils::ParseFragmentHTML(aInnerHTML,
+                                           this,
+                                           Tag(),
+                                           GetNameSpaceID(),
+                                           doc->GetCompatibilityMode() ==
+                                             eCompatibility_NavQuirks,
+                                           PR_TRUE);
     // HTML5 parser has notified, but not fired mutation events.
     FireMutationEventsForDirectParsing(doc, this, oldChildCount);
   } else {
     rv = nsContentUtils::CreateContextualFragment(this, aInnerHTML,
                                                   PR_TRUE,
                                                   getter_AddRefs(df));
     nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
     if (NS_SUCCEEDED(rv)) {
@@ -836,48 +836,49 @@ nsGenericHTMLElement::InsertAdjacentHTML
 
   // Needed when insertAdjacentHTML is used in combination with contenteditable
   mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, PR_TRUE);
   nsAutoScriptLoaderDisabler sld(doc);
   
   // Batch possible DOMSubtreeModified events.
   mozAutoSubtreeModified subtree(doc, nsnull);
 
+  nsresult rv;
   // Parse directly into destination if possible
   if (doc->IsHTML() &&
       (position == eBeforeEnd ||
        (position == eAfterEnd && !GetNextSibling()) ||
        (position == eAfterBegin && !GetFirstChild()))) {
     PRInt32 oldChildCount = destination->GetChildCount();
     PRInt32 contextNs = destination->GetNameSpaceID();
     nsIAtom* contextLocal = destination->Tag();
     if (contextLocal == nsGkAtoms::html && contextNs == kNameSpaceID_XHTML) {
       // For compat with IE6 through IE9. Willful violation of HTML5 as of
       // 2011-04-06. CreateContextualFragment does the same already.
       // Spec bug: http://www.w3.org/Bugs/Public/show_bug.cgi?id=12434
       contextLocal = nsGkAtoms::body;
     }
-    nsContentUtils::ParseFragmentHTML(aText,
-                                      destination,
-                                      contextLocal,
-                                      contextNs,
-                                      doc->GetCompatibilityMode() ==
-                                          eCompatibility_NavQuirks,
-                                      PR_TRUE);
+    rv = nsContentUtils::ParseFragmentHTML(aText,
+                                           destination,
+                                           contextLocal,
+                                           contextNs,
+                                           doc->GetCompatibilityMode() ==
+                                             eCompatibility_NavQuirks,
+                                           PR_TRUE);
     // HTML5 parser has notified, but not fired mutation events.
     FireMutationEventsForDirectParsing(doc, destination, oldChildCount);
-    return NS_OK;
+    return rv;
   }
 
   // couldn't parse directly
   nsCOMPtr<nsIDOMDocumentFragment> df;
-  nsresult rv = nsContentUtils::CreateContextualFragment(destination,
-                                                         aText,
-                                                         PR_TRUE,
-                                                         getter_AddRefs(df));
+  rv = nsContentUtils::CreateContextualFragment(destination,
+                                                aText,
+                                                PR_TRUE,
+                                                getter_AddRefs(df));
   nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Suppress assertion about node removal mutation events that can't have
   // listeners anyway, because no one has had the chance to register mutation
   // listeners on the fragment that comes from the parser.
   nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -2702,33 +2702,34 @@ nsresult nsHTMLEditor::CreateDOMFragment
 
 
 nsresult nsHTMLEditor::ParseFragment(const nsAString & aFragStr,
                                      nsIAtom* aContextLocalName,
                                      nsIDocument* aTargetDocument,
                                      nsCOMPtr<nsIDOMNode> *outNode,
                                      bool aTrustedInput)
 {
+  nsresult rv;
   nsCOMPtr<nsIDOMDocumentFragment> frag;
   NS_NewDocumentFragment(getter_AddRefs(frag),
                          aTargetDocument->NodeInfoManager());
   nsCOMPtr<nsIContent> fragment = do_QueryInterface(frag);
-  nsContentUtils::ParseFragmentHTML(aFragStr,
-                                    fragment,
-                                    aContextLocalName ?
-                                        aContextLocalName : nsGkAtoms::body,
-                                    kNameSpaceID_XHTML,
-                                    PR_FALSE,
-                                    PR_TRUE);
+  rv = nsContentUtils::ParseFragmentHTML(aFragStr,
+                                         fragment,
+                                         aContextLocalName ?
+                                           aContextLocalName : nsGkAtoms::body,
+                                        kNameSpaceID_XHTML,
+                                        PR_FALSE,
+                                        PR_TRUE);
   if (!aTrustedInput) {
     nsTreeSanitizer sanitizer(!!aContextLocalName, !aContextLocalName);
     sanitizer.Sanitize(fragment);
   }
   *outNode = do_QueryInterface(frag);
-  return NS_OK;
+  return rv;
 }
 
 nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode  *aFragmentAsNode,
                                                 nsCOMArray<nsIDOMNode>& outNodeList,
                                                 nsIDOMNode *aStartNode,
                                                 PRInt32 aStartOffset,
                                                 nsIDOMNode *aEndNode,
                                                 PRInt32 aEndOffset)
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3110,45 +3110,54 @@ AccountStorageForTextRun(gfxTextRun *aTe
       bytes &= ~(sizeof(gfxTextRun::CompressedGlyph) - 1);
     }
     bytes += sizeof(gfxTextRun);
     gTextRunStorage += bytes*aSign;
     gTextRunStorageHighWaterMark = NS_MAX(gTextRunStorageHighWaterMark, gTextRunStorage);
 }
 #endif
 
+static PRUint64
+GlyphStorageAllocCount(PRUint32 aLength, PRUint32 aFlags)
+{
+    // always need to allocate storage for the glyph data
+    PRUint64 allocCount = aLength;
+
+    // if the text is not persistent, we also need space for a copy
+    if (!(aFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
+        // figure out number of extra CompressedGlyph elements we need to
+        // get sufficient space for the text
+        typedef gfxTextRun::CompressedGlyph CompressedGlyph;
+        if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
+            allocCount += (aLength + sizeof(CompressedGlyph) - 1) /
+                          sizeof(CompressedGlyph);
+        } else {
+            allocCount += (aLength * sizeof(PRUnichar) +
+                              sizeof(CompressedGlyph) - 1) /
+                          sizeof(CompressedGlyph);
+        }
+    }
+    return allocCount;
+}
+
 // Helper for textRun creation to preallocate storage for glyphs and text;
 // this function returns a pointer to the newly-allocated glyph storage,
 // AND modifies the aText parameter if TEXT_IS_PERSISTENT was not set.
 // In that case, the text is appended to the glyph storage, so a single
 // delete[] operation in the textRun destructor will free both.
 // Returns nsnull if allocation fails.
 gfxTextRun::CompressedGlyph *
 gfxTextRun::AllocateStorage(const void*& aText, PRUint32 aLength, PRUint32 aFlags)
 {
     // Here, we rely on CompressedGlyph being the largest unit we care about for
     // allocation/alignment of either glyph data or text, so we allocate an array
     // of CompressedGlyphs, then take the last chunk of that and cast a pointer to
     // PRUint8* or PRUnichar* for text storage.
 
-    // always need to allocate storage for the glyph data
-    PRUint64 allocCount = aLength;
-
-    // if the text is not persistent, we also need space for a copy
-    if (!(aFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
-        // figure out number of extra CompressedGlyph elements we need to
-        // get sufficient space for the text
-        if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
-            allocCount += (aLength + sizeof(CompressedGlyph)-1)
-                          / sizeof(CompressedGlyph);
-        } else {
-            allocCount += (aLength*sizeof(PRUnichar) + sizeof(CompressedGlyph)-1)
-                          / sizeof(CompressedGlyph);
-        }
-    }
+    PRUint64 allocCount = GlyphStorageAllocCount(aLength, aFlags);
 
     // allocate the storage we need, returning nsnull on failure rather than
     // throwing an exception (because web content can create huge runs)
     CompressedGlyph *storage = new (std::nothrow) CompressedGlyph[allocCount];
     if (!storage) {
         NS_WARNING("failed to allocate glyph/text storage for text run!");
         return nsnull;
     }
@@ -4466,16 +4475,41 @@ gfxTextRun::ClusterIterator::ClusterAdva
 {
     if (mCurrentChar == PRUint32(-1)) {
         return 0;
     }
 
     return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider);
 }
 
+PRUint64
+gfxTextRun::ComputeSize()
+{
+    PRUint64 total = moz_malloc_usable_size(this);
+    if (total == 0) {
+        total = sizeof(gfxTextRun);
+    }
+
+    PRUint64 glyphDataSize = moz_malloc_usable_size(mCharacterGlyphs);
+    if (glyphDataSize == 0) {
+        // calculate how much gfxTextRun::AllocateStorage would have allocated
+        glyphDataSize = sizeof(CompressedGlyph) *
+            GlyphStorageAllocCount(mCharacterCount, mFlags);
+    }
+    total += glyphDataSize;
+
+    if (mDetailedGlyphs) {
+        total += mDetailedGlyphs->SizeOf();
+    }
+
+    total += mGlyphRuns.SizeOf();
+
+    return total;
+}
+
 
 #ifdef DEBUG
 void
 gfxTextRun::Dump(FILE* aOutput) {
     if (!aOutput) {
         aOutput = stdout;
     }
 
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1348,17 +1348,25 @@ public:
          * required for legible text should still be enabled.
          */
         TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0080,
         /**
          * When set, the textrun should favour speed of construction over
          * quality. This may involve disabling ligatures and/or kerning or
          * other effects.
          */
-        TEXT_OPTIMIZE_SPEED          = 0x0100
+        TEXT_OPTIMIZE_SPEED          = 0x0100,
+        /**
+         * For internal use by the memory reporter when accounting for
+         * storage used by textruns.
+         * Because the reporter may visit each textrun multiple times while
+         * walking the frame trees and textrun cache, it needs to mark
+         * textruns that have been seen so as to avoid multiple-accounting.
+         */
+        TEXT_RUN_SIZE_ACCOUNTED      = 0x0200
     };
 
     /**
      * This record contains all the parameters needed to initialize a textrun.
      */
     struct Parameters {
         // A reference context suggesting where the textrun will be rendered
         gfxContext   *mContext;
@@ -2033,16 +2041,31 @@ public:
         
         bool mClipBeforePart;
         bool mClipAfterPart;
     };
     
     // user font set generation when text run was created
     PRUint64 GetUserFontSetGeneration() { return mUserFontSetGeneration; }
 
+    // return storage used by this run, for memory reporter;
+    // nsTransformedTextRun needs to override this as it holds additional data
+    virtual PRUint64 ComputeSize();
+
+    void AccountForSize(PRUint64* aTotal)  {
+        if (mFlags & gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED) {
+            return;
+        }
+        mFlags |= gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
+        *aTotal += ComputeSize();
+    }
+    void ClearSizeAccounted() {
+        mFlags &= ~gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
+    }
+
 #ifdef DEBUG
     // number of entries referencing this textrun in the gfxTextRunWordCache
     PRUint32 mCachedWords;
     // generation of gfxTextRunWordCache that refers to this textrun;
     // if the cache gets cleared, then mCachedWords is no longer meaningful
     PRUint32 mCacheGeneration;
     
     void Dump(FILE* aOutput);
@@ -2203,16 +2226,21 @@ private:
                 if (!mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex),
                                                         CompareRecordOffsets())) {
                     return nsnull;
                 }
             }
             return details;
         }
 
+        PRUint32 SizeOf() {
+            return sizeof(DetailedGlyphStore) +
+                mDetails.SizeOf() + mOffsetToIndex.SizeOf();
+        }
+
     private:
         struct DGRec {
             DGRec(const PRUint32& aOffset, const PRUint32& aIndex)
                 : mOffset(aOffset), mIndex(aIndex) { }
             PRUint32 mOffset; // source character offset in the textrun
             PRUint32 mIndex;  // index where this char's DetailedGlyphs begin
         };
 
--- a/gfx/thebes/gfxTextRunWordCache.cpp
+++ b/gfx/thebes/gfxTextRunWordCache.cpp
@@ -130,16 +130,18 @@ public:
      */
     void Flush() {
         mCache.Clear(); 
 #ifdef DEBUG
         mGeneration++;
 #endif
     }
 
+    void ComputeStorage(PRUint64 *aTotal);
+
 #ifdef DEBUG
     PRUint32 mGeneration;
     void Dump();
 #endif
 
 protected:
     struct CacheHashKey {
         void        *mFontOrGroup;
@@ -212,16 +214,21 @@ protected:
     void FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun,
                        const gfxFontGroup::Parameters *aParams,
                        const nsTArray<DeferredWord>& aDeferredWords,
                        bool aSuccessful);
     void RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart,
                     PRUint32 aEnd, PRUint32 aHash);
     void Uninit();
 
+    static PLDHashOperator AccountForStorage(CacheHashEntry *aEntry,
+                                             void *aUserData);
+    static PLDHashOperator ClearSizeAccounted(CacheHashEntry *aEntry,
+                                              void *aUserData);
+
     nsTHashtable<CacheHashEntry> mCache;
 
     PRInt32 mBidiNumeral;
 
 #ifdef DEBUG
     static PLDHashOperator CacheDumpEntry(CacheHashEntry* aEntry, void* userArg);
 #endif
 };
@@ -902,16 +909,48 @@ TextRunWordCache::RemoveTextRun(gfxTextR
     }
     RemoveWord(aTextRun, wordStart, i, hash);
 #ifdef DEBUG
     NS_ASSERTION(aTextRun->mCachedWords == 0,
                  "Textrun was not completely removed from the cache!");
 #endif
 }
 
+/*static*/ PLDHashOperator
+TextRunWordCache::AccountForStorage(CacheHashEntry *aEntry, void *aUserData)
+{
+    gfxTextRun *run = aEntry->mTextRun;
+    if (run) {
+        PRUint64 *total = static_cast<PRUint64*>(aUserData);
+        run->AccountForSize(total);
+    }
+    return PL_DHASH_NEXT;
+}
+
+/*static*/ PLDHashOperator
+TextRunWordCache::ClearSizeAccounted(CacheHashEntry *aEntry, void *)
+{
+    gfxTextRun *run = aEntry->mTextRun;
+    if (run) {
+        run->ClearSizeAccounted();
+    }
+    return PL_DHASH_NEXT;
+}
+
+void
+TextRunWordCache::ComputeStorage(PRUint64 *aTotal)
+{
+    if (aTotal) {
+        *aTotal += mCache.SizeOf();
+        mCache.EnumerateEntries(AccountForStorage, aTotal);
+    } else {
+        mCache.EnumerateEntries(ClearSizeAccounted, nsnull);
+    }
+}
+
 static bool
 CompareDifferentWidthStrings(const PRUint8 *aStr1, const PRUnichar *aStr2,
                              PRUint32 aLength)
 {
     PRUint32 i;
     for (i = 0; i < aLength; ++i) {
         if (aStr1[i] != aStr2[i])
             return PR_FALSE;
@@ -1056,8 +1095,18 @@ gfxTextRunWordCache::RemoveTextRun(gfxTe
 
 void
 gfxTextRunWordCache::Flush()
 {
     if (!gTextRunWordCache)
         return;
     gTextRunWordCache->Flush();
 }
+
+void
+gfxTextRunWordCache::ComputeStorage(PRUint64 *aTotal)
+{
+    if (!gTextRunWordCache) {
+        return;
+    }
+    gTextRunWordCache->ComputeStorage(aTotal);
+}
+
--- a/gfx/thebes/gfxTextRunWordCache.h
+++ b/gfx/thebes/gfxTextRunWordCache.h
@@ -100,16 +100,25 @@ public:
     static void RemoveTextRun(gfxTextRun *aTextRun);
 
     /**
      * Flush the textrun cache. This must be called if a configuration
      * change that would affect textruns is applied.
      */
     static void Flush();
 
+    /**
+     * If aTotal is NULL, just clears the TEXT_RUN_MEMORY_ACCOUNTED flag
+     * on each textRun found.
+     * If aTotal is non-NULL, adds the storage used for each textRun to the
+     * total, and sets the TEXT_RUN_MEMORY_ACCOUNTED flag to avoid double-
+     * accounting. (Runs with this flag already set will be skipped.)
+     */
+    static void ComputeStorage(PRUint64 *aTotal);
+
 protected:
     friend class gfxPlatform;
 
     static nsresult Init();
     static void Shutdown();
 };
 
 #endif /* GFX_TEXT_RUN_WORD_CACHE_H */
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -38,17 +38,17 @@
 
 #include "GeckoChildProcessHost.h"
 
 #include "base/command_line.h"
 #include "base/path_service.h"
 #include "base/string_util.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/process_watcher.h"
-#ifdef XP_MACOSX
+#ifdef MOZ_WIDGET_COCOA
 #include "chrome/common/mach_ipc_mac.h"
 #include "base/rand_util.h"
 #include "nsILocalFileMac.h"
 #endif
 
 #include "prprf.h"
 #include "prenv.h"
 
@@ -101,17 +101,17 @@ GeckoChildProcessHost::GeckoChildProcess
                                              base::WaitableEventWatcher::Delegate* aDelegate)
   : ChildProcessHost(RENDER_PROCESS), // FIXME/cjones: we should own this enum
     mProcessType(aProcessType),
     mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
     mLaunched(false),
     mChannelInitialized(false),
     mDelegate(aDelegate),
     mChildProcessHandle(0)
-#if defined(XP_MACOSX)
+#if defined(MOZ_WIDGET_COCOA)
   , mChildTask(MACH_PORT_NULL)
 #endif
 {
     MOZ_COUNT_CTOR(GeckoChildProcessHost);
     
     MessageLoop* ioLoop = XRE_GetIOMessageLoop();
     ioLoop->PostTask(FROM_HERE,
                      NewRunnableMethod(this,
@@ -127,17 +127,17 @@ GeckoChildProcessHost::~GeckoChildProces
 
   if (mChildProcessHandle > 0)
     ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle
 #if defined(NS_BUILD_REFCNT_LOGGING)
                                             , false // don't "force"
 #endif
     );
 
-#if defined(XP_MACOSX)
+#if defined(MOZ_WIDGET_COCOA)
   if (mChildTask != MACH_PORT_NULL)
     mach_port_deallocate(mach_task_self(), mChildTask);
 #endif
 }
 
 void GetPathToBinary(FilePath& exePath)
 {
 #if defined(OS_WIN)
@@ -150,17 +150,17 @@ void GetPathToBinary(FilePath& exePath)
     NS_ASSERTION(directoryService, "Expected XPCOM to be available");
     if (directoryService) {
       nsCOMPtr<nsIFile> greDir;
       nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
       if (NS_SUCCEEDED(rv)) {
         nsCString path;
         greDir->GetNativePath(path);
         exePath = FilePath(path.get());
-#ifdef OS_MACOSX
+#ifdef MOZ_WIDGET_COCOA
         // We need to use an App Bundle on OS X so that we can hide
         // the dock icon. See Bug 557225.
         exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE);
 #endif
       }
     }
   }
 
@@ -168,17 +168,17 @@ void GetPathToBinary(FilePath& exePath)
     exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
     exePath = exePath.DirName();
   }
 
   exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
 #endif
 }
 
-#ifdef XP_MACOSX
+#ifdef MOZ_WIDGET_COCOA
 class AutoCFTypeObject {
 public:
   AutoCFTypeObject(CFTypeRef object)
   {
     mObject = object;
   }
   ~AutoCFTypeObject()
   {
@@ -188,17 +188,17 @@ private:
   CFTypeRef mObject;
 };
 #endif
 
 nsresult GeckoChildProcessHost::GetArchitecturesForBinary(const char *path, uint32 *result)
 {
   *result = 0;
 
-#ifdef XP_MACOSX
+#ifdef MOZ_WIDGET_COCOA
   CFURLRef url = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
                                                            (const UInt8*)path,
                                                            strlen(path),
                                                            false);
   if (!url) {
     return NS_ERROR_FAILURE;
   }
   AutoCFTypeObject autoPluginContainerURL(url);
@@ -234,17 +234,17 @@ nsresult GeckoChildProcessHost::GetArchi
   return (*result ? NS_OK : NS_ERROR_FAILURE);
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
 
 uint32 GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType type)
 {
-#ifdef XP_MACOSX
+#ifdef MOZ_WIDGET_COCOA
   if (type == GeckoProcessType_Plugin) {
     // Cache this, it shouldn't ever change.
     static uint32 pluginContainerArchs = 0;
     if (pluginContainerArchs == 0) {
       FilePath exePath;
       GetPathToBinary(exePath);
       nsresult rv = GetArchitecturesForBinary(exePath.value().c_str(), &pluginContainerArchs);
       NS_ASSERTION(NS_SUCCEEDED(rv) && pluginContainerArchs != 0, "Getting architecture of plugin container failed!");
@@ -566,22 +566,22 @@ GeckoChildProcessHost::PerformAsyncLaunc
     mFileMap.push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd));
     // "true" == crash reporting enabled
     childArgv.push_back("true");
   }
   else {
     // "false" == crash reporting disabled
     childArgv.push_back("false");
   }
-#  elif defined(XP_MACOSX)
+#  elif defined(MOZ_WIDGET_COCOA)
   childArgv.push_back(CrashReporter::GetChildNotificationPipe());
 #  endif  // OS_LINUX
 #endif
 
-#ifdef XP_MACOSX
+#ifdef MOZ_WIDGET_COCOA
   // Add a mach port to the command line so the child can communicate its
   // 'task_t' back to the parent.
   //
   // Put a random number into the channel name, so that a compromised renderer
   // can't pretend being the child that's forked off.
   std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d",
                                                   base::RandInt(0, std::numeric_limits<int>::max()));
   childArgv.push_back(mach_connection_name.c_str());
@@ -594,17 +594,17 @@ GeckoChildProcessHost::PerformAsyncLaunc
 #endif
 
   base::LaunchApp(childArgv, mFileMap,
 #if defined(OS_LINUX) || defined(OS_MACOSX)
                   newEnvVars,
 #endif
                   false, &process, arch);
 
-#ifdef XP_MACOSX
+#ifdef MOZ_WIDGET_COCOA
   // Wait for the child process to send us its 'task_t' data.
   const int kTimeoutMs = 10000;
 
   MachReceiveMessage child_message;
   ReceivePort parent_recv_port(mach_connection_name.c_str());
   kern_return_t err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
   if (err != KERN_SUCCESS) {
     std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
@@ -685,17 +685,17 @@ GeckoChildProcessHost::PerformAsyncLaunc
 #else
 #  error Sorry
 #endif
 
   if (!process) {
     return false;
   }
   SetHandle(process);
-#if defined(XP_MACOSX)
+#if defined(MOZ_WIDGET_COCOA)
   mChildTask = child_task;
 #endif
 
   return true;
 }
 
 void
 GeckoChildProcessHost::OnChannelConnected(int32 peer_pid)
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -341,17 +341,17 @@ unsigned char _BitScanReverse64(unsigned
  */
 #define JS_FLOOR_LOG2W(n) (JS_ASSERT((n) != 0), js_FloorLog2wImpl(n))
 
 #if JS_BYTES_PER_WORD == 4
 # ifdef JS_HAS_BUILTIN_BITSCAN32
 #  define js_FloorLog2wImpl(n)                                                \
     ((size_t)(JS_BITS_PER_WORD - 1 - js_bitscan_clz32(n)))
 # else
-#  define js_FloorLog2wImpl(n) ((size_t)JS_FloorLog2(n))
+extern size_t js_FloorLog2wImpl(size_t n);
 # endif
 #elif JS_BYTES_PER_WORD == 8
 # ifdef JS_HAS_BUILTIN_BITSCAN64
 #  define js_FloorLog2wImpl(n)                                                \
     ((size_t)(JS_BITS_PER_WORD - 1 - js_bitscan_clz64(n)))
 # else
 extern size_t js_FloorLog2wImpl(size_t n);
 # endif
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -43,66 +43,180 @@
 #include "builtin/RegExp.h"
 
 #include "vm/RegExpObject-inl.h"
 #include "vm/RegExpStatics-inl.h"
 
 using namespace js;
 using namespace js::types;
 
-/*
- * Return:
- * - The original if no escaping need be performed.
- * - A new string if escaping need be performed.
- * - NULL on error.
- */
-static JSString *
-EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped)
+class RegExpMatchBuilder
+{
+    JSContext   * const cx;
+    JSObject    * const array;
+
+    bool setProperty(JSAtom *name, Value v) {
+        return !!js_DefineProperty(cx, array, ATOM_TO_JSID(name), &v,
+                                   JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
+    }
+
+  public:
+    RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {}
+
+    bool append(uint32 index, Value v) {
+        JS_ASSERT(!array->getOps()->getElement);
+        return !!js_DefineElement(cx, array, index, &v, JS_PropertyStub, JS_StrictPropertyStub,
+                                  JSPROP_ENUMERATE);
+    }
+
+    bool setIndex(int index) {
+        return setProperty(cx->runtime->atomState.indexAtom, Int32Value(index));
+    }
+
+    bool setInput(JSString *str) {
+        JS_ASSERT(str);
+        return setProperty(cx->runtime->atomState.inputAtom, StringValue(str));
+    }
+};
+
+static bool
+CreateRegExpMatchResult(JSContext *cx, JSString *input, const jschar *chars, size_t length,
+                        MatchPairs *matchPairs, Value *rval)
+{
+    /*
+     * Create the (slow) result array for a match.
+     *
+     * Array contents:
+     *  0:              matched string
+     *  1..pairCount-1: paren matches
+     *  input:          input string
+     *  index:          start index for the match
+     */
+    JSObject *array = NewSlowEmptyArray(cx);
+    if (!array)
+        return false;
+
+    if (!input) {
+        input = js_NewStringCopyN(cx, chars, length);
+        if (!input)
+            return false;
+    }
+
+    RegExpMatchBuilder builder(cx, array);
+
+    for (size_t i = 0; i < matchPairs->pairCount(); ++i) {
+        MatchPair pair = matchPairs->pair(i);
+
+        JSString *captured;
+        if (pair.isUndefined()) {
+            JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
+            if (!builder.append(i, UndefinedValue()))
+                return false;
+        } else {
+            captured = js_NewDependentString(cx, input, pair.start, pair.length());
+            if (!captured || !builder.append(i, StringValue(captured)))
+                return false;
+        }
+    }
+
+    if (!builder.setIndex(matchPairs->pair(0).start) || !builder.setInput(input))
+        return false;
+
+    *rval = ObjectValue(*array);
+    return true;
+}
+
+template <class T>
+bool
+ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T *re, JSLinearString *input,
+                  const jschar *chars, size_t length,
+                  size_t *lastIndex, RegExpExecType type, Value *rval)
+{
+    LifoAllocScope allocScope(&cx->tempLifoAlloc());
+    MatchPairs *matchPairs = NULL;
+    RegExpRunStatus status = re->execute(cx, chars, length, lastIndex, allocScope, &matchPairs);
+
+    switch (status) {
+      case RegExpRunStatus_Error:
+        return false;
+      case RegExpRunStatus_Success_NotFound:
+        *rval = NullValue();
+        return true;
+      default:
+        JS_ASSERT(status == RegExpRunStatus_Success);
+        JS_ASSERT(matchPairs);
+    }
+
+    if (res)
+        res->updateFromMatchPairs(cx, input, matchPairs);
+
+    *lastIndex = matchPairs->pair(0).limit;
+
+    if (type == RegExpTest) {
+        *rval = BooleanValue(true);
+        return true;
+    }
+
+    return CreateRegExpMatchResult(cx, input, chars, length, matchPairs, rval);
+}
+
+bool
+js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpPrivate *rep, JSLinearString *input,
+                  const jschar *chars, size_t length,
+                  size_t *lastIndex, RegExpExecType type, Value *rval)
+{
+    return ExecuteRegExpImpl(cx, res, rep, input, chars, length, lastIndex, type, rval);
+}
+
+bool
+js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj, JSLinearString *input,
+                  const jschar *chars, size_t length,
+                  size_t *lastIndex, RegExpExecType type, Value *rval)
+{
+    return ExecuteRegExpImpl(cx, res, reobj, input, chars, length, lastIndex, type, rval);
+}
+
+/* Note: returns the original if no escaping need be performed. */
+static JSLinearString *
+EscapeNakedForwardSlashes(JSContext *cx, JSLinearString *unescaped)
 {
     size_t oldLen = unescaped->length();
-    const jschar *oldChars = unescaped->getChars(cx);
-    if (!oldChars)
-        return NULL;
+    const jschar *oldChars = unescaped->chars();
+
     JS::Anchor<JSString *> anchor(unescaped);
 
-    js::Vector<jschar, 128> newChars(cx);
+    /* We may never need to use |sb|. Start using it lazily. */
+    StringBuffer sb(cx);
+
     for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) {
         if (*it == '/' && (it == oldChars || it[-1] != '\\')) {
-            if (!newChars.length()) {
-                if (!newChars.reserve(oldLen + 1))
+            /* There's a forward slash that needs escaping. */
+            if (sb.empty()) {
+                /* This is the first one we've seen, copy everything up to this point. */
+                if (!sb.reserve(oldLen + 1))
                     return NULL;
-                newChars.infallibleAppend(oldChars, size_t(it - oldChars));
+                sb.infallibleAppend(oldChars, size_t(it - oldChars));
             }
-            if (!newChars.append('\\'))
+            if (!sb.append('\\'))
                 return NULL;
         }
 
-        if (!newChars.empty() && !newChars.append(*it))
+        if (!sb.empty() && !sb.append(*it))
             return NULL;
     }
 
-    if (newChars.empty())
-        return unescaped;
-
-    size_t len = newChars.length();
-    if (!newChars.append('\0'))
-        return NULL;
-    jschar *chars = newChars.extractRawBuffer();
-    JSString *escaped = js_NewString(cx, chars, len);
-    if (!escaped)
-        cx->free_(chars);
-    return escaped;
+    return sb.empty() ? unescaped : sb.finishString();
 }
 
 static bool
 ResetRegExpObjectWithStatics(JSContext *cx, RegExpObject *reobj,
-                             JSString *str, RegExpFlag flags = RegExpFlag(0))
+                             JSLinearString *str, RegExpFlag flags = RegExpFlag(0))
 {
     flags = RegExpFlag(flags | cx->regExpStatics()->getFlags());
-    return ResetRegExpObject(cx, reobj, str, flags);
+    return reobj->reset(cx, str, flags);
 }
 
 /*
  * Compile a new |RegExpPrivate| for the |RegExpObject|.
  *
  * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
  * arguments:
  *
@@ -120,61 +234,62 @@ CompileRegExpObject(JSContext *cx, RegEx
         *rval = ObjectValue(*obj);
         return true;
     }
 
     Value sourceValue = argv[0];
     if (ValueIsRegExp(sourceValue)) {
         /*
          * If we get passed in a |RegExpObject| source we return a new
-         * object with the same |RegExpPrivate|.
+         * object with the same source/flags.
          *
          * Note: the regexp static flags are not taken into consideration here.
          */
         JSObject &sourceObj = sourceValue.toObject();
         if (argc >= 2 && !argv[1].isUndefined()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED);
             return false;
         }
 
-        RegExpPrivate *rep = sourceObj.asRegExp()->getPrivate();
-        if (!rep)
-            return false;
-
-        rep->incref(cx);
-        if (!ResetRegExpObject(cx, obj, AlreadyIncRefed<RegExpPrivate>(rep)))
+        if (!obj->reset(cx, sourceObj.asRegExp()))
             return false;
         *rval = ObjectValue(*obj);
         return true;
     }
 
-    JSString *sourceStr;
+    JSLinearString *sourceStr;
     if (sourceValue.isUndefined()) {
         sourceStr = cx->runtime->emptyString;
     } else {
         /* Coerce to string and compile. */
-        sourceStr = js_ValueToString(cx, sourceValue);
+        JSString *str = js_ValueToString(cx, sourceValue);
+        if (!str)
+            return false;
+        sourceStr = str->ensureLinear(cx);
         if (!sourceStr)
             return false;
     }
 
     RegExpFlag flags = RegExpFlag(0);
     if (argc > 1 && !argv[1].isUndefined()) {
         JSString *flagStr = js_ValueToString(cx, argv[1]);
         if (!flagStr)
             return false;
         argv[1].setString(flagStr);
         if (!ParseRegExpFlags(cx, flagStr, &flags))
             return false;
     }
 
-    JSString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
+    JSLinearString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
     if (!escapedSourceStr)
         return false;
 
+    if (!RegExpPrivateCode::checkSyntax(cx, NULL, escapedSourceStr))
+        return false;
+
     if (!ResetRegExpObjectWithStatics(cx, obj, escapedSourceStr, flags))
         return false;
     *rval = ObjectValue(*obj);
     return true;
 }
 
 static JSBool
 regexp_compile(JSContext *cx, uintN argc, Value *vp)
@@ -182,19 +297,17 @@ regexp_compile(JSContext *cx, uintN argc
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
     JSObject *obj = NonGenericMethodGuard(cx, args, regexp_compile, &RegExpClass, &ok);
     if (!obj)
         return ok;
 
     RegExpObject *reobj = obj->asRegExp();
-    ok = CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval());
-    JS_ASSERT_IF(ok, reobj->getPrivate());
-    return ok;
+    return CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval());
 }
 
 static JSBool
 regexp_construct(JSContext *cx, uintN argc, Value *vp)
 {
     Value *argv = JS_ARGV(cx, vp);
 
     if (!IsConstructing(vp)) {
@@ -208,26 +321,20 @@ regexp_construct(JSContext *cx, uintN ar
             return true;
         }
     }
 
     JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
     if (!obj)
         return false;
 
-    PreInitRegExpObject pireo(obj);
-    RegExpObject *reobj = pireo.get();
+    if (!CompileRegExpObject(cx, obj->asRegExp(), argc, argv, &JS_RVAL(cx, vp)))
+        return false;
 
-    if (!CompileRegExpObject(cx, reobj, argc, argv, &JS_RVAL(cx, vp))) {
-        pireo.fail();
-        return false;
-    }
-
-    pireo.succeed();
-    *vp = ObjectValue(*reobj);
+    *vp = ObjectValue(*obj);
     return true;
 }
 
 static JSBool
 regexp_toString(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -351,40 +458,19 @@ js_InitRegExpClass(JSContext *cx, JSObje
     JS_ASSERT(obj->isNative());
 
     GlobalObject *global = obj->asGlobal();
 
     JSObject *proto = global->createBlankPrototype(cx, &RegExpClass);
     if (!proto)
         return NULL;
 
-    {
-        AlreadyIncRefed<RegExpPrivate> rep =
-          RegExpPrivate::create(cx, cx->runtime->emptyString, RegExpFlag(0), NULL);
-        if (!rep)
-            return NULL;
-
-        /*
-         * Associate the empty regular expression with |RegExp.prototype|, and define
-         * the initial non-method properties of any regular expression instance.
-         * These must be added before methods to preserve slot layout.
-         */
-#ifdef DEBUG
-        assertSameCompartment(cx, proto, rep->compartment);
-#endif
-
-        PreInitRegExpObject pireo(proto);
-        RegExpObject *reproto = pireo.get();
-        if (!ResetRegExpObject(cx, reproto, rep)) {
-            pireo.fail();
-            return NULL;
-        }
-
-        pireo.succeed();
-    }
+    RegExpObject *reproto = proto->asRegExp();
+    if (!reproto->reset(cx, cx->runtime->emptyString, RegExpFlag(0)))
+        return NULL;
 
     if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods))
         return NULL;
 
     JSFunction *ctor = global->createConstructor(cx, regexp_construct, &RegExpClass,
                                                  CLASS_ATOM(cx, RegExp), 2);
     if (!ctor)
         return NULL;
@@ -414,73 +500,78 @@ js_InitRegExpClass(JSContext *cx, JSObje
 }
 
 /*
  * ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2).
  *
  * RegExp.prototype.test doesn't need to create a results array, and we use
  * |execType| to perform this optimization.
  */
-static JSBool
+static bool
 ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     bool ok;
     JSObject *obj = NonGenericMethodGuard(cx, args, native, &RegExpClass, &ok);
     if (!obj)
         return ok;
 
     RegExpObject *reobj = obj->asRegExp();
-    RegExpPrivate *re = reobj->getPrivate();
-    if (!re)
+    RegExpPrivate *rep = reobj->getOrCreatePrivate(cx);
+    if (!rep)
         return true;
 
     /*
      * Code execution under this call could swap out the guts of |reobj|, so we
      * have to take a defensive refcount here.
      */
-    AutoRefCount<RegExpPrivate> arc(cx, NeedsIncRef<RegExpPrivate>(re));
+    AutoRefCount<RegExpPrivate> arc(cx, NeedsIncRef<RegExpPrivate>(rep));
     RegExpStatics *res = cx->regExpStatics();
 
     /* Step 2. */
-    JSString *input = js_ValueToString(cx, args.length() > 0 ?  args[0] : UndefinedValue());    
+    JSString *input = js_ValueToString(cx, (args.length() > 0) ? args[0] : UndefinedValue());
     if (!input)
         return false;
-    
+
     /* Step 3. */
+    JSLinearString *linearInput = input->ensureLinear(cx);
+    const jschar *chars = linearInput->chars();
     size_t length = input->length();
 
     /* Step 4. */
     const Value &lastIndex = reobj->getLastIndex();
 
     /* Step 5. */
     jsdouble i;
     if (!ToInteger(cx, lastIndex, &i))
         return false;
 
     /* Steps 6-7 (with sticky extension). */
-    if (!re->global() && !re->sticky())
+    if (!rep->global() && !rep->sticky())
         i = 0;
 
     /* Step 9a. */
     if (i < 0 || i > length) {
         reobj->zeroLastIndex();
         args.rval() = NullValue();
         return true;
     }
 
     /* Steps 8-21. */
+    RegExpExecType execType = (native == regexp_test) ? RegExpTest : RegExpExec;
     size_t lastIndexInt(i);
-    if (!re->execute(cx, res, input, &lastIndexInt, native == regexp_test, &args.rval()))
+    if (!ExecuteRegExp(cx, res, rep, linearInput, chars, length, &lastIndexInt, execType,
+                       &args.rval())) {
         return false;
+    }
 
     /* Step 11 (with sticky extension). */
-    if (re->global() || (!args.rval().isNull() && re->sticky())) {
+    if (rep->global() || (!args.rval().isNull() && rep->sticky())) {
         if (args.rval().isNull())
             reobj->zeroLastIndex();
         else
             reobj->setLastIndex(lastIndexInt);
     }
 
     return true;
 }
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -48,16 +48,31 @@ js_InitRegExpClass(JSContext *cx, JSObje
 
 /*
  * The following builtin natives are extern'd for pointer comparison in
  * other parts of the engine.
  */
 
 namespace js {
 
+/* 
+ * |res| may be null if the |RegExpStatics| are not to be updated.
+ * |input| may be null if there is no |JSString| corresponding to
+ * |chars| and |length|.
+ */
+bool
+ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj, JSLinearString *input,
+              const jschar *chars, size_t length,
+              size_t *lastIndex, RegExpExecType type, Value *rval);
+
+bool
+ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpPrivate *rep, JSLinearString *input,
+              const jschar *chars, size_t length,
+              size_t *lastIndex, RegExpExecType type, Value *rval);
+
 extern JSBool
 regexp_exec(JSContext *cx, uintN argc, Value *vp);
 
 extern JSBool
 regexp_test(JSContext *cx, uintN argc, Value *vp);
 
 } /* namespace js */
 
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -319,16 +319,20 @@ class LifoAllocScope {
         mark = lifoAlloc->mark();
     }
 
     ~LifoAllocScope() {
         if (shouldRelease)
             lifoAlloc->release(mark);
     }
 
+    LifoAlloc &alloc() {
+        return *lifoAlloc;
+    }
+
     void releaseEarly() {
         JS_ASSERT(shouldRelease);
         lifoAlloc->release(mark);
         shouldRelease = false;
     }
 };
 
 } /* namespace js */
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5956,26 +5956,19 @@ JS_ClearRegExpStatics(JSContext *cx, JSO
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteRegExp(JSContext *cx, JSObject *obj, JSObject *reobj, jschar *chars, size_t length,
                  size_t *indexp, JSBool test, jsval *rval)
 {
     CHECK_REQUEST(cx);
 
-    RegExpPrivate *rep = reobj->asRegExp()->getPrivate();
-    if (!rep)
-        return false;
-
-    JSString *str = js_NewStringCopyN(cx, chars, length);
-    if (!str)
-        return false;
-
     RegExpStatics *res = obj->asGlobal()->getRegExpStatics();
-    return rep->execute(cx, res, str, indexp, test, rval);
+    return ExecuteRegExp(cx, res, reobj->asRegExp(), NULL, chars, length,
+                         indexp, test ? RegExpTest : RegExpExec, rval);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewRegExpObjectNoStatics(JSContext *cx, char *bytes, size_t length, uintN flags)
 {
     CHECK_REQUEST(cx);
     jschar *chars = InflateString(cx, bytes, &length);
     if (!chars)
@@ -5993,25 +5986,18 @@ JS_NewUCRegExpObjectNoStatics(JSContext 
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *obj, jschar *chars, size_t length,
                           size_t *indexp, JSBool test, jsval *rval)
 {
     CHECK_REQUEST(cx);
 
-    RegExpPrivate *rep = obj->asRegExp()->getPrivate();
-    if (!rep)
-        return false;
-
-    JSString *str = js_NewStringCopyN(cx, chars, length);
-    if (!str)
-        return false;
-
-    return rep->executeNoStatics(cx, str, indexp, test, rval);
+    return ExecuteRegExp(cx, NULL, obj->asRegExp(), NULL, chars, length, indexp,
+                         test ? RegExpTest : RegExpExec, rval);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ObjectIsRegExp(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj);
     return obj->isRegExp();
 }
--- a/js/src/jslog2.cpp
+++ b/js/src/jslog2.cpp
@@ -49,16 +49,27 @@ JS_STATIC_ASSERT(sizeof(unsigned int) ==
 JS_STATIC_ASSERT_IF(JS_BYTES_PER_WORD == 4,
                     sizeof(unsigned int) == sizeof(JSUword));
 #endif
 #ifdef JS_HAS_BUILTIN_BITSCAN64
 JS_STATIC_ASSERT_IF(JS_BYTES_PER_WORD == 8,
                     sizeof(unsigned long long) == sizeof(JSUword));
 #endif
 
+#if !defined(JS_HAS_BUILTIN_BITSCAN32) && JS_BYTES_PER_WORD == 4
+
+size_t
+js_FloorLog2wImpl(size_t n)
+{
+    size_t log2;
+
+    JS_FLOOR_LOG2(log2, n);
+    return log2;
+}
+#endif
 /*
  * js_FloorLog2wImpl has to be defined only for 64-bit non-GCC case.
  */
 #if !defined(JS_HAS_BUILTIN_BITSCAN64) && JS_BYTES_PER_WORD == 8
 
 size_t
 js_FloorLog2wImpl(size_t n)
 {
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -123,27 +123,35 @@ class JSAtom;
 struct JSDefinition;
 class JSWrapper;
 
 namespace js {
 
 struct ArgumentsData;
 struct Class;
 
+class RegExpObject;
 class RegExpPrivate;
 class RegExpStatics;
+class MatchPairs;
 
 enum RegExpFlag
 {
     IgnoreCaseFlag  = JS_BIT(0),
     GlobalFlag      = JS_BIT(1),
     MultilineFlag   = JS_BIT(2),
     StickyFlag      = JS_BIT(3)
 };
 
+enum RegExpExecType
+{
+    RegExpExec,
+    RegExpTest
+};
+
 class AutoStringRooter;
 class ExecuteArgsGuard;
 class InvokeFrameGuard;
 class InvokeArgsGuard;
 class StringBuffer;
 class TraceRecorder;
 struct TraceMonitor;
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -67,16 +67,17 @@
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsprobes.h"
 #include "jsscope.h"
 #include "jsstr.h"
 #include "jsversion.h"
 
+#include "builtin/RegExp.h"
 #include "vm/GlobalObject.h"
 #include "vm/RegExpObject.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsautooplen.h"        // generated headers last
 
 #include "vm/RegExpObject-inl.h"
@@ -512,31 +513,31 @@ js_str_toString(JSContext *cx, uintN arg
 
     args.rval().setString(str);
     return true;
 }
 
 /*
  * Java-like string native methods.
  */
- 
+
 JS_ALWAYS_INLINE bool
 ValueToIntegerRange(JSContext *cx, const Value &v, int32 *out)
 {
     if (v.isInt32()) {
         *out = v.toInt32();
     } else {
         double d;
         if (!ToInteger(cx, v, &d))
             return false;
         if (d > INT32_MAX)
             *out = INT32_MAX;
         else if (d < INT32_MIN)
             *out = INT32_MIN;
-        else 
+        else
             *out = int32(d);
     }
 
     return true;
 }
 
 static JSBool
 str_substring(JSContext *cx, uintN argc, Value *vp)
@@ -1248,53 +1249,59 @@ class FlatMatch
     const jschar    *pat;
     size_t          patlen;
     int32           match_;
 
     friend class RegExpGuard;
 
   public:
     FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
-    JSString *pattern() const { return patstr; }
+    JSLinearString *pattern() const { return patstr; }
     size_t patternLength() const { return patlen; }
 
     /*
      * Note: The match is -1 when the match is performed successfully,
      * but no match is found.
      */
     int32 match() const { return match_; }
 };
 
-/* A regexp and optional associated object. */
 class RegExpPair
 {
     AutoRefCount<RegExpPrivate> rep_;
-    JSObject                    *reobj_;
+    RegExpObject                *reobj_;
 
     explicit RegExpPair(RegExpPair &);
+    void operator=(const RegExpPair &);
 
   public:
     explicit RegExpPair(JSContext *cx) : rep_(cx) {}
 
-    void reset(JSObject &obj) {
-        reobj_ = &obj;
-        RegExpPrivate *rep = reobj_->asRegExp()->getPrivate();
-        JS_ASSERT(rep);
+    bool resetWithObject(JSContext *cx, RegExpObject *reobj) {
+        reobj_ = reobj;
+        RegExpPrivate *rep = reobj_->asRegExp()->getOrCreatePrivate(cx);
+        if (!rep)
+            return false;
         rep_.reset(NeedsIncRef<RegExpPrivate>(rep));
+        return true;
     }
 
-    void reset(AlreadyIncRefed<RegExpPrivate> rep) {
+    void resetWithPrivate(AlreadyIncRefed<RegExpPrivate> rep) {
         reobj_ = NULL;
         rep_.reset(rep);
     }
 
-    /* Note: May be null. */
-    JSObject *reobj() const { return reobj_; }
-    bool hasRegExp() const { return !rep_.null(); }
-    RegExpPrivate &re() const { JS_ASSERT(hasRegExp()); return *rep_; }
+    bool null() const { return rep_.null(); }
+
+    RegExpObject *reobj() const { return reobj_; }
+
+    RegExpPrivate *getPrivate() const {
+        JS_ASSERT(!null());
+        return rep_.get();
+    }
 };
 
 /*
  * RegExpGuard factors logic out of String regexp operations.
  *
  * @param optarg    Indicates in which argument position RegExp
  *                  flags will be found, if present. This is a Mozilla
  *                  extension and not part of any ECMA spec.
@@ -1309,17 +1316,17 @@ class RegExpGuard
     FlatMatch   fm;
 
     /*
      * Upper bound on the number of characters we are willing to potentially
      * waste on searching for RegExp meta-characters.
      */
     static const size_t MAX_FLAT_PAT_LEN = 256;
 
-    static JSString *flattenPattern(JSContext *cx, JSLinearString *patstr) {
+    static JSLinearString *flattenPattern(JSContext *cx, JSLinearString *patstr) {
         StringBuffer sb(cx);
         if (!sb.reserve(patstr->length()))
             return NULL;
 
         static const jschar ESCAPE_CHAR = '\\';
         const jschar *chars = patstr->chars();
         size_t len = patstr->length();
         for (const jschar *it = chars; it != chars + len; ++it) {
@@ -1338,23 +1345,23 @@ class RegExpGuard
     explicit RegExpGuard(JSContext *cx) : cx(cx), rep(cx) {}
     ~RegExpGuard() {}
 
     /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
     bool
     init(uintN argc, Value *vp, bool convertVoid = false)
     {
         if (argc != 0 && ValueIsRegExp(vp[2])) {
-            rep.reset(vp[2].toObject());
+            rep.resetWithObject(cx, vp[2].toObject().asRegExp());
         } else {
             if (convertVoid && (argc == 0 || vp[2].isUndefined())) {
                 fm.patstr = cx->runtime->emptyString;
                 return true;
             }
-            
+
             fm.patstr = ArgToRootedString(cx, argc, vp, 0);
             if (!fm.patstr)
                 return false;
         }
         return true;
     }
 
     /*
@@ -1365,17 +1372,17 @@ class RegExpGuard
      * @return                  Whether flat matching could be used.
      *
      * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending().
      */
     const FlatMatch *
     tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc,
                  bool checkMetaChars = true)
     {
-        if (rep.hasRegExp())
+        if (!rep.null())
             return NULL;
 
         fm.pat = fm.patstr->chars();
         fm.patlen = fm.patstr->length();
 
         if (optarg < argc)
             return NULL;
 
@@ -1398,56 +1405,56 @@ class RegExpGuard
         }
         return &fm;
     }
 
     /* If the pattern is not already a regular expression, make it so. */
     const RegExpPair *
     normalizeRegExp(bool flat, uintN optarg, uintN argc, Value *vp)
     {
-        if (rep.hasRegExp())
+        if (!rep.null())
             return &rep;
 
         /* Build RegExp from pattern string. */
         JSString *opt;
         if (optarg < argc) {
             opt = js_ValueToString(cx, vp[2 + optarg]);
             if (!opt)
                 return NULL;
         } else {
             opt = NULL;
         }
 
-        JSString *patstr;
+        JSLinearString *patstr;
         if (flat) {
             patstr = flattenPattern(cx, fm.patstr);
             if (!patstr)
                 return NULL;
         } else {
             patstr = fm.patstr;
         }
         JS_ASSERT(patstr);
 
-        AlreadyIncRefed<RegExpPrivate> re = RegExpPrivate::createFlagged(cx, patstr, opt, NULL);
+        AlreadyIncRefed<RegExpPrivate> re = RegExpPrivate::create(cx, patstr, opt, NULL);
         if (!re)
             return NULL;
-        rep.reset(re);
+        rep.resetWithPrivate(re);
         return &rep;
     }
 
 #if DEBUG
-    bool hasRegExpPair() const { return rep.hasRegExp(); }
+    bool hasRegExpPair() const { return !rep.null(); }
 #endif
 };
 
-/* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
+/* ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
 static JS_ALWAYS_INLINE bool
-Matched(bool test, const Value &v)
+Matched(RegExpExecType type, const Value &v)
 {
-    return test ? v.isTrue() : !v.isNull();
+    return (type == RegExpTest) ? v.isTrue() : !v.isNull();
 }
 
 typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data);
 
 /*
  * BitOR-ing these flags allows the DoMatch caller to control when how the
  * RegExp engine is called and when callbacks are fired.
  */
@@ -1458,43 +1465,50 @@ enum MatchControlFlags {
 
    MATCH_ARGS    = TEST_GLOBAL_BIT,
    MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
    REPLACE_ARGS  = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
 };
 
 /* Factor out looping and matching logic. */
 static bool
-DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &rep,
+DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &regExpPair,
         DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
 {
-    RegExpPrivate &re = rep.re();
-    if (re.global()) {
+    RegExpPrivate *rep = regExpPair.getPrivate();
+    JSLinearString *linearStr = str->ensureLinear(cx);
+    if (!linearStr)
+        return false;
+    const jschar *chars = linearStr->chars();
+    size_t length = linearStr->length();
+
+    if (rep->global()) {
         /* global matching ('g') */
-        bool testGlobal = flags & TEST_GLOBAL_BIT;
-        if (rep.reobj())
-            rep.reobj()->asRegExp()->zeroLastIndex();
+        RegExpExecType type = (flags & TEST_GLOBAL_BIT) ? RegExpTest : RegExpExec;
+        if (RegExpObject *reobj = regExpPair.reobj())
+            reobj->zeroLastIndex();
+
         for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
-            if (!re.execute(cx, res, str, &i, testGlobal, rval))
+            if (!ExecuteRegExp(cx, res, rep, linearStr, chars, length, &i, type, rval))
                 return false;
-            if (!Matched(testGlobal, *rval))
+            if (!Matched(type, *rval))
                 break;
             if (!callback(cx, res, count, data))
                 return false;
             if (!res->matched())
                 ++i;
         }
     } else {
         /* single match */
-        bool testSingle = !!(flags & TEST_SINGLE_BIT),
-             callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
+        RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec;
+        bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
         size_t i = 0;
-        if (!re.execute(cx, res, str, &i, testSingle, rval))
+        if (!ExecuteRegExp(cx, res, rep, linearStr, chars, length, &i, type, rval))
             return false;
-        if (callbackOnSingle && Matched(testSingle, *rval) && !callback(cx, res, 0, data))
+        if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data))
             return false;
     }
     return true;
 }
 
 static bool
 BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value *vp)
 {
@@ -1539,17 +1553,17 @@ MatchCallback(JSContext *cx, RegExpStati
 }
 
 JSBool
 js::str_match(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *str = ThisToStringForStringProto(cx, vp);
     if (!str)
         return false;
-    
+
     RegExpGuard g(cx);
     if (!g.init(argc, vp, true))
         return false;
     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc))
         return BuildFlatMatchArray(cx, str, *fm, vp);
     if (cx->isExceptionPending())  /* from tryFlatMatch */
         return false;
 
@@ -1559,17 +1573,17 @@ js::str_match(JSContext *cx, uintN argc,
 
     AutoObjectRooter array(cx);
     MatchArgType arg = array.addr();
     RegExpStatics *res = cx->regExpStatics();
     Value rval;
     if (!DoMatch(cx, res, str, *rep, MatchCallback, arg, MATCH_ARGS, &rval))
         return false;
 
-    if (rep->re().global())
+    if (rep->getPrivate()->global())
         vp->setObjectOrNull(array.object());
     else
         *vp = rval;
     return true;
 }
 
 JSBool
 js::str_search(JSContext *cx, uintN argc, Value *vp)
@@ -1582,24 +1596,30 @@ js::str_search(JSContext *cx, uintN argc
     if (!g.init(argc, vp, true))
         return false;
     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc)) {
         vp->setInt32(fm->match());
         return true;
     }
     if (cx->isExceptionPending())  /* from tryFlatMatch */
         return false;
-    
+
     const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
     if (!rep)
         return false;
 
+    JSLinearString *linearStr = str->ensureLinear(cx);
+    if (!linearStr)
+        return false;
+    const jschar *chars = linearStr->chars();
+    size_t length = linearStr->length();
     RegExpStatics *res = cx->regExpStatics();
+    /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
     size_t i = 0;
-    if (!rep->re().execute(cx, res, str, &i, true, vp))
+    if (!ExecuteRegExp(cx, res, rep->getPrivate(), linearStr, chars, length, &i, RegExpTest, vp))
         return false;
 
     if (vp->isTrue())
         vp->setInt32(res->matchStart());
     else
         vp->setInt32(-1);
     return true;
 }
@@ -1652,17 +1672,17 @@ InterpretDollar(JSContext *cx, RegExpSta
         }
         if (num == 0)
             return false;
 
         *skip = cp - dp;
 
         JS_ASSERT(num <= res->parenCount());
 
-        /* 
+        /*
          * Note: we index to get the paren with the (1-indexed) pair
          * number, as opposed to a (0-indexed) paren number.
          */
         res->getParen(num, out);
         return true;
     }
 
     *skip = 2;
@@ -1810,17 +1830,17 @@ FindReplaceLength(JSContext *cx, RegExpS
         } else {
             dp++;
         }
     }
     *sizep = replen;
     return true;
 }
 
-/* 
+/*
  * Precondition: |rdata.sb| already has necessary growth space reserved (as
  * derived from FindReplaceLength).
  */
 static void
 DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata)
 {
     JSLinearString *repstr = rdata.repstr;
     const jschar *cp;
@@ -2207,17 +2227,17 @@ js::str_replace(JSContext *cx, uintN arg
     if (fm->match() < 0) {
         vp->setString(rdata.str);
         return true;
     }
 
     if (rdata.lambda)
         return str_replace_flat_lambda(cx, argc, vp, rdata, *fm);
 
-    /* 
+    /*
      * Note: we could optimize the text.length == pattern.length case if we wanted,
      * even in the presence of dollar metachars.
      */
     if (rdata.dollar)
         return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollar, *fm, vp);
 
     return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, vp);
 }
@@ -2378,30 +2398,32 @@ SplitHelper(JSContext *cx, JSLinearStrin
  * The SplitMatch operation from ES5 15.5.4.14 is implemented using different
  * matchers for regular expression and string separators.
  *
  * The algorithm differs from the spec in that the matchers return the next
  * index at which a match happens.
  */
 class SplitRegExpMatcher {
     RegExpStatics *res;
-    RegExpPrivate *re;
+    RegExpPrivate *rep;
 
   public:
     static const bool returnsCaptures = true;
-    SplitRegExpMatcher(RegExpPrivate *re, RegExpStatics *res) : res(res), re(re) {}
+    SplitRegExpMatcher(RegExpPrivate *rep, RegExpStatics *res) : res(res), rep(rep) {}
 
     inline bool operator()(JSContext *cx, JSLinearString *str, size_t index,
                            SplitMatchResult *result) {
         Value rval
 #ifdef __GNUC__ /* quell GCC overwarning */
             = UndefinedValue()
 #endif
         ;
-        if (!re->execute(cx, res, str, &index, true, &rval))
+        const jschar *chars = str->chars();
+        size_t length = str->length();
+        if (!ExecuteRegExp(cx, res, rep, str, chars, length, &index, RegExpTest, &rval))
             return false;
         if (!rval.isTrue()) {
             result->setFailure();
             return true;
         }
         JSSubString sep;
         res->getLastMatch(&sep);
 
@@ -2460,17 +2482,19 @@ js::str_split(JSContext *cx, uintN argc,
     }
 
     /* Step 8. */
     RegExpPrivate *re = NULL;
     JSLinearString *sepstr = NULL;
     bool sepUndefined = (argc == 0 || vp[2].isUndefined());
     if (!sepUndefined) {
         if (ValueIsRegExp(vp[2])) {
-            re = vp[2].toObject().asRegExp()->getPrivate();
+            re = vp[2].toObject().asRegExp()->getOrCreatePrivate(cx);
+            if (!re)
+                return false;
         } else {
             JSString *sep = js_ValueToString(cx, vp[2]);
             if (!sep)
                 return false;
             vp[2].setString(sep);
 
             sepstr = sep->ensureLinear(cx);
             if (!sepstr)
@@ -2541,17 +2565,17 @@ str_substr(JSContext *cx, uintN argc, Va
             begin += length; /* length + INT_MIN will always be less then 0 */
             if (begin < 0)
                 begin = 0;
         }
 
         if (argc == 1 || vp[3].isUndefined()) {
             len = length - begin;
         } else {
-            if (!ValueToIntegerRange(cx, vp[3], &len))  
+            if (!ValueToIntegerRange(cx, vp[3], &len))
                 return false;
 
             if (len <= 0) {
                 str = cx->runtime->emptyString;
                 goto out;
             }
 
             if (uint32(length) < uint32(begin + len))
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -6588,19 +6588,19 @@ mjit::Compiler::jsop_regexp()
                                        types::OBJECT_FLAG_REGEXP_FLAGS_SET)) {
         prepareStubCall(Uses(0));
         masm.move(ImmPtr(obj), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
         frame.pushSynced(JSVAL_TYPE_OBJECT);
         return;
     }
 
-    RegExpPrivate *regexp = static_cast<RegExpObject *>(obj)->getPrivate();
-
-    DebugOnly<uint32> origFlags = regexp->getFlags();
+    RegExpObject *reobj = obj->asRegExp();
+
+    DebugOnly<uint32> origFlags = reobj->getFlags();
     DebugOnly<uint32> staticsFlags = res->getFlags();
     JS_ASSERT((origFlags & staticsFlags) == staticsFlags);
 
     /*
      * JS semantics require regular expression literals to create different
      * objects every time they execute. We only need to do this cloning if the
      * script could actually observe the effect of such cloning, by getting
      * or setting properties on it. Particular RegExp and String natives take
@@ -6642,20 +6642,16 @@ mjit::Compiler::jsop_regexp()
     Jump emptyFreeList = masm.getNewObject(cx, result, obj);
 
     stubcc.linkExit(emptyFreeList, Uses(0));
     stubcc.leave();
 
     stubcc.masm.move(ImmPtr(obj), Registers::ArgReg1);
     OOL_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
 
-    /* Bump the refcount on the wrapped RegExp. */
-    size_t *refcount = regexp->addressOfRefCount();
-    masm.add32(Imm32(1), AbsoluteAddress(refcount));
-
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result);
 
     stubcc.rejoin(Changes(1));
 }
 
 bool
 mjit::Compiler::startLoop(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
 {
new file mode 100644
--- /dev/null
+++ b/js/src/vm/MatchPairs.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=99 ft=cpp:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla SpiderMonkey JavaScript code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Chris Leary <cdleary@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef MatchPairs_h__
+#define MatchPairs_h__
+
+/*
+ * RegExp match results are succinctly represented by pairs of integer
+ * indices delimiting (start, limit] segments of the input string.
+ *
+ * The pair count for a given RegExp match is the capturing parentheses
+ * count plus one for the "0 capturing paren" whole text match.
+ */
+
+namespace js {
+
+struct MatchPair
+{
+    int start;
+    int limit;
+
+    MatchPair(int start, int limit) : start(start), limit(limit) {}
+
+    size_t length() const {
+        JS_ASSERT(!isUndefined());
+        return limit - start;
+    }
+
+    bool isUndefined() const {
+        return start == -1;
+    }
+
+    void check() const {
+        JS_ASSERT(limit >= start);
+        JS_ASSERT_IF(!isUndefined(), start >= 0);
+    }
+};
+
+class MatchPairs
+{
+    size_t  pairCount_;
+    int     buffer_[1];
+
+    explicit MatchPairs(size_t pairCount) : pairCount_(pairCount) {
+        initPairValues();
+    }
+
+    void initPairValues() {
+        for (int *it = buffer_; it < buffer_ + 2 * pairCount_; ++it)
+            *it = -1;
+    }
+
+    static size_t calculateSize(size_t backingPairCount) {
+        return sizeof(MatchPairs) - sizeof(int) + sizeof(int) * backingPairCount;
+    }
+
+    int *buffer() { return buffer_; }
+
+    friend class RegExpPrivate;
+
+  public:
+    /*
+     * |backingPairCount| is necessary because PCRE uses extra space
+     * after the actual results in the buffer.
+     */
+    static MatchPairs *create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount);
+
+    size_t pairCount() const { return pairCount_; }
+
+    MatchPair pair(size_t i) {
+        JS_ASSERT(i < pairCount());
+        return MatchPair(buffer_[2 * i], buffer_[2 * i + 1]);
+    }
+
+    void displace(size_t amount) {
+        if (!amount)
+            return;
+
+        for (int *it = buffer_; it < buffer_ + 2 * pairCount_; ++it)
+            *it = (*it < 0) ? -1 : *it + amount;
+    }
+
+    inline void checkAgainst(size_t length);
+};
+
+} /* namespace js */
+
+#endif
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -47,59 +47,21 @@
 #include "jsobjinlines.h"
 #include "jsstrinlines.h"
 #include "RegExpStatics-inl.h"
 
 inline js::RegExpObject *
 JSObject::asRegExp()
 {
     JS_ASSERT(isRegExp());
-    js::RegExpObject *reobj = static_cast<js::RegExpObject *>(this);
-    JS_ASSERT(reobj->getPrivate());
-    return reobj;
+    return static_cast<js::RegExpObject *>(this);
 }
 
 namespace js {
 
-/*
- * Maintains the post-initialization invariant of having a RegExpPrivate.
- *
- * N.B. If initialization fails, the |RegExpPrivate| will be null, so
- * finalization must consider that as a possibility.
- */
-class PreInitRegExpObject
-{
-    RegExpObject    *reobj;
-    DebugOnly<bool> gotResult;
-
-  public:
-    explicit PreInitRegExpObject(JSObject *obj) {
-        JS_ASSERT(obj->isRegExp());
-        reobj = static_cast<RegExpObject *>(obj);
-        gotResult = false;
-    }
-
-    ~PreInitRegExpObject() {
-        JS_ASSERT(gotResult);
-    }
-
-    RegExpObject *get() { return reobj; }
-
-    void succeed() {
-        JS_ASSERT(!gotResult);
-        JS_ASSERT(reobj->getPrivate());
-        gotResult = true;
-    }
-
-    void fail() {
-        JS_ASSERT(!gotResult);
-        gotResult = true;
-    }
-};
-
 inline bool
 ValueIsRegExp(const Value &v)
 {
     return !v.isPrimitive() && v.toObject().isRegExp();
 }
 
 inline bool
 IsRegExpMetaChar(jschar c)
@@ -120,78 +82,80 @@ HasRegExpMetaChars(const jschar *chars, 
 {
     for (size_t i = 0; i < length; ++i) {
         if (IsRegExpMetaChar(chars[i]))
             return true;
     }
     return false;
 }
 
-inline bool
-ResetRegExpObject(JSContext *cx, RegExpObject *reobj, JSString *str, RegExpFlag flags)
-{
-    AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, str, flags, NULL);
-    if (!rep)
-        return false;
-
-    return reobj->reset(cx, rep);
-}
-
-inline bool
-ResetRegExpObject(JSContext *cx, RegExpObject *reobj, AlreadyIncRefed<RegExpPrivate> rep)
-{
-    return reobj->reset(cx, rep);
-}
-
 inline RegExpObject *
 RegExpObject::create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
-                     RegExpFlag flags, TokenStream *ts)
+                     RegExpFlag flags, TokenStream *tokenStream)
 {
     RegExpFlag staticsFlags = res->getFlags();
-    return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), ts);
+    return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), tokenStream);
 }
 
 inline RegExpObject *
 RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length,
-                              RegExpFlag flags, TokenStream *ts)
+                              RegExpFlag flags, TokenStream *tokenStream)
 {
-    JSString *str = js_NewStringCopyN(cx, chars, length);
-    if (!str)
+    JSLinearString *source = js_NewStringCopyN(cx, chars, length);
+    if (!source)
         return NULL;
 
-    /*
-     * |NewBuiltinClassInstance| can GC before we store |re| in the
-     * private field of the object. At that point the only reference to
-     * the source string could be from the malloc-allocated GC-invisible
-     * |re|. So we must anchor.
-     */
-    JS::Anchor<JSString *> anchor(str);
-    AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, str, flags, ts);
-    if (!rep)
+    /* |NewBuiltinClassInstance| can GC. */
+    JS::Anchor<JSString *> anchor(source);
+
+    if (!RegExpPrivateCode::checkSyntax(cx, tokenStream, source))
         return NULL;
 
     JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
     if (!obj)
         return NULL;
 
-    PreInitRegExpObject pireo(obj);
-    RegExpObject *reobj = pireo.get();
-    if (!ResetRegExpObject(cx, reobj, rep)) {
+    RegExpObject *reobj = obj->asRegExp();
+    return reobj->reset(cx, source, flags) ? reobj : NULL;
+}
+
+inline void
+RegExpObject::finalize(JSContext *cx)
+{
+    if (RegExpPrivate *rep = getPrivate())
         rep->decref(cx);
-        pireo.fail();
-        return NULL;
-    }
-
-    pireo.succeed();
-    return reobj;
+#ifdef DEBUG
+    setPrivate((void *) 0x1); /* Non-null but still in the zero page. */
+#endif
 }
 
 inline bool
 RegExpObject::reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep)
 {
+    if (!reset(cx, rep->getSource(), rep->getFlags()))
+        return false;
+
+    setPrivate(rep.get());
+    return true;
+}
+
+inline bool
+RegExpObject::reset(JSContext *cx, RegExpObject *other)
+{
+    if (RegExpPrivate *rep = other->getPrivate()) {
+        rep->incref(cx);
+        return reset(cx, AlreadyIncRefed<RegExpPrivate>(rep));
+    }
+
+    return reset(cx, other->getSource(), other->getFlags());
+}
+
+inline bool
+RegExpObject::reset(JSContext *cx, JSLinearString *source, RegExpFlag flags)
+{
     if (nativeEmpty()) {
         const js::Shape **shapep = &cx->compartment->initialRegExpShape;
         if (!*shapep) {
             *shapep = assignInitialShape(cx);
             if (!*shapep)
                 return false;
         }
         setLastProperty(*shapep);
@@ -203,114 +167,30 @@ RegExpObject::reset(JSContext *cx, Alrea
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->sourceAtom))->slot == SOURCE_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->globalAtom))->slot == GLOBAL_FLAG_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->ignoreCaseAtom))->slot ==
                                  IGNORE_CASE_FLAG_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->multilineAtom))->slot ==
                                  MULTILINE_FLAG_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot == STICKY_FLAG_SLOT);
 
-    setPrivate(rep.get());
     zeroLastIndex();
-    setSource(rep->getSource());
-    setGlobal(rep->global());
-    setIgnoreCase(rep->ignoreCase());
-    setMultiline(rep->multiline());
-    setSticky(rep->sticky());
+    setPrivate(NULL);
+    setSource(source);
+    setGlobal(flags & GlobalFlag);
+    setIgnoreCase(flags & IgnoreCaseFlag);
+    setMultiline(flags & MultilineFlag);
+    setSticky(flags & StickyFlag);
     return true;
 }
 
 /* RegExpPrivate inlines. */
 
-class RegExpMatchBuilder
-{
-    JSContext   * const cx;
-    JSObject    * const array;
-
-    bool setProperty(JSAtom *name, Value v) {
-        return !!js_DefineProperty(cx, array, ATOM_TO_JSID(name), &v,
-                                   JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
-    }
-
-  public:
-    RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {}
-
-    bool append(uint32 index, Value v) {
-        JS_ASSERT(!array->getOps()->getElement);
-        return !!js_DefineElement(cx, array, index, &v, JS_PropertyStub, JS_StrictPropertyStub,
-                                  JSPROP_ENUMERATE);
-    }
-
-    bool setIndex(int index) {
-        return setProperty(cx->runtime->atomState.indexAtom, Int32Value(index));
-    }
-
-    bool setInput(JSString *str) {
-        JS_ASSERT(str);
-        return setProperty(cx->runtime->atomState.inputAtom, StringValue(str));
-    }
-};
-
-inline void
-RegExpPrivate::checkMatchPairs(JSString *input, int *buf, size_t matchItemCount)
-{
-#if DEBUG
-    size_t inputLength = input->length();
-    for (size_t i = 0; i < matchItemCount; i += 2) {
-        int start = buf[i];
-        int limit = buf[i + 1];
-        JS_ASSERT(limit >= start); /* Limit index must be larger than the start index. */
-        if (start == -1)
-            continue;
-        JS_ASSERT(start >= 0);
-        JS_ASSERT(size_t(limit) <= inputLength);
-    }
-#endif
-}
-
-inline JSObject *
-RegExpPrivate::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount)
-{
-    /*
-     * Create the result array for a match. Array contents:
-     *  0:              matched string
-     *  1..pairCount-1: paren matches
-     */
-    JSObject *array = NewSlowEmptyArray(cx);
-    if (!array)
-        return NULL;
-
-    RegExpMatchBuilder builder(cx, array);
-    for (size_t i = 0; i < matchItemCount; i += 2) {
-        int start = buf[i];
-        int end = buf[i + 1];
-
-        JSString *captured;
-        if (start >= 0) {
-            JS_ASSERT(start <= end);
-            JS_ASSERT(unsigned(end) <= input->length());
-            captured = js_NewDependentString(cx, input, start, end - start);
-            if (!captured || !builder.append(i / 2, StringValue(captured)))
-                return NULL;
-        } else {
-            /* Missing parenthesized match. */
-            JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
-            if (!builder.append(i / 2, UndefinedValue()))
-                return NULL;
-        }
-    }
-
-    if (!builder.setIndex(buf[0]) || !builder.setInput(input))
-        return NULL;
-
-    return array;
-}
-
 inline AlreadyIncRefed<RegExpPrivate>
-RegExpPrivate::create(JSContext *cx, JSString *source, RegExpFlag flags, TokenStream *ts)
+RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts)
 {
     typedef AlreadyIncRefed<RegExpPrivate> RetType;
 
     JSLinearString *flatSource = source->ensureLinear(cx);
     if (!flatSource)
         return RetType(NULL);
 
     RegExpPrivate *self = cx->new_<RegExpPrivate>(flatSource, flags, cx->compartment);
@@ -411,43 +291,43 @@ RegExpPrivate::compile(JSContext *cx, To
     sb.infallibleAppend(postfix, JS_ARRAY_LENGTH(postfix));
 
     JSLinearString *fakeySource = sb.finishString();
     if (!fakeySource)
         return false;
     return code.compile(cx, *fakeySource, ts, &parenCount, getFlags());
 }
 
-inline RegExpPrivateCode::ExecuteResult
-RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t start, size_t length,
+inline RegExpRunStatus
+RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
                            int *output, size_t outputCount)
 {
     int result;
 #if ENABLE_YARR_JIT
     (void) cx; /* Unused. */
     if (codeBlock.isFallBack())
         result = JSC::Yarr::interpret(byteCode, chars, start, length, output);
     else
         result = JSC::Yarr::execute(codeBlock, chars, start, length, output);
 #else
     result = jsRegExpExecute(cx, compiled, chars, length, start, output, outputCount);
 #endif
 
     if (result == -1)
-        return Success_NotFound;
+        return RegExpRunStatus_Success_NotFound;
 
 #if !ENABLE_YARR_JIT
     if (result < 0) {
         reportPCREError(cx, result);
-        return Error;
+        return RegExpRunStatus_Error;
     }
 #endif
 
     JS_ASSERT(result >= 0);
-    return Success;
+    return RegExpRunStatus_Success;
 }
 
 inline void
 RegExpPrivate::incref(JSContext *cx)
 {
 #ifdef DEBUG
     assertSameCompartment(cx, compartment);
 #endif
@@ -460,16 +340,25 @@ RegExpPrivate::decref(JSContext *cx)
 #ifdef DEBUG
     assertSameCompartment(cx, compartment);
 #endif
     if (--refCount == 0)
         cx->delete_(this);
 }
 
 inline RegExpPrivate *
+RegExpObject::getOrCreatePrivate(JSContext *cx)
+{
+    if (RegExpPrivate *rep = getPrivate())
+        return rep;
+
+    return makePrivate(cx) ? getPrivate() : NULL;
+}
+
+inline RegExpPrivate *
 RegExpObject::getPrivate() const
 {
     RegExpPrivate *rep = static_cast<RegExpPrivate *>(JSObject::getPrivate());
 #ifdef DEBUG
     if (rep)
         CompartmentChecker::check(compartment(), rep->compartment);
 #endif
     return rep;
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -36,16 +36,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "jsscan.h"
 
 #include "vm/RegExpStatics.h"
+#include "vm/MatchPairs.h"
 
 #include "jsobjinlines.h"
 #include "jsstrinlines.h"
 
 #include "vm/RegExpObject-inl.h"
 
 #ifdef JS_TRACER
 #include "jstracer.h"
@@ -54,100 +55,109 @@ using namespace nanojit;
 
 using namespace js;
 
 JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
 JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
 JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
 JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
 
-bool
-RegExpPrivate::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inputstr,
-                               size_t *lastIndex, bool test, Value *rval)
+MatchPairs *
+MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount)
+{
+    void *mem = alloc.alloc(calculateSize(backingPairCount));
+    if (!mem)
+        return NULL;
+
+    return new (mem) MatchPairs(pairCount);
+}
+
+inline void
+MatchPairs::checkAgainst(size_t inputLength)
 {
-    const size_t pairCount = parenCount + 1;
-    const size_t matchItemCount = pairCount * 2;
-    const size_t bufCount = RegExpPrivateCode::getOutputSize(pairCount);
+#if DEBUG
+    for (size_t i = 0; i < pairCount(); ++i) {
+        MatchPair p = pair(i);
+        p.check();
+        if (p.isUndefined())
+            continue;
+        JS_ASSERT(size_t(p.limit) <= inputLength);
+    }
+#endif
+}
 
-    LifoAllocScope las(&cx->tempLifoAlloc());
-    int *buf = cx->tempLifoAlloc().newArray<int>(bufCount);
-    if (!buf)
-        return false;
+RegExpRunStatus
+RegExpPrivate::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
+                       LifoAllocScope &allocScope, MatchPairs **output)
+{
+    const size_t origLength = length;
+    size_t backingPairCount = RegExpPrivateCode::getOutputSize(pairCount());
+
+    MatchPairs *matchPairs = MatchPairs::create(allocScope.alloc(), pairCount(), backingPairCount);
+    if (!matchPairs)
+        return RegExpRunStatus_Error;
 
     /*
-     * The JIT regexp procedure doesn't always initialize matchPair values.
-     * Maybe we can make this faster by ensuring it does?
+     * |displacement| emulates sticky mode by matching from this offset
+     * into the char buffer and subtracting the delta off at the end.
      */
-    for (int *it = buf; it != buf + matchItemCount; ++it)
-        *it = -1;
-    
-    JSLinearString *input = inputstr->ensureLinear(cx);
-    if (!input)
-        return false;
-
-    JS::Anchor<JSString *> anchor(input);
-    size_t len = input->length();
-    const jschar *chars = input->chars();
-
-    /* 
-     * inputOffset emulates sticky mode by matching from this offset into the char buf and
-     * subtracting the delta off at the end.
-     */
-    size_t inputOffset = 0;
+    size_t start = *lastIndex;
+    size_t displacement = 0;
 
     if (sticky()) {
-        /* Sticky matches at the last index for the regexp object. */
-        chars += *lastIndex;
-        len -= *lastIndex;
-        inputOffset = *lastIndex;
+        displacement = *lastIndex;
+        chars += displacement;
+        length -= displacement;
+        start = 0;
     }
 
-    size_t start = *lastIndex - inputOffset;
-    RegExpPrivateCode::ExecuteResult result = code.execute(cx, chars, start, len, buf, bufCount);
+    RegExpRunStatus status = code.execute(cx, chars, length, start,
+                                          matchPairs->buffer(), backingPairCount);
 
-    switch (result) {
-      case RegExpPrivateCode::Error:
-        return false;
-      case RegExpPrivateCode::Success_NotFound:
-        *rval = NullValue();
-        return true;
+    switch (status) {
+      case RegExpRunStatus_Error:
+        return status;
+      case RegExpRunStatus_Success_NotFound:
+        *output = matchPairs;
+        return status;
       default:
-        JS_ASSERT(result == RegExpPrivateCode::Success);
+        JS_ASSERT(status == RegExpRunStatus_Success);
     }
 
-    /* 
-     * Adjust buf for the inputOffset. Use of sticky is rare and the matchItemCount is small, so
-     * just do another pass.
-     */
-    if (JS_UNLIKELY(inputOffset)) {
-        for (size_t i = 0; i < matchItemCount; ++i)
-            buf[i] = buf[i] < 0 ? -1 : buf[i] + inputOffset;
-    }
+    matchPairs->displace(displacement);
+    matchPairs->checkAgainst(origLength);
 
-    /* Make sure the populated contents of |buf| are sane values against |input|. */
-    checkMatchPairs(input, buf, matchItemCount);
+    *lastIndex = matchPairs->pair(0).limit;
+    *output = matchPairs;
 
-    if (res)
-        res->updateFromMatch(cx, input, buf, matchItemCount);
-
-    *lastIndex = buf[1];
+    return RegExpRunStatus_Success;
+}
 
-    if (test) {
-        *rval = BooleanValue(true);
-        return true;
-    }
-
-    JSObject *array = createResult(cx, input, buf, matchItemCount);
-    if (!array)
+bool
+RegExpObject::makePrivate(JSContext *cx)
+{
+    JS_ASSERT(!getPrivate());
+    AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, getSource(), getFlags(), NULL);
+    if (!rep)
         return false;
 
-    *rval = ObjectValue(*array);
+    setPrivate(rep.get());
     return true;
 }
 
+RegExpRunStatus
+RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
+                      LifoAllocScope &allocScope, MatchPairs **output)
+{
+    if (!getPrivate() && !makePrivate(cx))
+        return RegExpRunStatus_Error;
+
+    return getPrivate()->execute(cx, chars, length, lastIndex, allocScope, output);
+}
+
 const Shape *
 RegExpObject::assignInitialShape(JSContext *cx)
 {
     JS_ASSERT(!cx->compartment->initialRegExpShape);
     JS_ASSERT(isRegExp());
     JS_ASSERT(nativeEmpty());
 
     JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
@@ -221,18 +231,17 @@ js_XDRRegExpObject(JSXDRState *xdr, JSOb
 
 #define js_XDRRegExpObject NULL
 
 #endif /* !JS_HAS_XDR */
 
 static void
 regexp_finalize(JSContext *cx, JSObject *obj)
 {
-    if (RegExpPrivate *rep = static_cast<RegExpObject *>(obj)->getPrivate())
-        rep->decref(cx);
+    obj->asRegExp()->finalize(cx);
 }
 
 Class js::RegExpClass = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     JS_PropertyStub,         /* addProperty */
@@ -289,19 +298,19 @@ RegExpPrivateCode::reportYarrError(JSCon
 void
 RegExpPrivateCode::reportPCREError(JSContext *cx, int error)
 {
 #define REPORT(msg_) \
     JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, msg_); \
     return
     switch (error) {
       case -2: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
-      case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred."); 
+      case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred.");
       case 1: REPORT(JSMSG_TRAILING_SLASH);
-      case 2: REPORT(JSMSG_TRAILING_SLASH); 
+      case 2: REPORT(JSMSG_TRAILING_SLASH);
       case 3: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
       case 4: REPORT(JSMSG_BAD_QUANTIFIER);
       case 5: REPORT(JSMSG_BAD_QUANTIFIER);
       case 6: REPORT(JSMSG_BAD_CLASS_RANGE);
       case 7: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
       case 8: REPORT(JSMSG_BAD_CLASS_RANGE);
       case 9: REPORT(JSMSG_BAD_QUANTIFIER);
       case 10: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN);
@@ -312,16 +321,17 @@ RegExpPrivateCode::reportPCREError(JSCon
       case 15: REPORT(JSMSG_BAD_BACKREF);
       case 16: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
       case 17: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
       default:
         JS_NOT_REACHED("Precondition violation: unknown PCRE error code.");
     }
 #undef REPORT
 }
+
 #endif /* ENABLE_YARR_JIT */
 
 bool
 js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut)
 {
     size_t n = flagStr->length();
     const jschar *s = flagStr->getChars(cx);
     if (!s)
@@ -352,74 +362,68 @@ js::ParseRegExpFlags(JSContext *cx, JSSt
           }
         }
 #undef HANDLE_FLAG
     }
     return true;
 }
 
 AlreadyIncRefed<RegExpPrivate>
-RegExpPrivate::createFlagged(JSContext *cx, JSString *str, JSString *opt, TokenStream *ts)
+RegExpPrivate::create(JSContext *cx, JSLinearString *str, JSString *opt, TokenStream *ts)
 {
     if (!opt)
         return create(cx, str, RegExpFlag(0), ts);
 
     RegExpFlag flags = RegExpFlag(0);
     if (!ParseRegExpFlags(cx, opt, &flags))
         return AlreadyIncRefed<RegExpPrivate>(NULL);
 
     return create(cx, str, flags, ts);
 }
 
 RegExpObject *
-RegExpObject::clone(JSContext *cx, RegExpObject *obj, RegExpObject *proto)
+RegExpObject::clone(JSContext *cx, RegExpObject *reobj, RegExpObject *proto)
 {
     JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent());
     if (!clone)
         return NULL;
 
     /*
      * This clone functionality does not duplicate the JIT'd code blob,
      * which is necessary for cross-compartment cloning functionality.
      */
-    assertSameCompartment(cx, obj, clone);
+    assertSameCompartment(cx, reobj, clone);
 
     RegExpStatics *res = cx->regExpStatics();
+    RegExpObject *reclone = clone->asRegExp();
 
-    /* 
+    /*
      * Check that the RegExpPrivate for the original is okay to use in
-     * the clone -- if the RegExpStatics provides more flags we'll need
-     * a different RegExpPrivate.
+     * the clone -- if the |RegExpStatics| provides more flags we'll
+     * need a different |RegExpPrivate|.
      */
-    AlreadyIncRefed<RegExpPrivate> rep(NULL);
-    {
-        RegExpFlag origFlags = obj->getFlags();
-        RegExpFlag staticsFlags = res->getFlags();
-        if ((origFlags & staticsFlags) != staticsFlags) {
-            RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
-            rep = RegExpPrivate::create(cx, obj->getSource(), newFlags, NULL);
-            if (!rep)
-                return NULL;
-        } else {
-            RegExpPrivate *toShare = obj->getPrivate();
-            toShare->incref(cx);
-            rep = AlreadyIncRefed<RegExpPrivate>(toShare);
-        }
+    RegExpFlag origFlags = reobj->getFlags();
+    RegExpFlag staticsFlags = res->getFlags();
+    if ((origFlags & staticsFlags) != staticsFlags) {
+        RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
+        return reclone->reset(cx, reobj->getSource(), newFlags) ? reclone : NULL;
     }
 
-    JS_ASSERT(rep);
-
-    PreInitRegExpObject pireo(clone);
-    RegExpObject *reclone = pireo.get();
-    if (!ResetRegExpObject(cx, reclone, rep)) {
-        pireo.fail();
-        return NULL;
+    RegExpPrivate *toShare = reobj->getPrivate();
+    if (toShare) {
+        toShare->incref(cx);
+        if (!reclone->reset(cx, AlreadyIncRefed<RegExpPrivate>(toShare))) {
+            toShare->decref(cx);
+            return NULL;
+        }
+    } else {
+        if (!reclone->reset(cx, reobj))
+            return NULL;
     }
 
-    pireo.succeed();
     return reclone;
 }
 
 JSObject * JS_FASTCALL
 js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
 {
     JS_ASSERT(obj->isRegExp());
     JS_ASSERT(proto->isRegExp());
@@ -430,35 +434,31 @@ js_CloneRegExpObject(JSContext *cx, JSOb
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_CloneRegExpObject, CONTEXT, OBJECT, OBJECT, 0,
                      ACCSET_STORE_ANY)
 #endif
 
 JSFlatString *
 RegExpObject::toString(JSContext *cx) const
 {
-    RegExpPrivate *rep = getPrivate();
-    if (!rep)
-        return cx->runtime->emptyString;
-
-    JSLinearString *src = rep->getSource();
+    JSLinearString *src = getSource();
     StringBuffer sb(cx);
     if (size_t len = src->length()) {
         if (!sb.reserve(len + 2))
             return NULL;
         sb.infallibleAppend('/');
         sb.infallibleAppend(src->chars(), len);
         sb.infallibleAppend('/');
     } else {
         if (!sb.append("/(?:)/"))
             return NULL;
     }
-    if (rep->global() && !sb.append('g'))
+    if (global() && !sb.append('g'))
         return NULL;
-    if (rep->ignoreCase() && !sb.append('i'))
+    if (ignoreCase() && !sb.append('i'))
         return NULL;
-    if (rep->multiline() && !sb.append('m'))
+    if (multiline() && !sb.append('m'))
         return NULL;
-    if (rep->sticky() && !sb.append('y'))
+    if (sticky() && !sb.append('y'))
         return NULL;
 
     return sb.finishString();
 }
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -44,23 +44,29 @@
 #include <stddef.h>
 #include "jsobj.h"
 
 #include "js/TemplateLib.h"
 
 #include "yarr/Yarr.h"
 #if ENABLE_YARR_JIT
 #include "yarr/YarrJIT.h"
+#include "yarr/YarrSyntaxChecker.h"
 #else
 #include "yarr/pcre/pcre.h"
 #endif
 
 namespace js {
 
-class RegExpPrivate;
+enum RegExpRunStatus
+{
+    RegExpRunStatus_Error,
+    RegExpRunStatus_Success,
+    RegExpRunStatus_Success_NotFound
+};
 
 class RegExpObject : public ::JSObject
 {
     static const uintN LAST_INDEX_SLOT          = 0;
     static const uintN SOURCE_SLOT              = 1;
     static const uintN GLOBAL_FLAG_SLOT         = 2;
     static const uintN IGNORE_CASE_FLAG_SLOT    = 3;
     static const uintN MULTILINE_FLAG_SLOT      = 4;
@@ -82,79 +88,107 @@ class RegExpObject : public ::JSObject
     createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
                     TokenStream *ts);
 
     static RegExpObject *clone(JSContext *cx, RegExpObject *obj, RegExpObject *proto);
 
     /* Note: fallible. */
     JSFlatString *toString(JSContext *cx) const;
 
+    /*
+     * Run the regular expression over the input text.
+     *
+     * Results are placed in |output| as integer pairs. For eaxmple,
+     * |output[0]| and |output[1]| represent the text indices that make
+     * up the "0" (whole match) pair. Capturing parens will result in
+     * more output.
+     *
+     * N.B. it's the responsibility of the caller to hook the |output|
+     * into the |RegExpStatics| appropriately, if necessary.
+     */
+    RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
+                            LifoAllocScope &allocScope, MatchPairs **output);
+
     /* Accessors. */
 
     const Value &getLastIndex() const {
         return getSlot(LAST_INDEX_SLOT);
     }
     void setLastIndex(const Value &v) {
         setSlot(LAST_INDEX_SLOT, v);
     }
     void setLastIndex(double d) {
         setSlot(LAST_INDEX_SLOT, NumberValue(d));
     }
     void zeroLastIndex() {
         setSlot(LAST_INDEX_SLOT, Int32Value(0));
     }
 
-    JSString *getSource() const {
-        return getSlot(SOURCE_SLOT).toString();
+    JSLinearString *getSource() const {
+        return &getSlot(SOURCE_SLOT).toString()->asLinear();
     }
-    void setSource(JSString *source) {
+    void setSource(JSLinearString *source) {
         setSlot(SOURCE_SLOT, StringValue(source));
     }
     RegExpFlag getFlags() const {
         uintN flags = 0;
-        flags |= getSlot(GLOBAL_FLAG_SLOT).toBoolean() ? GlobalFlag : 0;
-        flags |= getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean() ? IgnoreCaseFlag : 0;
-        flags |= getSlot(MULTILINE_FLAG_SLOT).toBoolean() ? MultilineFlag : 0;
-        flags |= getSlot(STICKY_FLAG_SLOT).toBoolean() ? StickyFlag : 0;
+        flags |= global() ? GlobalFlag : 0;
+        flags |= ignoreCase() ? IgnoreCaseFlag : 0;
+        flags |= multiline() ? MultilineFlag : 0;
+        flags |= sticky() ? StickyFlag : 0;
         return RegExpFlag(flags);
     }
-    inline RegExpPrivate *getPrivate() const;
 
     /* Flags. */
 
     void setIgnoreCase(bool enabled)    { setSlot(IGNORE_CASE_FLAG_SLOT, BooleanValue(enabled)); }
     void setGlobal(bool enabled)        { setSlot(GLOBAL_FLAG_SLOT, BooleanValue(enabled)); }
     void setMultiline(bool enabled)     { setSlot(MULTILINE_FLAG_SLOT, BooleanValue(enabled)); }
     void setSticky(bool enabled)        { setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled)); }
+    bool ignoreCase() const { return getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
+    bool global() const     { return getSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
+    bool multiline() const  { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
+    bool sticky() const     { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
+
+    /*
+     * N.B. |RegExpObject|s can be mutated in place because of |RegExp.prototype.compile|, hence
+     * |reset| for re-initialization.
+     */
+
+    inline bool reset(JSContext *cx, RegExpObject *other);
+    inline bool reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
+    inline bool reset(JSContext *cx, JSLinearString *source, RegExpFlag flags);
+
+    inline RegExpPrivate *getOrCreatePrivate(JSContext *cx);
+    inline void finalize(JSContext *cx);
 
   private:
-    /* N.B. |RegExpObject|s can be mutated in place because of |RegExp.prototype.compile|. */
-    inline bool reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
+    /* The |RegExpPrivate| is lazily created at the time of use. */
+    inline RegExpPrivate *getPrivate() const;
 
-    friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSString *, RegExpFlag);
+    /*
+     * Precondition: the syntax for |source| has already been validated.
+     * Side effect: sets the private field.
+     */
+    bool makePrivate(JSContext *cx);
+
+    friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSLinearString *, RegExpFlag);
     friend bool ResetRegExpObject(JSContext *, RegExpObject *, AlreadyIncRefed<RegExpPrivate>);
 
     /*
      * Compute the initial shape to associate with fresh RegExp objects,
      * encoding their initial properties. Return the shape after
      * changing this regular expression object's last property to it.
      */
     const Shape *assignInitialShape(JSContext *cx);
 
     RegExpObject();
     RegExpObject &operator=(const RegExpObject &reo);
 }; /* class RegExpObject */
 
-inline bool
-ResetRegExpObject(JSContext *cx, RegExpObject *reobj, JSString *str, RegExpFlag flags);
-
-/* N.B. On failure, caller must decref |rep|. */
-inline bool
-ResetRegExpObject(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
-
 /* Abstracts away the gross |RegExpPrivate| backend details. */
 class RegExpPrivateCode
 {
 #if ENABLE_YARR_JIT
     typedef JSC::Yarr::BytecodePattern BytecodePattern;
     typedef JSC::Yarr::ErrorCode ErrorCode;
     typedef JSC::Yarr::JSGlobalData JSGlobalData;
     typedef JSC::Yarr::YarrCodeBlock YarrCodeBlock;
@@ -184,30 +218,41 @@ class RegExpPrivateCode
         if (byteCode)
             Foreground::delete_<BytecodePattern>(byteCode);
 #else
         if (compiled)
             jsRegExpFree(compiled);
 #endif
     }
 
+    static bool checkSyntax(JSContext *cx, TokenStream *tokenStream, JSLinearString *source) {
+#if ENABLE_YARR_JIT
+        ErrorCode error = JSC::Yarr::checkSyntax(*source);
+        if (error == JSC::Yarr::NoError)
+            return true;
+
+        reportYarrError(cx, tokenStream, error);
+        return false;
+#else
+# error "Syntax checking not implemented for !ENABLE_YARR_JIT"
+#endif
+    }
+
 #if ENABLE_YARR_JIT
     static inline bool isJITRuntimeEnabled(JSContext *cx);
-    void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error);
+    static void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error);
 #else
-    void reportPCREError(JSContext *cx, int error);
+    static void reportPCREError(JSContext *cx, int error);
 #endif
 
     inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
                         RegExpFlag flags);
 
-    enum ExecuteResult { Error, Success, Success_NotFound };
-
-    inline ExecuteResult execute(JSContext *cx, const jschar *chars, size_t start, size_t length,
-                                 int *output, size_t outputCount);
+    inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
+                                   int *output, size_t outputCount);
 
     static size_t getOutputSize(size_t pairCount) {
 #if ENABLE_YARR_JIT
         return pairCount * 2;
 #else
         return pairCount * 3; /* Should be x2, but PCRE has... needs. */
 #endif
     }
@@ -242,66 +287,49 @@ class RegExpPrivate
     RegExpPrivate(JSLinearString *source, RegExpFlag flags, JSCompartment *compartment)
       : source(source), refCount(1), parenCount(0), flags(flags), compartment(compartment)
     { }
 
     JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
 
     bool compile(JSContext *cx, TokenStream *ts);
     static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
-    static JSObject *createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount);
-    bool executeInternal(JSContext *cx, RegExpStatics *res, JSString *input,
-                         size_t *lastIndex, bool test, Value *rval);
 
   public:
-    /*
-     * Execute regexp on |input| at |*lastIndex|.
-     *
-     * On match:    Update |*lastIndex| and RegExp class statics.
-     *              Return true if test is true. Place an array in |*rval| if test is false.
-     * On mismatch: Make |*rval| null.
-     */
-    bool execute(JSContext *cx, RegExpStatics *res, JSString *input, size_t *lastIndex, bool test,
-                 Value *rval) {
-        JS_ASSERT(res);
-        return executeInternal(cx, res, input, lastIndex, test, rval);
-    }
+    static AlreadyIncRefed<RegExpPrivate>
+    create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts);
 
-    bool executeNoStatics(JSContext *cx, JSString *input, size_t *lastIndex, bool test,
-                          Value *rval) {
-        return executeInternal(cx, NULL, input, lastIndex, test, rval);
-    }
-
-    /* Factories */
+    static AlreadyIncRefed<RegExpPrivate>
+    create(JSContext *cx, JSLinearString *source, JSString *flags, TokenStream *ts);
 
-    static AlreadyIncRefed<RegExpPrivate> create(JSContext *cx, JSString *source, RegExpFlag flags,
-                                                 TokenStream *ts);
-
-    /* Would overload |create|, but |0| resolves ambiguously against pointer and uint. */
-    static AlreadyIncRefed<RegExpPrivate> createFlagged(JSContext *cx, JSString *source,
-                                                        JSString *flags, TokenStream *ts);
+    RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
+                            LifoAllocScope &allocScope, MatchPairs **output);
 
     /* Mutators */
 
     void incref(JSContext *cx);
     void decref(JSContext *cx);
 
     /* For JIT access. */
     size_t *addressOfRefCount() { return &refCount; }
 
     /* Accessors */
 
-    JSLinearString *getSource() const { return source; }
-    size_t getParenCount() const { return parenCount; }
-    RegExpFlag getFlags() const { return flags; }
+    JSLinearString *getSource() const   { return source; }
+    size_t getParenCount() const        { return parenCount; }
+
+    /* Accounts for the "0" (whole match) pair. */
+    size_t pairCount() const            { return parenCount + 1; }
+
+    RegExpFlag getFlags() const         { return flags; }
     bool ignoreCase() const { return flags & IgnoreCaseFlag; }
     bool global() const     { return flags & GlobalFlag; }
     bool multiline() const  { return flags & MultilineFlag; }
     bool sticky() const     { return flags & StickyFlag; }
-}; /* class RegExpPrivate */
+};
 
 /*
  * Parse regexp flags. Report an error and return false if an invalid
  * sequence of flags is encountered (repeat/invalid flag).
  *
  * N.B. flagStr must be rooted.
  */
 bool
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -40,22 +40,24 @@
 
 #ifndef RegExpStatics_h__
 #define RegExpStatics_h__
 
 #include "jscntxt.h"
 
 #include "js/Vector.h"
 
+#include "vm/MatchPairs.h"
+
 namespace js {
 
 class RegExpStatics
 {
-    typedef Vector<int, 20, SystemAllocPolicy> MatchPairs;
-    MatchPairs      matchPairs;
+    typedef Vector<int, 20, SystemAllocPolicy> Pairs;
+    Pairs           matchPairs;
     /* The input that was used to produce matchPairs. */
     JSLinearString  *matchPairsInput;
     /* The input last set on the statics. */
     JSString        *pendingInput;
     RegExpFlag      flags;
     RegExpStatics   *bufferLink;
     bool            copied;
 
@@ -157,27 +159,30 @@ class RegExpStatics
 
   public:
     RegExpStatics() : bufferLink(NULL), copied(false) { clear(); }
 
     static JSObject *create(JSContext *cx, GlobalObject *parent);
 
     /* Mutators. */
 
-    bool updateFromMatch(JSContext *cx, JSLinearString *input, int *buf, size_t matchItemCount) {
+    bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs *newPairs) {
+        JS_ASSERT(input);
         aboutToWrite();
         pendingInput = input;
 
-        if (!matchPairs.resizeUninitialized(matchItemCount)) {
+        if (!matchPairs.resizeUninitialized(2 * newPairs->pairCount())) {
             js_ReportOutOfMemory(cx);
             return false;
         }
 
-        for (size_t i = 0; i < matchItemCount; ++i)
-            matchPairs[i] = buf[i];
+        for (size_t i = 0; i < newPairs->pairCount(); ++i) {
+            matchPairs[2 * i] = newPairs->pair(i).start;
+            matchPairs[2 * i + 1] = newPairs->pair(i).limit;
+        }
 
         matchPairsInput = input;
         return true;
     }
 
     inline void setMultiline(JSContext *cx, bool enabled);
 
     void clear() {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4278,16 +4278,49 @@ nsLayoutUtils::GetFontFacesForText(nsIFr
                                        curr);
   } while (aFollowContinuations &&
            (curr = static_cast<nsTextFrame*>(curr->GetNextContinuation())));
 
   return NS_OK;
 }
 
 /* static */
+nsresult
+nsLayoutUtils::GetTextRunMemoryForFrames(nsIFrame* aFrame, PRUint64* aTotal)
+{
+  NS_PRECONDITION(aFrame, "NULL frame pointer");
+
+  if (aFrame->GetType() == nsGkAtoms::textFrame) {
+    nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame);
+    gfxTextRun *run = textFrame->GetTextRun();
+    if (run) {
+      if (aTotal) {
+        run->AccountForSize(aTotal);
+      } else {
+        run->ClearSizeAccounted();
+      }
+    }
+    return NS_OK;
+  }
+
+  nsAutoTArray<nsIFrame::ChildList,4> childListArray;
+  aFrame->GetChildLists(&childListArray);
+
+  for (nsIFrame::ChildListArrayIterator childLists(childListArray);
+       !childLists.IsDone(); childLists.Next()) {
+    for (nsFrameList::Enumerator e(childLists.CurrentList());
+         !e.AtEnd(); e.Next()) {
+      GetTextRunMemoryForFrames(e.get(), aTotal);
+    }
+  }
+
+  return NS_OK;
+}
+
+/* static */
 void
 nsLayoutUtils::Shutdown()
 {
   if (sContentMap) {
     delete sContentMap;
     sContentMap = NULL;
   }
 }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1429,16 +1429,30 @@ public:
    */
   static nsresult GetFontFacesForText(nsIFrame* aFrame,
                                       PRInt32 aStartOffset,
                                       PRInt32 aEndOffset,
                                       bool aFollowContinuations,
                                       nsFontFaceList* aFontFaceList);
 
   /**
+   * Walks the frame tree starting at aFrame looking for textRuns.
+   * If aTotal is NULL, just clears the TEXT_RUN_MEMORY_ACCOUNTED flag
+   * on each textRun found.
+   * If aTotal is non-NULL, adds the storage used for each textRun to the
+   * total, and sets the TEXT_RUN_MEMORY_ACCOUNTED flag to avoid double-
+   * accounting. (Runs with this flag already set will be skipped.)
+   * Expected usage pattern is therefore to call twice:
+   *    rv = GetTextRunMemoryForFrames(rootFrame, NULL);
+   *    rv = GetTextRunMemoryForFrames(rootFrame, &total);
+   */
+  static nsresult GetTextRunMemoryForFrames(nsIFrame* aFrame,
+                                            PRUint64* aTotal);
+
+  /**
    * Checks if CSS 3D transforms are currently enabled.
    */
   static bool Are3DTransformsEnabled();
 
   static void Shutdown();
 
 #ifdef DEBUG
   /**
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -215,16 +215,18 @@
 #define ANCHOR_SCROLL_FLAGS (SCROLL_OVERFLOW_HIDDEN | SCROLL_NO_PARENT_FRAMES)
 
 #include "nsContentCID.h"
 static NS_DEFINE_IID(kRangeCID,     NS_RANGE_CID);
 
 /* for NS_MEMORY_REPORTER_IMPLEMENT */
 #include "nsIMemoryReporter.h"
 
+#include "gfxTextRunWordCache.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
 CapturingContentInfo nsIPresShell::gCaptureInfo =
   { PR_FALSE /* mAllowed */,     PR_FALSE /* mRetargetToElement */,
     PR_FALSE /* mPreventDrag */, nsnull /* mContent */ };
 nsIContent* nsIPresShell::gKeyDownTarget;
@@ -636,50 +638,79 @@ PresShell::MemoryReporter::SizeEnumerato
       str += spec;
     }
   }
 
   str += NS_LITERAL_CSTRING(")");
 
   NS_NAMED_LITERAL_CSTRING(kArenaDesc, "Memory used by layout PresShell, PresContext, and other related areas.");
   NS_NAMED_LITERAL_CSTRING(kStyleDesc, "Memory used by the style system.");
+  NS_NAMED_LITERAL_CSTRING(kTextRunsDesc, "Memory used for text-runs (glyph layout) in the PresShell's frame tree.");
 
   nsCAutoString arenaPath = str + NS_LITERAL_CSTRING("/arenas");
   nsCAutoString stylePath = str + NS_LITERAL_CSTRING("/styledata");
+  nsCAutoString textRunsPath = str + NS_LITERAL_CSTRING("/textruns");
 
   PRUint32 arenasSize;
   arenasSize = aShell->EstimateMemoryUsed();
   arenasSize += aShell->mPresContext->EstimateMemoryUsed();
 
   PRUint32 styleSize;
   styleSize = aShell->StyleSet()->SizeOf();
 
+  PRUint64 textRunsSize;
+  textRunsSize = aShell->ComputeTextRunMemoryUsed();
+
   data->callback->
     Callback(EmptyCString(), arenaPath, nsIMemoryReporter::KIND_HEAP,
              nsIMemoryReporter::UNITS_BYTES, arenasSize, kArenaDesc,
              data->closure);
 
   data->callback->
     Callback(EmptyCString(), stylePath, nsIMemoryReporter::KIND_HEAP,
              nsIMemoryReporter::UNITS_BYTES, styleSize, kStyleDesc,
              data->closure);
 
+  if (textRunsSize) {
+    data->callback->
+      Callback(EmptyCString(), textRunsPath, nsIMemoryReporter::KIND_HEAP,
+               nsIMemoryReporter::UNITS_BYTES, textRunsSize, kTextRunsDesc,
+               data->closure);
+  }
+
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
                                           nsISupports* aClosure)
 {
   MemoryReporterData data;
   data.callback = aCb;
   data.closure = aClosure;
 
+  // clear TEXT_RUN_SIZE_ACCOUNTED flag on cached runs
+  gfxTextRunWordCache::ComputeStorage(nsnull);
+
   sLiveShells->EnumerateEntries(SizeEnumerator, &data);
 
+  NS_NAMED_LITERAL_CSTRING(kTextRunWordCachePath,
+                           "explicit/gfx/textrun-word-cache");
+  NS_NAMED_LITERAL_CSTRING(kTextRunWordCacheDesc,
+                           "Memory used by cached text-runs that are "
+                           "not owned by a PresShell's frame tree.");
+
+  // now total up cached runs that aren't otherwise accounted for
+  PRUint64 textRunWordCacheSize = 0;
+  gfxTextRunWordCache::ComputeStorage(&textRunWordCacheSize);
+
+  aCb->Callback(EmptyCString(), kTextRunWordCachePath,
+                nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
+                textRunWordCacheSize, kTextRunWordCacheDesc, aClosure);
+
   return NS_OK;
 }
 
 class nsAutoCauseReflowNotifier
 {
 public:
   nsAutoCauseReflowNotifier(PresShell* aShell)
     : mShell(aShell)
@@ -8705,8 +8736,27 @@ PresShell::GetRootPresShell()
   if (mPresContext) {
     nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
     if (rootPresContext) {
       return static_cast<PresShell*>(rootPresContext->PresShell());
     }
   }
   return nsnull;
 }
+
+PRUint64
+PresShell::ComputeTextRunMemoryUsed()
+{
+  nsIFrame* rootFrame = FrameManager()->GetRootFrame();
+  if (!rootFrame) {
+    return 0;
+  }
+
+  // clear the TEXT_RUN_MEMORY_ACCOUNTED flags
+  nsLayoutUtils::GetTextRunMemoryForFrames(rootFrame, nsnull);
+
+  // collect the total memory in use for textruns
+  PRUint64 total = 0;
+  nsLayoutUtils::GetTextRunMemoryForFrames(rootFrame, &total);
+
+  return total;
+}
+
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -893,16 +893,18 @@ public:
 
     result += sizeof(PresShell);
     result += mStackArena.Size();
     result += mFrameArena.Size();
 
     return result;
   }
 
+  PRUint64 ComputeTextRunMemoryUsed();
+
   class MemoryReporter : public nsIMemoryMultiReporter
   {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIMEMORYMULTIREPORTER
   protected:
     static PLDHashOperator SizeEnumerator(PresShellPtrKey *aEntry, void *userArg);
   };
--- a/layout/generic/nsTextRunTransformations.cpp
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -94,16 +94,38 @@ nsTransformedTextRun::SetPotentialLineBr
   bool changed = gfxTextRun::SetPotentialLineBreaks(aStart, aLength,
       aBreakBefore, aRefContext);
   if (changed) {
     mNeedsRebuild = PR_TRUE;
   }
   return changed;
 }
 
+PRUint64
+nsTransformedTextRun::ComputeSize()
+{
+  PRUint32 total = gfxTextRun::ComputeSize();
+  if (moz_malloc_usable_size(this) == 0) {
+    total += sizeof(nsTransformedTextRun) - sizeof(gfxTextRun);
+  }
+  total += mStyles.SizeOf();
+  total += mCapitalize.SizeOf();
+  if (mOwnsFactory) {
+    PRUint32 factorySize = moz_malloc_usable_size(mFactory);
+    if (factorySize == 0) {
+      // this may not quite account for everything
+      // (e.g. nsCaseTransformTextRunFactory adds a couple of members)
+      // but I'm not sure it's worth the effort to track more precisely
+      factorySize = sizeof(nsTransformingTextRunFactory);
+    }
+    total += factorySize;
+  }
+  return total;
+}
+
 nsTransformedTextRun*
 nsTransformingTextRunFactory::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
                                           const gfxTextRunFactory::Parameters* aParams,
                                           gfxFontGroup* aFontGroup, PRUint32 aFlags,
                                           nsStyleContext** aStyles, bool aOwnsFactory)
 {
   return nsTransformedTextRun::Create(aParams, this, aFontGroup,
                                       aString, aLength, aFlags, aStyles, aOwnsFactory);
--- a/layout/generic/nsTextRunTransformations.h
+++ b/layout/generic/nsTextRunTransformations.h
@@ -127,16 +127,19 @@ public:
   void FinishSettingProperties(gfxContext* aRefContext)
   {
     if (mNeedsRebuild) {
       mNeedsRebuild = PR_FALSE;
       mFactory->RebuildTextRun(this, aRefContext);
     }
   }
 
+  // override the gfxTextRun impl to account for additional members here
+  virtual PRUint64 ComputeSize();
+
   nsTransformingTextRunFactory       *mFactory;
   nsTArray<nsRefPtr<nsStyleContext> > mStyles;
   nsTArray<bool>              mCapitalize;
   bool                                mOwnsFactory;
   bool                                mNeedsRebuild;
 
 private:
   nsTransformedTextRun(const gfxTextRunFactory::Parameters* aParams,
--- a/netwerk/mime/nsMIMEHeaderParamImpl.cpp
+++ b/netwerk/mime/nsMIMEHeaderParamImpl.cpp
@@ -365,44 +365,37 @@ nsMIMEHeaderParamImpl::DoParameterIntern
             goto increment_str;
           }
         }
         // look for single quotation mark(')
         const char *sQuote1 = PL_strchr(valueStart, 0x27);
         const char *sQuote2 = (char *) (sQuote1 ? PL_strchr(sQuote1 + 1, 0x27) : nsnull);
 
         // Two single quotation marks must be present even in
-        // absence of charset and lang. 
-        if (!sQuote1 || !sQuote2)
-          NS_WARNING("Mandatory two single quotes are missing in header parameter\n");
+        // absence of charset and lang.
+        if (!sQuote1 || !sQuote2) {
+          // log the warning and skip to next parameter
+          NS_WARNING("Mandatory two single quotes are missing in header parameter, parameter ignored\n");
+          goto increment_str;
+        }
+
         if (aCharset && sQuote1 > valueStart && sQuote1 < valueEnd)
         {
           *aCharset = (char *) nsMemory::Clone(valueStart, sQuote1 - valueStart + 1);
           if (*aCharset) 
             *(*aCharset + (sQuote1 - valueStart)) = 0;
         }
-        if (aLang && sQuote1 && sQuote2 && sQuote2 > sQuote1 + 1 &&
-            sQuote2 < valueEnd)
+        if (aLang && sQuote2 > sQuote1 + 1 && sQuote2 < valueEnd)
         {
           *aLang = (char *) nsMemory::Clone(sQuote1 + 1, sQuote2 - (sQuote1 + 1) + 1);
           if (*aLang) 
             *(*aLang + (sQuote2 - (sQuote1 + 1))) = 0;
         }
-
-        // Be generous and handle gracefully when required 
-        // single quotes are absent.
-        if (sQuote1)
-        {
-          if(!sQuote2)
-            sQuote2 = sQuote1;
-        }
-        else
-          sQuote2 = valueStart - 1;
-
-        if (sQuote2 && sQuote2 + 1 < valueEnd)
+  
+        if (sQuote2 + 1 < valueEnd)
         {
           if (*aResult)
           {
             // caseA value already read, or caseC/D value already read
             // but we're now reading caseB: either way, drop old value
             nsMemory::Free(*aResult);
             haveCaseAValue = false;
           }
--- a/netwerk/test/unit/test_MIME_params.js
+++ b/netwerk/test/unit/test_MIME_params.js
@@ -246,16 +246,31 @@ var tests = [
    
   // the actual bug, without 2231/5987 encoding
   ["attachment; filename X", 
    "attachment", Cr.NS_ERROR_INVALID_ARG],
 
   // sanity check with WS on both sides
   ["attachment; filename = foo-A.html", 
    "attachment", "foo-A.html"],   
+
+  // Bug 692574: RFC2231/5987 decoding should not tolerate missing single
+  // quotes
+
+  // one missing
+  ["attachment; filename*=UTF-8'foo-%41.html", 
+   "attachment", Cr.NS_ERROR_INVALID_ARG],
+
+  // both missing
+  ["attachment; filename*=foo-%41.html", 
+   "attachment", Cr.NS_ERROR_INVALID_ARG],
+
+  // make sure fallback works
+  ["attachment; filename*=UTF-8'foo-%41.html; filename=bar.html", 
+   "attachment", "bar.html"],
 ];
 
 function do_tests(whichRFC)
 {
   var mhp = Components.classes["@mozilla.org/network/mime-hdrparam;1"]
                       .getService(Components.interfaces.nsIMIMEHeaderParam);
 
   var unused = { value : null };
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -347,26 +347,27 @@ nsHtml5TreeOpExecutor::UpdateStyleSheet(
 
   // Re-open update
   BeginDocUpdate();
 }
 
 void
 nsHtml5TreeOpExecutor::FlushSpeculativeLoads()
 {
-  if (NS_UNLIKELY(!mParser)) {
-    return;
-  }
   nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
   mStage.MoveSpeculativeLoadsTo(speculativeLoadQueue);
   const nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
   const nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
   for (nsHtml5SpeculativeLoad* iter = const_cast<nsHtml5SpeculativeLoad*>(start);
        iter < end;
        ++iter) {
+    if (NS_UNLIKELY(!mParser)) {
+      // An extension terminated the parser from a HTTP observer.
+      return;
+    }
     iter->Perform(this);
   }
 }
 
 class nsHtml5FlushLoopGuard
 {
   private:
     nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
@@ -448,21 +449,31 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
       // Make sure speculative loads never start after the corresponding
       // normal loads for the same URLs.
       const nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
       const nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
       for (nsHtml5SpeculativeLoad* iter = (nsHtml5SpeculativeLoad*)start;
            iter < end;
            ++iter) {
         iter->Perform(this);
+        if (NS_UNLIKELY(!mParser)) {
+          // An extension terminated the parser from a HTTP observer.
+          mOpQueue.Clear(); // clear in order to be able to assert in destructor
+          return;
+        }
       }
     } else {
       FlushSpeculativeLoads(); // Make sure speculative loads never start after
                                // the corresponding normal loads for the same
                                // URLs.
+      if (NS_UNLIKELY(!mParser)) {
+        // An extension terminated the parser from a HTTP observer.
+        mOpQueue.Clear(); // clear in order to be able to assert in destructor
+        return;
+      }
       // Not sure if this grip is still needed, but previously, the code
       // gripped before calling ParseUntilBlocked();
       nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip = 
         GetParser()->GetStreamParser();
       // Now parse content left in the document.write() buffer queue if any.
       // This may generate tree ops on its own or dequeue a speculation.
       GetParser()->ParseUntilBlocked();
     }
@@ -542,25 +553,25 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
       }
     }
   }
 }
 
 void
 nsHtml5TreeOpExecutor::FlushDocumentWrite()
 {
-  if (!mParser) {
+  FlushSpeculativeLoads(); // Make sure speculative loads never start after the
+                // corresponding normal loads for the same URLs.
+
+  if (NS_UNLIKELY(!mParser)) {
     // The parse has ended.
     mOpQueue.Clear(); // clear in order to be able to assert in destructor
     return;
   }
   
-  FlushSpeculativeLoads(); // Make sure speculative loads never start after the 
-                // corresponding normal loads for the same URLs.
-
   if (mFlushState != eNotFlushing) {
     // XXX Can this happen? In case it can, let's avoid crashing.
     return;
   }
 
   mFlushState = eInFlush;
 
   // avoid crashing near EOF
--- a/toolkit/components/feeds/nsScriptableUnescapeHTML.cpp
+++ b/toolkit/components/feeds/nsScriptableUnescapeHTML.cpp
@@ -175,22 +175,22 @@ nsScriptableUnescapeHTML::ParseFragment(
                                             tagStack,
                                             PR_TRUE,
                                             aReturn);
       fragment = do_QueryInterface(*aReturn);
     } else {
       NS_NewDocumentFragment(aReturn,
                              document->NodeInfoManager());
       fragment = do_QueryInterface(*aReturn);
-      nsContentUtils::ParseFragmentHTML(aFragment,
-                                        fragment,
-                                        nsGkAtoms::body,
-                                        kNameSpaceID_XHTML,
-                                        PR_FALSE,
-                                        PR_TRUE);
+      rv = nsContentUtils::ParseFragmentHTML(aFragment,
+                                             fragment,
+                                             nsGkAtoms::body,
+                                             kNameSpaceID_XHTML,
+                                             PR_FALSE,
+                                             PR_TRUE);
       // Now, set the base URI on all subtree roots.
       if (aBaseURI) {
         aBaseURI->GetSpec(spec);
         nsAutoString spec16;
         CopyUTF8toUTF16(spec, spec16);
         nsIContent* node = fragment->GetFirstChild();
         while (node) {
           if (node->IsElement()) {
--- a/xpcom/glue/nsTArray-inl.h
+++ b/xpcom/glue/nsTArray-inl.h
@@ -50,43 +50,43 @@ template<class Alloc>
 nsTArray_base<Alloc>::~nsTArray_base() {
   if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) {
     Alloc::Free(mHdr);
   }
   MOZ_COUNT_DTOR(nsTArray_base);
 }
 
 template<class Alloc>
-nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elemAlign) {
+const nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elemAlign) const {
   // Assuming |this| points to an nsAutoArray, we want to get a pointer to
   // mAutoBuf.  So just cast |this| to nsAutoArray* and read &mAutoBuf!
 
-  void* autoBuf = &reinterpret_cast<nsAutoArrayBase<nsTArray<PRUint32>, 1>*>(this)->mAutoBuf;
+  const void* autoBuf = &reinterpret_cast<const nsAutoArrayBase<nsTArray<PRUint32>, 1>*>(this)->mAutoBuf;
 
   // If we're on a 32-bit system and elemAlign is 8, we need to adjust our
   // pointer to take into account the extra alignment in the auto array.
 
   // Check that the auto array is padded as we expect.
   PR_STATIC_ASSERT(sizeof(void*) != 4 ||
                    (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 &&
                     sizeof(nsAutoTArray<mozilla::AlignedElem<8>, 1>) ==
                       sizeof(void*) + sizeof(nsTArrayHeader) +
                       4 + sizeof(mozilla::AlignedElem<8>)));
 
   // We don't support alignments greater than 8 bytes.
   NS_ABORT_IF_FALSE(elemAlign <= 4 || elemAlign == 8, "unsupported alignment.");
   if (sizeof(void*) == 4 && elemAlign == 8) {
-    autoBuf = reinterpret_cast<char*>(autoBuf) + 4;
+    autoBuf = reinterpret_cast<const char*>(autoBuf) + 4;
   }
 
-  return reinterpret_cast<Header*>(autoBuf);
+  return reinterpret_cast<const Header*>(autoBuf);
 }
 
 template<class Alloc>
-bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() {
+bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() const {
   if (!mHdr->mIsAutoArray) {
     return PR_FALSE;
   }
 
   // This is nuts.  If we were sane, we'd pass elemAlign as a parameter to
   // this function.  Unfortunately this function is called in nsTArray_base's
   // destructor, at which point we don't know elem_type's alignment.
   //
@@ -113,18 +113,18 @@ bool nsTArray_base<Alloc>::UsesAutoArray
   // Note that this means that we can't store elements with alignment 16 in an
   // nsTArray, because GetAutoArrayBuffer(16) could lie outside the memory
   // owned by this nsAutoTArray.  We statically assert that elem_type's
   // alignment is 8 bytes or less in nsAutoArrayBase.
 
   PR_STATIC_ASSERT(sizeof(nsTArrayHeader) > 4);
 
 #ifdef DEBUG
-  PRPtrdiff diff = reinterpret_cast<char*>(GetAutoArrayBuffer(8)) -
-                   reinterpret_cast<char*>(GetAutoArrayBuffer(4));
+  PRPtrdiff diff = reinterpret_cast<const char*>(GetAutoArrayBuffer(8)) -
+                   reinterpret_cast<const char*>(GetAutoArrayBuffer(4));
   NS_ABORT_IF_FALSE(diff >= 0 && diff <= 4, "GetAutoArrayBuffer doesn't do what we expect.");
 #endif
 
   return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8);
 }
 
 
 template<class Alloc>
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -251,33 +251,41 @@ protected:
       bool mIsAuto;
   };
 
   // Helper function for SwapArrayElements. Ensures that if the array
   // is an nsAutoTArray that it doesn't use the built-in buffer.
   bool EnsureNotUsingAutoArrayBuffer(size_type elemSize);
 
   // Returns true if this nsTArray is an nsAutoTArray with a built-in buffer.
-  bool IsAutoArray() {
+  bool IsAutoArray() const {
     return mHdr->mIsAutoArray;
   }
 
   // Returns a Header for the built-in buffer of this nsAutoTArray.
   Header* GetAutoArrayBuffer(size_t elemAlign) {
     NS_ASSERTION(IsAutoArray(), "Should be an auto array to call this");
     return GetAutoArrayBufferUnsafe(elemAlign);
   }
+  const Header* GetAutoArrayBuffer(size_t elemAlign) const {
+    NS_ASSERTION(IsAutoArray(), "Should be an auto array to call this");
+    return GetAutoArrayBufferUnsafe(elemAlign);
+  }
 
   // Returns a Header for the built-in buffer of this nsAutoTArray, but doesn't
   // assert that we are an nsAutoTArray.
-  Header* GetAutoArrayBufferUnsafe(size_t elemAlign);
+  Header* GetAutoArrayBufferUnsafe(size_t elemAlign) {
+    return const_cast<Header*>(static_cast<const nsTArray_base<Alloc>*>(this)->
+                               GetAutoArrayBufferUnsafe(elemAlign));
+  }
+  const Header* GetAutoArrayBufferUnsafe(size_t elemAlign) const;
 
   // Returns true if this is an nsAutoTArray and it currently uses the
   // built-in buffer to store its elements.
-  bool UsesAutoArrayBuffer();
+  bool UsesAutoArrayBuffer() const;
 
   // The array's elements (prefixed with a Header).  This pointer is never
   // null.  If the array is empty, then this will point to sEmptyHdr.
   Header *mHdr;
 
   Header* Hdr() const { 
     return mHdr;
   }
@@ -470,17 +478,18 @@ public:
   nsTArray& operator=(const nsTArray<E, Allocator>& other) {
     ReplaceElementsAt(0, Length(), other.Elements(), other.Length());
     return *this;
   }
 
   // @return The amount of memory taken used by this nsTArray, not including
   // sizeof(this)
   size_t SizeOf() const {
-    return this->Capacity() * sizeof(elem_type) + sizeof(*this->Hdr());
+    return this->UsesAutoArrayBuffer() ?
+      0 : this->Capacity() * sizeof(elem_type) + sizeof(*this->Hdr());
   }
 
   //
   // Accessor methods
   //
 
   // This method provides direct access to the array elements.
   // @return A pointer to the first element of the array.  If the array is