Merge from tracemonkey.
authorDavid Anderson <danderson@mozilla.com>
Mon, 13 Sep 2010 22:31:15 -0700
changeset 74576 f33e6c62f1c3c0bcb75d3781019a403e895a667d
parent 74575 eab4befb94208e46f3aea7be48c9595b47f492c4 (current diff)
parent 53861 661301474ef801155a56c3253a83473f8eb46801 (diff)
child 74577 3d11e994ad09447b115ea1f57f354f1bfa521f5c
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone2.0b6pre
Merge from tracemonkey.
js/src/jsgc.cpp
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -119,19 +119,17 @@
 #include "nsHTMLFormElement.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 
 #include "nsTextEditRules.h"
 
 // JS headers are needed for the pattern attribute.
-#include "jsapi.h"    // for js_SaveAndClearRegExpStatics 
-                      // and js_RestoreRegExpStatics
-#include "jsregexp.h" // for js::AutoValueRooter
+#include "jsapi.h"
 #include "jscntxt.h"
 
 #include "nsHTMLInputElement.h"
 
 // XXX align=left, hspace, vspace, border? other nav4 attrs
 
 static NS_DEFINE_CID(kXULControllersCID,  NS_XULCONTROLLERS_CID);
 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
@@ -4003,35 +4001,28 @@ nsHTMLInputElement::IsPatternMatching(ns
   NS_ENSURE_TRUE(ctx, PR_TRUE);
 
   JSAutoRequest ar(ctx);
 
   // The pattern has to match the entire value.
   aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0);
   aPattern.Append(NS_LITERAL_STRING(")$"));
 
-  JSObject* re = JS_NewUCRegExpObject(ctx, reinterpret_cast<jschar*>
-                                             (aPattern.BeginWriting()),
-                                      aPattern.Length(), 0);
+  JSObject* re = JS_NewUCRegExpObjectNoStatics(ctx, reinterpret_cast<jschar*>
+                                                 (aPattern.BeginWriting()),
+                                                aPattern.Length(), 0);
   NS_ENSURE_TRUE(re, PR_TRUE);
 
-  js::AutoObjectRooter re_root(ctx, re);
-  js::AutoStringRooter tvr(ctx);
-  js::RegExpStatics statics(ctx);
   jsval rval = JSVAL_NULL;
   size_t idx = 0;
   JSBool res;
 
-  js_SaveAndClearRegExpStatics(ctx, &statics, &tvr);
-
-  res = JS_ExecuteRegExp(ctx, re, reinterpret_cast<jschar*>
+  res = JS_ExecuteRegExpNoStatics(ctx, re, reinterpret_cast<jschar*>
                                     (aValue.BeginWriting()),
-                         aValue.Length(), &idx, JS_TRUE, &rval);
-
-  js_RestoreRegExpStatics(ctx, &statics);
+                                  aValue.Length(), &idx, JS_TRUE, &rval);
 
   return res == JS_FALSE || rval != JSVAL_NULL;
 }
 
 //
 // Visitor classes
 //
 //
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -3335,17 +3335,20 @@ nsJSContext::ClearScope(void *aGlobalObj
     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
   if (stack && NS_FAILED(stack->Push(mContext))) {
     stack = nsnull;
   }
 
   if (aGlobalObj) {
     JSObject *obj = (JSObject *)aGlobalObj;
     JSAutoRequest ar(mContext);
-    ::JS_ClearScope(mContext, obj);
+    JS_ClearScope(mContext, obj);
+    if (!obj->getParent()) {
+      JS_ClearRegExpStatics(mContext, obj);
+    }
 
     // Always clear watchpoints, to deal with two cases:
     // 1.  The first document for this window is loading, and a miscreant has
     //     preset watchpoints on the window object in order to attack the new
     //     document's privileged information.
     // 2.  A document loaded and used watchpoints on its own window, leaving
     //     them set until the next document loads. We must clean up window
     //     watchpoints here.
@@ -3363,18 +3366,16 @@ nsJSContext::ClearScope(void *aGlobalObj
 
       // Clear up obj's prototype chain, but not Object.prototype.
       for (JSObject *o = ::JS_GetPrototype(mContext, obj), *next;
            o && (next = ::JS_GetPrototype(mContext, o)); o = next)
         ::JS_ClearScope(mContext, o);
     }
   }
 
-  ::JS_ClearRegExpStatics(mContext);
-
   if (stack) {
     stack->Pop(nsnull);
   }
 }
 
 void
 nsJSContext::WillInitializeContext()
 {
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -250,73 +250,53 @@ IDBCursor::CreateCommon(IDBRequest* aReq
 
   return cursor.forget();
 }
 
 IDBCursor::IDBCursor()
 : mDirection(nsIIDBCursor::NEXT),
   mCachedValue(JSVAL_VOID),
   mHaveCachedValue(false),
-  mValueRooted(false),
+  mJSRuntime(nsnull),
   mContinueCalled(false),
   mDataIndex(0),
   mType(OBJECTSTORE)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBCursor::~IDBCursor()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (mValueRooted) {
-    NS_DROP_JS_OBJECTS(this, IDBCursor);
+  if (mJSRuntime) {
+    js_RemoveRoot(mJSRuntime, &mCachedValue);
   }
 
   if (mListenerManager) {
     mListenerManager->Disconnect();
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBCursor,
                                                   nsDOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRequest,
-                                                       nsPIDOMEventTarget)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction,
-                                                       nsPIDOMEventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mObjectStore,
                                                        nsPIDOMEventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mIndex,
                                                        nsPIDOMEventTarget)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction,
+                                                       nsPIDOMEventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(IDBCursor)
-  if (tmp->mValueRooted) {
-    NS_DROP_JS_OBJECTS(tmp, IDBCursor);
-    tmp->mCachedValue = JSVAL_VOID;
-    tmp->mHaveCachedValue = false;
-    tmp->mValueRooted = false;
-  }
-NS_IMPL_CYCLE_COLLECTION_ROOT_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor)
-  if (JSVAL_IS_GCTHING(tmp->mCachedValue)) {
-    void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedValue);
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing)
-  }
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBCursor,
                                                 nsDOMEventTargetHelper)
   // Don't unlink mObjectStore, mIndex, or mTransaction!
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRequest)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBCursor)
   NS_INTERFACE_MAP_ENTRY(nsIIDBCursor)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBCursor)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
@@ -392,25 +372,29 @@ IDBCursor::GetValue(JSContext* aCx,
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 
   if (!mHaveCachedValue) {
     JSAutoRequest ar(aCx);
 
+    if (!mJSRuntime) {
+      JSRuntime* rt = JS_GetRuntime(aCx);
+      JSBool ok = js_AddRootRT(rt, &mCachedValue,
+                               "IDBCursor::mCachedValue");
+      NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+      mJSRuntime = rt;
+    }
+
     nsCOMPtr<nsIJSON> json(new nsJSON());
     rv = json->DecodeToJSVal(mData[mDataIndex].value, aCx, &mCachedValue);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    if (!mValueRooted) {
-      NS_HOLD_JS_OBJECTS(this, IDBCursor);
-      mValueRooted = true;
-    }
-
     mHaveCachedValue = true;
   }
 
   *aValue = mCachedValue;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -73,18 +73,18 @@ class IDBCursor : public nsDOMEventTarge
                   public nsIIDBCursor
 {
   friend class ContinueRunnable;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIIDBCURSOR
 
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(IDBCursor,
-                                                         nsDOMEventTargetHelper)
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBCursor,
+                                           nsDOMEventTargetHelper)
 
   static
   already_AddRefed<IDBCursor>
   Create(IDBRequest* aRequest,
          IDBTransaction* aTransaction,
          IDBObjectStore* aObjectStore,
          PRUint16 aDirection,
          nsTArray<KeyValuePair>& aData);
@@ -132,17 +132,17 @@ protected:
   nsRefPtr<IDBObjectStore> mObjectStore;
   nsRefPtr<IDBIndex> mIndex;
 
   PRUint16 mDirection;
 
   nsCOMPtr<nsIVariant> mCachedKey;
   jsval mCachedValue;
   bool mHaveCachedValue;
-  bool mValueRooted;
+  JSRuntime* mJSRuntime;
 
   bool mContinueCalled;
   PRUint32 mDataIndex;
 
   Type mType;
   nsTArray<KeyValuePair> mData;
   nsTArray<KeyKeyPair> mKeyData;
 
--- a/dom/indexedDB/IDBEvents.cpp
+++ b/dom/indexedDB/IDBEvents.cpp
@@ -200,27 +200,17 @@ IDBEvent::CreateGenericEventRunnable(con
 
   nsCOMPtr<nsIRunnable> runnable(new EventFiringRunnable(aTarget, event));
   return runnable.forget();
 }
 
 NS_IMPL_ADDREF_INHERITED(IDBEvent, nsDOMEvent)
 NS_IMPL_RELEASE_INHERITED(IDBEvent, nsDOMEvent)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(IDBEvent)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSource)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSource)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBEvent)
+NS_INTERFACE_MAP_BEGIN(IDBEvent)
   NS_INTERFACE_MAP_ENTRY(nsIIDBEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
 
 NS_IMETHODIMP
 IDBEvent::GetSource(nsISupports** aSource)
 {
   nsCOMPtr<nsISupports> source(mSource);
   source.forget(aSource);
@@ -336,29 +326,17 @@ IDBSuccessEvent::CreateRunnable(IDBReque
 
   nsCOMPtr<nsIRunnable> runnable(new EventFiringRunnable(aRequest, event));
   return runnable.forget();
 }
 
 NS_IMPL_ADDREF_INHERITED(IDBSuccessEvent, IDBEvent)
 NS_IMPL_RELEASE_INHERITED(IDBSuccessEvent, IDBEvent)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(IDBSuccessEvent)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBSuccessEvent, IDBEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mResult)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTransaction)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBSuccessEvent, IDBEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mResult)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTransaction)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBSuccessEvent)
+NS_INTERFACE_MAP_BEGIN(IDBSuccessEvent)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIDBTransactionEvent, mTransaction)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(IDBTransactionEvent,
                                                    mTransaction)
   NS_INTERFACE_MAP_ENTRY(nsIIDBSuccessEvent)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBSuccessEvent)
 NS_INTERFACE_MAP_END_INHERITING(IDBEvent)
 
 DOMCI_DATA(IDBSuccessEvent, IDBSuccessEvent)
@@ -389,23 +367,16 @@ IDBSuccessEvent::GetResult(JSContext* aC
 NS_IMETHODIMP
 IDBSuccessEvent::GetTransaction(nsIIDBTransaction** aTransaction)
 {
   nsCOMPtr<nsIIDBTransaction> transaction(mTransaction);
   transaction.forget(aTransaction);
   return NS_OK;
 }
 
-GetSuccessEvent::~GetSuccessEvent()
-{
-  if (mValueRooted) {
-    NS_DROP_JS_OBJECTS(this, GetSuccessEvent);
-  }
-}
-
 nsresult
 GetSuccessEvent::Init(IDBRequest* aRequest,
                       IDBTransaction* aTransaction)
 {
   mSource = aRequest->Source();
   mTransaction = aTransaction;
 
   nsresult rv = InitEvent(NS_LITERAL_STRING(SUCCESS_EVT_STR), PR_FALSE,
@@ -422,87 +393,58 @@ NS_IMETHODIMP
 GetSuccessEvent::GetResult(JSContext* aCx,
                            jsval* aResult)
 {
   if (mValue.IsVoid()) {
     *aResult = JSVAL_VOID;
     return NS_OK;
   }
 
-  if (!mValueRooted) {
-    RootCachedValue();
-
+  if (!mJSRuntime) {
     nsString jsonValue = mValue;
     mValue.Truncate();
 
     JSAutoRequest ar(aCx);
 
+    JSRuntime* rt = JS_GetRuntime(aCx);
+
+    JSBool ok = js_AddRootRT(rt, &mCachedValue,
+                             "GetSuccessEvent::mCachedValue");
+    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+    mJSRuntime = rt;
+
     nsCOMPtr<nsIJSON> json(new nsJSON());
     nsresult rv = json->DecodeToJSVal(jsonValue, aCx, &mCachedValue);
     if (NS_FAILED(rv)) {
       mCachedValue = JSVAL_VOID;
 
       NS_ERROR("Failed to decode!");
       return rv;
     }
   }
 
   *aResult = mCachedValue;
   return NS_OK;
 }
 
-void
-GetSuccessEvent::RootCachedValue()
-{
-  mValueRooted = PR_TRUE;
-  NS_HOLD_JS_OBJECTS(this, GetSuccessEvent);
-}
-
-NS_IMPL_ADDREF_INHERITED(GetSuccessEvent, IDBSuccessEvent)
-NS_IMPL_RELEASE_INHERITED(GetSuccessEvent, IDBSuccessEvent)
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(GetSuccessEvent)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(GetSuccessEvent,
-                                                  IDBSuccessEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(GetSuccessEvent)
-  if (tmp->mValueRooted) {
-    NS_DROP_JS_OBJECTS(tmp, GetSuccessEvent);
-    tmp->mCachedValue = JSVAL_VOID;
-    tmp->mValueRooted = PR_FALSE;
-  }
-NS_IMPL_CYCLE_COLLECTION_ROOT_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(GetSuccessEvent,
-                                                IDBSuccessEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mResult)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTransaction)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(GetSuccessEvent)
-  if (JSVAL_IS_GCTHING(tmp->mCachedValue)) {
-    void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedValue);
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing)
-  }
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GetSuccessEvent)
-NS_INTERFACE_MAP_END_INHERITING(IDBSuccessEvent)
-
 NS_IMETHODIMP
 GetAllSuccessEvent::GetResult(JSContext* aCx,
                               jsval* aResult)
 {
-  if (!mValueRooted) {
-    RootCachedValue();
+  if (!mJSRuntime) {
+    JSAutoRequest ar(aCx);
+
+    JSRuntime* rt = JS_GetRuntime(aCx);
 
-    JSAutoRequest ar(aCx);
+    JSBool ok = js_AddRootRT(rt, &mCachedValue,
+                             "GetSuccessEvent::mCachedValue");
+    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+    mJSRuntime = rt;
 
     // Swap into a stack array so that we don't hang on to the strings if
     // something fails.
     nsTArray<nsString> values;
     if (!mValues.SwapElements(values)) {
       NS_ERROR("Failed to swap elements!");
       return NS_ERROR_FAILURE;
     }
@@ -550,20 +492,26 @@ GetAllSuccessEvent::GetResult(JSContext*
   *aResult = mCachedValue;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GetAllKeySuccessEvent::GetResult(JSContext* aCx,
                                  jsval* aResult)
 {
-  if (!mValueRooted) {
-    RootCachedValue();
+  if (!mJSRuntime) {
+    JSAutoRequest ar(aCx);
+
+    JSRuntime* rt = JS_GetRuntime(aCx);
 
-    JSAutoRequest ar(aCx);
+    JSBool ok = js_AddRootRT(rt, &mCachedValue,
+                             "GetSuccessEvent::mCachedValue");
+    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+    mJSRuntime = rt;
 
     // Swap into a stack array so that we don't hang on to the strings if
     // something fails.
     nsTArray<Key> keys;
     if (!mKeys.SwapElements(keys)) {
       NS_ERROR("Failed to swap elements!");
       return NS_ERROR_FAILURE;
     }
--- a/dom/indexedDB/IDBEvents.h
+++ b/dom/indexedDB/IDBEvents.h
@@ -68,18 +68,16 @@ class IDBTransaction;
 class IDBEvent : public nsDOMEvent,
                  public nsIIDBEvent
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIIDBEVENT
   NS_FORWARD_TO_NSDOMEVENT
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBEvent, nsDOMEvent)
-
   static already_AddRefed<nsIDOMEvent>
   CreateGenericEvent(const nsAString& aType);
 
   static already_AddRefed<nsIRunnable>
   CreateGenericEventRunnable(const nsAString& aType,
                              nsIDOMEventTarget* aTarget);
 
 protected:
@@ -118,18 +116,16 @@ class IDBSuccessEvent : public IDBEvent,
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIIDBSUCCESSEVENT
   NS_DECL_NSIIDBTRANSACTIONEVENT
   NS_FORWARD_NSIDOMEVENT(IDBEvent::)
   NS_FORWARD_NSIIDBEVENT(IDBEvent::)
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBSuccessEvent, IDBEvent)
-
   static already_AddRefed<nsIDOMEvent>
   Create(IDBRequest* aRequest,
          nsIVariant* aResult,
          nsIIDBTransaction* aTransaction);
 
   static already_AddRefed<nsIRunnable>
   CreateRunnable(IDBRequest* aRequest,
                  nsIVariant* aResult,
@@ -143,40 +139,38 @@ protected:
 };
 
 class GetSuccessEvent : public IDBSuccessEvent
 {
 public:
   GetSuccessEvent(const nsAString& aValue)
   : mValue(aValue),
     mCachedValue(JSVAL_VOID),
-    mValueRooted(PR_FALSE)
+    mJSRuntime(nsnull)
   { }
 
-  ~GetSuccessEvent();
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(GetSuccessEvent,
-                                                         IDBSuccessEvent)
+  ~GetSuccessEvent()
+  {
+    if (mJSRuntime) {
+      js_RemoveRoot(mJSRuntime, &mCachedValue);
+    }
+  }
 
   NS_IMETHOD GetResult(JSContext* aCx,
                        jsval* aResult);
 
   nsresult Init(IDBRequest* aRequest,
                 IDBTransaction* aTransaction);
 
 private:
   nsString mValue;
 
 protected:
-  void RootCachedValue();
-
   jsval mCachedValue;
   JSRuntime* mJSRuntime;
-  PRBool mValueRooted;
 };
 
 class GetAllSuccessEvent : public GetSuccessEvent
 {
 public:
   GetAllSuccessEvent(nsTArray<nsString>& aValues)
   : GetSuccessEvent(EmptyString())
   {
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -108,24 +108,22 @@ IDBRequest::GetOnerror(nsIDOMEventListen
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest,
                                                   nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnSuccessListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSource)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest,
                                                 nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnSuccessListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSource)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBRequest)
   NS_INTERFACE_MAP_ENTRY(nsIIDBRequest)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBRequest)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(IDBRequest, nsDOMEventTargetHelper)
--- a/dom/src/threads/nsDOMThreadService.cpp
+++ b/dom/src/threads/nsDOMThreadService.cpp
@@ -482,17 +482,18 @@ protected:
           *aCloseRunnableSet = mKillWorkerWhenDone;
           gDOMThreadService->WorkerComplete(this);
           mon.NotifyAll();
           return;
         }
       }
 
       // Clear out any old cruft hanging around in the regexp statics.
-      JS_ClearRegExpStatics(aCx);
+      if (JSObject *global = JS_GetGlobalObject(aCx))
+          JS_ClearRegExpStatics(aCx, global);
 
       runnable->Run();
     }
     NS_NOTREACHED("Shouldn't ever get here!");
   }
 
   // Set at construction
   nsRefPtr<nsDOMWorker> mWorker;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2907,21 +2907,30 @@ JS_GetObjectId(JSContext *cx, JSObject *
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewGlobalObject(JSContext *cx, JSClass *clasp)
 {
     CHECK_REQUEST(cx);
     JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, Valueify(clasp), NULL, NULL);
-    if (obj &&
+    if (!obj ||
         !js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_COMPARTMENT,
                             PrivateValue(cx->compartment))) {
-        return false;
+        return NULL;
     }
+
+    /* FIXME: comment. */
+    JSObject *res = regexp_statics_construct(cx);
+    if (!res ||
+        !js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_REGEXP_STATICS,
+                            ObjectValue(*res))) {
+        return NULL;
+    }
+
     return obj;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp, JSPrincipals *principals)
 {
     CHECK_REQUEST(cx);
     JSCompartment *compartment = NewCompartment(cx, principals);
@@ -5381,79 +5390,106 @@ JS_SetErrorReporter(JSContext *cx, JSErr
 }
 
 /************************************************************************/
 
 /*
  * Regular Expressions.
  */
 JS_PUBLIC_API(JSObject *)
-JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags)
+JS_NewRegExpObject(JSContext *cx, JSObject *obj, char *bytes, size_t length, uintN flags)
 {
     CHECK_REQUEST(cx);
     jschar *chars = js_InflateString(cx, bytes, &length);
     if (!chars)
         return NULL;
-    JSObject *obj = RegExp::createObject(cx, chars, length, flags);
+    RegExpStatics *res = RegExpStatics::extractFrom(obj);
+    JSObject *reobj = RegExp::createObject(cx, res, chars, length, flags);
+    cx->free(chars);
+    return reobj;
+}
+
+JS_PUBLIC_API(JSObject *)
+JS_NewUCRegExpObject(JSContext *cx, JSObject *obj, jschar *chars, size_t length, uintN flags)
+{
+    CHECK_REQUEST(cx);
+    RegExpStatics *res = RegExpStatics::extractFrom(obj);
+    return RegExp::createObject(cx, res, chars, length, flags);
+}
+
+JS_PUBLIC_API(void)
+JS_SetRegExpInput(JSContext *cx, JSObject *obj, JSString *input, JSBool multiline)
+{
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, input);
+
+    RegExpStatics::extractFrom(obj)->reset(input, !!multiline);
+}
+
+JS_PUBLIC_API(void)
+JS_ClearRegExpStatics(JSContext *cx, JSObject *obj)
+{
+    CHECK_REQUEST(cx);
+    JS_ASSERT(obj);
+
+    RegExpStatics::extractFrom(obj)->clear();
+}
+
+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);
+
+    RegExp *re = RegExp::extractFrom(reobj);
+    if (!re)
+        return false;
+
+    JSString *str = js_NewStringCopyN(cx, chars, length);
+    if (!str)
+        return false;
+
+    return re->execute(cx, RegExpStatics::extractFrom(obj), str, indexp, test, Valueify(rval));
+}
+
+JS_PUBLIC_API(JSObject *)
+JS_NewRegExpObjectNoStatics(JSContext *cx, char *bytes, size_t length, uintN flags)
+{
+    CHECK_REQUEST(cx);
+    jschar *chars = js_InflateString(cx, bytes, &length);
+    if (!chars)
+        return NULL;
+    JSObject *obj = RegExp::createObjectNoStatics(cx, chars, length, flags);
     cx->free(chars);
     return obj;
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags)
-{
-    CHECK_REQUEST(cx);
-    return RegExp::createObject(cx, chars, length, flags);
-}
-
-JS_PUBLIC_API(void)
-JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline)
+JS_NewUCRegExpObjectNoStatics(JSContext *cx, jschar *chars, size_t length, uintN flags)
 {
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, input);
-
-    /* No locking required, cx is thread-private and input must be live. */
-    cx->regExpStatics.reset(input, !!multiline);
-}
-
-JS_PUBLIC_API(void)
-JS_ClearRegExpStatics(JSContext *cx)
-{
-    /* No locking required, cx is thread-private and input must be live. */
-    cx->regExpStatics.clear();
-}
-
-JS_PUBLIC_API(void)
-JS_ClearRegExpRoots(JSContext *cx)
-{
-    /* No locking required, cx is thread-private and input must be live. */
-    cx->regExpStatics.clear();
+    return RegExp::createObjectNoStatics(cx, chars, length, flags);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_ExecuteRegExp(JSContext *cx, JSObject *obj, jschar *chars, size_t length,
-                 size_t *indexp, JSBool test, jsval *rval)
+JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *obj, jschar *chars, size_t length,
+                          size_t *indexp, JSBool test, jsval *rval)
 {
     CHECK_REQUEST(cx);
-
+    
     RegExp *re = RegExp::extractFrom(obj);
-    if (!re) {
-      return JS_FALSE;
-    }
+    if (!re)
+        return false;
 
     JSString *str = js_NewStringCopyN(cx, chars, length);
-    if (!str) {
-        return JS_FALSE;
-    }
-    AutoValueRooter v(cx, StringValue(str));
-
-    return re->execute(cx, str, indexp, test, Valueify(rval));
-}
-
-/* TODO: compile, get/set other statics... */
+    if (!str)
+        return false;
+
+    return re->executeNoStatics(cx, str, indexp, test, Valueify(rval));
+}
 
 /************************************************************************/
 
 JS_PUBLIC_API(void)
 JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks)
 {
     cx->localeCallbacks = callbacks;
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1692,20 +1692,21 @@ struct JSClass {
 #define JSCLASS_IS_ANONYMOUS            (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1))
 #define JSCLASS_IS_GLOBAL               (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2))
 
 /* Indicates that JSClass.mark is a tracer with JSTraceOp type. */
 #define JSCLASS_MARK_IS_TRACE           (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
 #define JSCLASS_INTERNAL_FLAG2          (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4))
 
 /* Additional global reserved slots, beyond those for standard prototypes. */
-#define JSRESERVED_GLOBAL_SLOTS_COUNT     3
+#define JSRESERVED_GLOBAL_SLOTS_COUNT     4
 #define JSRESERVED_GLOBAL_COMPARTMENT     (JSProto_LIMIT * 3)
 #define JSRESERVED_GLOBAL_THIS            (JSRESERVED_GLOBAL_COMPARTMENT + 1)
 #define JSRESERVED_GLOBAL_THROWTYPEERROR  (JSRESERVED_GLOBAL_THIS + 1)
+#define JSRESERVED_GLOBAL_REGEXP_STATICS  (JSRESERVED_GLOBAL_THROWTYPEERROR + 1)
 
 /*
  * ECMA-262 requires that most constructors used internally create objects
  * with "the original Foo.prototype value" as their [[Prototype]] (__proto__)
  * member initial value.  The "original ... value" verbiage is there because
  * in ECMA-262, global properties naming class objects are read/write and
  * deleteable, for the most part.
  *
@@ -2946,35 +2947,42 @@ JS_SetErrorReporter(JSContext *cx, JSErr
 #define JSREG_FOLD      0x01    /* fold uppercase to lowercase */
 #define JSREG_GLOB      0x02    /* global exec, creates array of matches */
 #define JSREG_MULTILINE 0x04    /* treat ^ and $ as begin and end of line */
 #define JSREG_STICKY    0x08    /* only match starting at lastIndex */
 #define JSREG_FLAT      0x10    /* parse as a flat regexp */
 #define JSREG_NOCOMPILE 0x20    /* do not try to compile to native code */
 
 extern JS_PUBLIC_API(JSObject *)
-JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags);
+JS_NewRegExpObject(JSContext *cx, JSObject *obj, char *bytes, size_t length, uintN flags);
 
 extern JS_PUBLIC_API(JSObject *)
-JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags);
+JS_NewUCRegExpObject(JSContext *cx, JSObject *obj, jschar *chars, size_t length, uintN flags);
 
 extern JS_PUBLIC_API(void)
-JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline);
+JS_SetRegExpInput(JSContext *cx, JSObject *obj, JSString *input, JSBool multiline);
 
 extern JS_PUBLIC_API(void)
-JS_ClearRegExpStatics(JSContext *cx);
-
-extern JS_PUBLIC_API(void)
-JS_ClearRegExpRoots(JSContext *cx);
+JS_ClearRegExpStatics(JSContext *cx, JSObject *obj);
 
 extern JS_PUBLIC_API(JSBool)
-JS_ExecuteRegExp(JSContext *cx, JSObject *obj, jschar *chars, size_t length,
+JS_ExecuteRegExp(JSContext *cx, JSObject *obj, JSObject *reobj, jschar *chars, size_t length,
                  size_t *indexp, JSBool test, jsval *rval);
 
-/* TODO: compile, get/set other statics... */
+/* RegExp interface for clients without a global object. */
+
+extern JS_PUBLIC_API(JSObject *)
+JS_NewRegExpObjectNoStatics(JSContext *cx, char *bytes, size_t length, uintN flags);
+
+extern JS_PUBLIC_API(JSObject *)
+JS_NewUCRegExpObjectNoStatics(JSContext *cx, jschar *chars, size_t length, uintN flags);
+
+extern JS_PUBLIC_API(JSBool)
+JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *reobj, jschar *chars, size_t length,
+                          size_t *indexp, JSBool test, jsval *rval);
 
 /************************************************************************/
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsExceptionPending(JSContext *cx);
 
 extern JS_PUBLIC_API(JSBool)
 JS_GetPendingException(JSContext *cx, jsval *vp);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1083,19 +1083,16 @@ js_DestroyContext(JSContext *cx, JSDestr
             /* Unpin all common atoms before final GC. */
             js_FinishCommonAtoms(cx);
 
             /* Clear debugging state to remove GC roots. */
             JS_ClearAllTraps(cx);
             JS_ClearAllWatchPoints(cx);
         }
 
-        /* Remove more GC roots in regExpStatics, then collect garbage. */
-        JS_ClearRegExpRoots(cx);
-
 #ifdef JS_THREADSAFE
         /*
          * Destroying a context implicitly calls JS_EndRequest().  Also, we must
          * end our request here in case we are "last" -- in that event, another
          * js_DestroyContext that was not last might be waiting in the GC for our
          * request to end.  We'll let it run below, just before we do the truly
          * final GC and then free atom state.
          */
@@ -1138,17 +1135,16 @@ js_DestroyContext(JSContext *cx, JSDestr
 static void
 FreeContext(JSContext *cx)
 {
 #ifdef JS_THREADSAFE
     JS_ASSERT(!cx->thread);
 #endif
 
     /* Free the stuff hanging off of cx. */
-    cx->regExpStatics.clear();
     VOUCH_DOES_NOT_REQUIRE_STACK();
     JS_FinishArenaPool(&cx->tempPool);
     JS_FinishArenaPool(&cx->regExpPool);
 
     if (cx->lastMessage)
         js_free(cx->lastMessage);
 
     /* Remove any argument formatters. */
@@ -2006,17 +2002,16 @@ DSTOffsetCache::DSTOffsetCache()
 {
     purge();
 }
 
 JSContext::JSContext(JSRuntime *rt)
   : runtime(rt),
     compartment(rt->defaultCompartment),
     regs(NULL),
-    regExpStatics(this),
     busyArrays(this)
 {}
 
 void
 JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs)
 {
     JS_ASSERT(regs != &newregs);
     if (hasActiveSegment()) {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -62,17 +62,17 @@
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsgcchunk.h"
 #include "jshashtable.h"
 #include "jsinterp.h"
 #include "jsobj.h"
 #include "jspropertycache.h"
 #include "jspropertytree.h"
-#include "jsregexp.h"
+#include "jsstaticcheck.h"
 #include "jsutil.h"
 #include "jsarray.h"
 #include "jsvector.h"
 #include "prmjtime.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
@@ -1913,121 +1913,16 @@ typedef struct JSResolvingEntry {
 #define JSRESOLVE_INFER         0xffff  /* infer bits from current bytecode */
 
 extern const JSDebugHooks js_NullDebugHooks;  /* defined in jsdbgapi.cpp */
 
 namespace js {
 
 class AutoGCRooter;
 
-class RegExpStatics
-{
-    js::Vector<int, 20>         matchPairs;
-    JSContext                   *cx;
-    JSString                    *input;
-    uintN                       flags;
-
-    bool createDependent(size_t start, size_t end, Value *out) const;
-
-    size_t pairCount() const {
-        JS_ASSERT(matchPairs.length() % 2 == 0);
-        return matchPairs.length() / 2;
-    }
-    /*
-     * Check whether the index at |checkValidIndex| is valid (>= 0).
-     * If so, construct a string for it and place it in |*out|.
-     * If not, place undefined in |*out|.
-     */
-    bool makeMatch(size_t checkValidIndex, size_t pairNum, Value *out) const;
-    static const uintN allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_STICKY | JSREG_MULTILINE;
-    friend class RegExp;
-
-  public:
-    explicit RegExpStatics(JSContext *cx) : matchPairs(cx), cx(cx) { clear(); }
-    void clone(const RegExpStatics &other);
-
-    /* Mutators. */
-
-    void setMultiline(bool enabled) {
-        if (enabled)
-            flags = flags | JSREG_MULTILINE;
-        else
-            flags = flags & ~JSREG_MULTILINE;
-    }
-
-    void clear() {
-        input = 0;
-        flags = 0;
-        matchPairs.clear();
-    }
-
-    void checkInvariants() {
-        if (pairCount() > 0) {
-            JS_ASSERT(input);
-            JS_ASSERT(get(0, 0) <= get(0, 1));
-            JS_ASSERT(get(0, 1) <= int(input->length()));
-        }
-    }
-
-    void reset(JSString *newInput, bool newMultiline) {
-        clear();
-        input = newInput;
-        setMultiline(newMultiline);
-        checkInvariants();
-    }
-
-    void setInput(JSString *newInput) {
-        input = newInput;
-    }
-
-    /* Accessors. */
-
-    JSString *getInput() const { return input; }
-    uintN getFlags() const { return flags; }
-    bool multiline() const { return flags & JSREG_MULTILINE; }
-    bool matched() const { JS_ASSERT(pairCount() > 0); return get(0, 1) - get(0, 0) > 0; }
-    size_t getParenCount() const { JS_ASSERT(pairCount() > 0); return pairCount() - 1; }
-
-    void mark(JSTracer *trc) const {
-        if (input)
-            JS_CALL_STRING_TRACER(trc, input, "res->input");
-    }
-
-    size_t getParenLength(size_t parenNum) const {
-        if (pairCount() <= parenNum + 1)
-            return 0;
-        return get(parenNum + 1, 1) - get(parenNum + 1, 0);
-    }
-
-    int get(size_t pairNum, bool which) const {
-        JS_ASSERT(pairNum < pairCount());
-        return matchPairs[2 * pairNum + which];
-    }
-
-    /* Value creators. */
-
-    bool createInput(Value *out) const;
-    bool createLastMatch(Value *out) const { return makeMatch(0, 0, out); }
-    bool createLastParen(Value *out) const;
-    bool createLeftContext(Value *out) const;
-    bool createRightContext(Value *out) const;
-
-    bool createParen(size_t parenNum, Value *out) const {
-        return makeMatch((parenNum + 1) * 2, parenNum + 1, out);
-    }
-
-    /* Substring creators. */
-
-    void getParen(size_t num, JSSubString *out) const;
-    void getLastMatch(JSSubString *out) const;
-    void getLastParen(JSSubString *out) const;
-    void getLeftContext(JSSubString *out) const;
-    void getRightContext(JSSubString *out) const;
-};
-
 #define JS_HAS_OPTION(cx,option)        (((cx)->options & (option)) != 0)
 #define JS_HAS_STRICT_OPTION(cx)        JS_HAS_OPTION(cx, JSOPTION_STRICT)
 #define JS_HAS_WERROR_OPTION(cx)        JS_HAS_OPTION(cx, JSOPTION_WERROR)
 #define JS_HAS_COMPILE_N_GO_OPTION(cx)  JS_HAS_OPTION(cx, JSOPTION_COMPILE_N_GO)
 #define JS_HAS_ATLINE_OPTION(cx)        JS_HAS_OPTION(cx, JSOPTION_ATLINE)
 
 static inline bool
 OptionsHasXML(uint32 options)
@@ -2200,19 +2095,16 @@ struct JSContext
     JSArenaPool         tempPool;
 
     /* Temporary arena pool used while evaluate regular expressions. */
     JSArenaPool         regExpPool;
 
     /* Top-level object and pointer to top stack frame's scope chain. */
     JSObject            *globalObject;
 
-    /* Regular expression class statics. */
-    js::RegExpStatics   regExpStatics;
-
     /* State for object and array toSource conversion. */
     JSSharpObjectMap    sharpObjectMap;
     js::HashSet<JSObject *> busyArrays;
 
     /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */
     JSArgumentFormatMap *argumentFormatMap;
 
     /* Last message string and trace file for debugging. */
@@ -2265,16 +2157,18 @@ struct JSContext
     }
 
     /* Return the current segment, which may or may not be active. */
     js::StackSegment *getCurrentSegment() const {
         assertSegmentsInSync();
         return currentSegment;
     }
 
+    inline js::RegExpStatics *regExpStatics();
+
     /* Add the given segment to the list as the new active segment. */
     void pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &regs);
 
     /* Remove the active segment and make the next segment active. */
     void popSegmentAndFrame();
 
     /* Mark the top segment as suspended, without pushing a new one. */
     void saveActiveSegment();
@@ -2557,22 +2451,16 @@ private:
      * The allocation code calls the function to indicate either OOM failure
      * when p is null or that a memory pressure counter has reached some
      * threshold when p is not null. The function takes the pointer and not
      * a boolean flag to minimize the amount of code in its inlined callers.
      */
     JS_FRIEND_API(void) checkMallocGCPressure(void *p);
 };
 
-static inline void
-js_TraceRegExpStatics(JSTracer *trc, JSContext *acx)
-{
-    acx->regExpStatics.mark(trc);
-}
-
 #ifdef JS_THREADSAFE
 # define JS_THREAD_ID(cx)       ((cx)->thread ? (cx)->thread->id : 0)
 #endif
 
 #if defined JS_THREADSAFE && defined DEBUG
 
 namespace js {
 
@@ -3410,16 +3298,18 @@ CanLeaveTrace(JSContext *cx)
 #else
     return JS_FALSE;
 #endif
 }
 
 extern void
 SetPendingException(JSContext *cx, const Value &v);
 
+class RegExpStatics;
+
 } /* namespace js */
 
 /*
  * Get the current frame, first lazily instantiating stack frames if needed.
  * (Do not access cx->fp() directly except in JS_REQUIRES_STACK code.)
  *
  * Defined in jstracer.cpp if JS_TRACER is defined.
  */
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -40,16 +40,32 @@
 
 #ifndef jscntxtinlines_h___
 #define jscntxtinlines_h___
 
 #include "jscntxt.h"
 #include "jsparse.h"
 #include "jsstaticcheck.h"
 #include "jsxml.h"
+#include "jsregexp.h"
+
+inline js::RegExpStatics *
+JSContext::regExpStatics()
+{
+    VOUCH_HAVE_STACK();
+    /*
+     * Whether we're on trace or not, the scope chain associated with cx->fp
+     * will lead us to the appropriate global. Although cx->fp is stale on
+     * trace, trace execution never crosses globals.
+     */
+    JS_ASSERT(hasfp());
+    JSObject *global = fp()->scopeChain().getGlobal();
+    js::RegExpStatics *res = js::RegExpStatics::extractFrom(global);
+    return res;
+}
 
 inline bool
 JSContext::ensureGeneratorStackSpace()
 {
     bool ok = genStack.reserve(genStack.length() + 1);
     if (!ok)
         js_ReportOutOfMemory(this);
     return ok;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1370,16 +1370,34 @@ call_resolve(JSContext *cx, JSObject *ob
     /* Control flow reaches here only if id was not resolved. */
     return JS_TRUE;
 }
 
 static void
 call_trace(JSTracer *trc, JSObject *obj)
 {
     JS_ASSERT(obj->isCall());
+    JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
+    if (fp) {
+        /*
+         * FIXME: Hide copies of stack values rooted by fp from the Cycle
+         * Collector, which currently lacks a non-stub Unlink implementation
+         * for JS objects (including Call objects), so is unable to collect
+         * cycles involving Call objects whose frames are active without this
+         * hiding hack.
+         */
+        uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1;
+        JS_ASSERT(first <= JS_INITIAL_NSLOTS);
+
+        uintN count = fp->fun()->countArgsAndVars();
+        uintN fixed = JS_MIN(count, JS_INITIAL_NSLOTS - first);
+
+        SetValueRangeToUndefined(&obj->fslots[first], fixed);
+        SetValueRangeToUndefined(obj->dslots, count - fixed);
+    }
 
     MaybeMarkGenerator(trc, obj);
 }
 
 JS_PUBLIC_DATA(Class) js_CallClass = {
     "Call",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::CALL_RESERVED_SLOTS) |
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2166,18 +2166,16 @@ MarkContext(JSTracer *trc, JSContext *ac
     }
 
     for (js::AutoGCRooter *gcr = acx->autoGCRooters; gcr; gcr = gcr->down)
         gcr->trace(trc);
 
     if (acx->sharpObjectMap.depth > 0)
         js_TraceSharpMap(trc, &acx->sharpObjectMap);
 
-    js_TraceRegExpStatics(trc, acx);
-
     MarkValue(trc, acx->iterValue, "iterValue");
 
     acx->compartment->marked = true;
 
 #ifdef JS_TRACER
     TracerState* state = acx->tracerState;
     while (state) {
         if (state->nativeVp)
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -8536,19 +8536,29 @@ Parser::primaryExpr(TokenKind tt, JSBool
         break;
 
       case TOK_REGEXP:
       {
         pn = NullaryNode::create(tc);
         if (!pn)
             return NULL;
 
-        JSObject *obj = RegExp::createObject(context, tokenStream.getTokenbuf().begin(),
-                                             tokenStream.getTokenbuf().length(),
-                                             tokenStream.currentToken().t_reflags);
+        JSObject *obj;
+        if (context->hasfp()) {
+            obj = RegExp::createObject(context, context->regExpStatics(),
+                                       tokenStream.getTokenbuf().begin(),
+                                       tokenStream.getTokenbuf().length(),
+                                       tokenStream.currentToken().t_reflags);
+        } else {
+            obj = RegExp::createObjectNoStatics(context,
+                                                tokenStream.getTokenbuf().begin(),
+                                                tokenStream.getTokenbuf().length(),
+                                                tokenStream.currentToken().t_reflags);
+        }
+
         if (!obj)
             return NULL;
         if (!tc->compileAndGo()) {
             obj->clearParent();
             obj->clearProto();
         }
 
         pn->pn_objbox = tc->parser->newObjectBox(obj);
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -61,43 +61,57 @@
 #ifdef JS_TRACER
 #include "jstracer.h"
 using namespace avmplus;
 using namespace nanojit;
 #endif
 
 using namespace js;
 
-
-class RegExpMatchBuilder
-{
-    JSContext   *cx;
-    JSObject    *array;
+/*
+ * RegExpStatics allocates memory -- in order to keep the statics stored
+ * per-global and not leak, we create a js::Class to wrap the C++ instance and
+ * provide an appropriate finalizer. We store an instance of that js::Class in
+ * a global reserved slot.
+ */
 
-  public:
-    RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {}
-    bool append(int index, JSString *str) {
-        JS_ASSERT(str);
-        return append(INT_TO_JSID(index), StringValue(str));
-    }
+static void
+resc_finalize(JSContext *cx, JSObject *obj)
+{
+    RegExpStatics *res = static_cast<RegExpStatics *>(obj->getPrivate());
+    cx->destroy<RegExpStatics>(res);
+}
 
-    bool append(jsid id, Value val) {
-        return !!js_DefineProperty(cx, array, id, &val, js::PropertyStub, js::PropertyStub,
-                                   JSPROP_ENUMERATE);
-    }
+static void
+resc_trace(JSTracer *trc, JSObject *obj)
+{
+    void *pdata = obj->getPrivate();
+    JS_ASSERT(pdata);
+    RegExpStatics *res = static_cast<RegExpStatics *>(pdata);
+    res->mark(trc);
+}
 
-    bool appendIndex(int index) {
-        return append(ATOM_TO_JSID(cx->runtime->atomState.indexAtom), Int32Value(index));
-    }
-
-    /* Sets the input attribute of the match array. */
-    bool appendInput(JSString *str) {
-        JS_ASSERT(str);
-        return append(ATOM_TO_JSID(cx->runtime->atomState.inputAtom), StringValue(str));
-    }
+Class js::regexp_statics_class = {
+    "RegExpStatics", 
+    JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE,
+    PropertyStub,   /* addProperty */
+    PropertyStub,   /* delProperty */
+    PropertyStub,   /* getProperty */
+    PropertyStub,   /* setProperty */
+    EnumerateStub,
+    ResolveStub,
+    ConvertStub,
+    resc_finalize,
+    NULL,           /* reserved0   */
+    NULL,           /* checkAccess */
+    NULL,           /* call        */
+    NULL,           /* construct   */
+    NULL,           /* xdrObject   */
+    NULL,           /* hasInstance */
+    JS_CLASS_TRACE(resc_trace)
 };
 
 /*
  * Lock obj and replace its regexp internals with |newRegExp|.
  * Decref the replaced regexp internals.
  */
 static void
 SwapObjectRegExp(JSContext *cx, JSObject *obj, RegExp &newRegExp)
@@ -108,46 +122,30 @@ SwapObjectRegExp(JSContext *cx, JSObject
         oldRegExp = RegExp::extractFrom(obj);
         obj->setPrivate(&newRegExp);
         obj->zeroRegExpLastIndex();
     }
     if (oldRegExp)
         oldRegExp->decref(cx);
 }
 
-void
-js_SaveAndClearRegExpStatics(JSContext *cx, js::RegExpStatics *statics, js::AutoStringRooter *tvr)
-{
-    JS_ASSERT(statics);
-    statics->clone(cx->regExpStatics);
-    if (statics->getInput())
-        tvr->setString(statics->getInput());
-    cx->regExpStatics.clear();
-}
-
-void
-js_RestoreRegExpStatics(JSContext *cx, js::RegExpStatics *statics)
-{
-    JS_ASSERT(statics);
-    cx->regExpStatics.clone(*statics);
-}
-
 JSObject * JS_FASTCALL
 js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
 {
     JS_ASSERT(obj->getClass() == &js_RegExpClass);
     JS_ASSERT(proto);
     JS_ASSERT(proto->getClass() == &js_RegExpClass);
     JSObject *clone = NewNativeClassInstance(cx, &js_RegExpClass, proto, proto->getParent());
     if (!clone)
         return NULL;
+    RegExpStatics *res = cx->regExpStatics();
     RegExp *re = RegExp::extractFrom(obj);
     {
         uint32 origFlags = re->getFlags();
-        uint32 staticsFlags = cx->regExpStatics.getFlags();
+        uint32 staticsFlags = res->getFlags();
         if ((origFlags & staticsFlags) != staticsFlags) {
             /*
              * This regex is lacking flags from the statics, so we must recompile with the new
              * flags instead of increffing.
              */
             re = RegExp::create(cx, re->getSource(), origFlags | staticsFlags);
         } else {
             re->incref(cx);
@@ -168,120 +166,16 @@ js_ObjectIsRegExp(JSObject *obj)
 {
     return obj->isRegExp();
 }
 
 /*
  * js::RegExp
  */
 
-bool
-RegExp::execute(JSContext *cx, JSString *input, size_t *lastIndex, bool test, Value *rval)
-{
-#if !ENABLE_YARR_JIT
-    JS_ASSERT(compiled);
-#endif
-    const size_t pairCount = parenCount + 1;
-    const size_t bufCount = pairCount * 3; /* Should be x2, but PCRE has... needs. */
-    const size_t matchItemCount = pairCount * 2;
-
-    /*
-     * The regular expression arena pool is special... we want to hang on to it
-     * until a GC is performed so rapid subsequent regexp executions don't
-     * thrash malloc/freeing arena chunks.
-     *
-     * Stick a timestamp at the base of that pool.
-     */
-    if (!cx->regExpPool.first.next) {
-        int64 *timestamp;
-        JS_ARENA_ALLOCATE_CAST(timestamp, int64 *, &cx->regExpPool, sizeof *timestamp);
-        if (!timestamp)
-            return false;
-        *timestamp = JS_Now();
-    }
-
-    AutoArenaAllocator aaa(&cx->regExpPool);
-    int *buf = aaa.alloc<int>(bufCount);
-    if (!buf)
-        return false;
-    /*
-     * The JIT regexp procedure doesn't always initialize matchPair values.
-     * Maybe we can make this faster by ensuring it does?
-     */
-    for (int *it = buf; it != buf + matchItemCount; ++it)
-        *it = -1;
-    const jschar *chars = input->chars();
-    size_t len = input->length();
-    size_t inputOffset = 0;
-    if (sticky()) {
-        /* Sticky matches at the last index for the regexp object. */
-        chars += *lastIndex;
-        len -= *lastIndex;
-        inputOffset = *lastIndex;
-    }
-#if ENABLE_YARR_JIT
-    int result = JSC::Yarr::executeRegex(cx, compiled, chars, *lastIndex - inputOffset, len, buf,
-                                         bufCount);
-#else
-    int result = jsRegExpExecute(cx, compiled, chars, len, *lastIndex - inputOffset, buf, 
-                                 bufCount) < 0 ? -1 : buf[0];
-#endif
-    if (result == -1) {
-        *rval = NullValue();
-        return true;
-    }
-    RegExpStatics &statics = cx->regExpStatics;
-    statics.input = input;
-    statics.matchPairs.clear();
-    if (!statics.matchPairs.reserve(matchItemCount))
-        return false;
-    for (size_t idx = 0; idx < matchItemCount; idx += 2) {
-        JS_ASSERT(buf[idx + 1] >= buf[idx]);
-        if (!statics.matchPairs.append(buf[idx] + inputOffset))
-            return false;
-        if (!statics.matchPairs.append(buf[idx + 1] + inputOffset))
-            return false;
-    }
-    *lastIndex = statics.matchPairs[1];
-    if (test) {
-        *rval = BooleanValue(true);
-        return true;
-    }
-
-    /*
-     * Create the return array for a match. Returned array contents:
-     *  0:              matched string
-     *  1..parenCount:  paren matches
-     */
-    JSObject *array = js_NewSlowArrayObject(cx);
-    if (!array)
-        return false;
-    *rval = ObjectValue(*array);
-    RegExpMatchBuilder builder(cx, array);
-    for (size_t idx = 0; idx < matchItemCount; idx += 2) {
-        int start = statics.matchPairs[idx];
-        int end = statics.matchPairs[idx + 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(idx / 2, captured)))
-                return false;
-        } else {
-            /* Missing parenthesized match. */
-            JS_ASSERT(idx != 0); /* Since we had a match, first pair must be present. */
-            JS_ASSERT(start == end && end == -1);
-            if (!builder.append(INT_TO_JSID(idx / 2), UndefinedValue()))
-                return false;
-        }
-    }
-    return builder.appendIndex(statics.matchPairs[0]) && builder.appendInput(input);
-}
-
 void
 RegExp::handleYarrError(JSContext *cx, int error)
 {
     /* Hack: duplicated from yarr/yarr/RegexParser.h */
     enum ErrorCode {
         NoError,
         PatternTooLarge,
         QuantifierOutOfOrder,
@@ -494,55 +388,55 @@ regexp_resolve(JSContext *cx, JSObject *
  *  RegExp.leftContext          $`
  *  RegExp.rightContext         $'
  */
 
 #define DEFINE_STATIC_GETTER(name, code)                                        \
     static JSBool                                                               \
     name(JSContext *cx, JSObject *obj, jsid id, jsval *vp)                      \
     {                                                                           \
-        RegExpStatics &statics = cx->regExpStatics;                             \
+        RegExpStatics *res = cx->regExpStatics();                               \
         code;                                                                   \
     }
 
-DEFINE_STATIC_GETTER(static_input_getter,        return statics.createInput(Valueify(vp)))
-DEFINE_STATIC_GETTER(static_multiline_getter,    *vp = BOOLEAN_TO_JSVAL(statics.multiline());
+DEFINE_STATIC_GETTER(static_input_getter,        return res->createInput(cx, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_multiline_getter,    *vp = BOOLEAN_TO_JSVAL(res->multiline());
                                                  return true)
-DEFINE_STATIC_GETTER(static_lastMatch_getter,    return statics.createLastMatch(Valueify(vp)))
-DEFINE_STATIC_GETTER(static_lastParen_getter,    return statics.createLastParen(Valueify(vp)))
-DEFINE_STATIC_GETTER(static_leftContext_getter,  return statics.createLeftContext(Valueify(vp)))
-DEFINE_STATIC_GETTER(static_rightContext_getter, return statics.createRightContext(Valueify(vp)))
+DEFINE_STATIC_GETTER(static_lastMatch_getter,    return res->createLastMatch(cx, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_lastParen_getter,    return res->createLastParen(cx, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_leftContext_getter,  return res->createLeftContext(cx, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_rightContext_getter, return res->createRightContext(cx, Valueify(vp)))
 
-DEFINE_STATIC_GETTER(static_paren1_getter,       return statics.createParen(0, Valueify(vp)))
-DEFINE_STATIC_GETTER(static_paren2_getter,       return statics.createParen(1, Valueify(vp)))
-DEFINE_STATIC_GETTER(static_paren3_getter,       return statics.createParen(2, Valueify(vp)))
-DEFINE_STATIC_GETTER(static_paren4_getter,       return statics.createParen(3, Valueify(vp)))
-DEFINE_STATIC_GETTER(static_paren5_getter,       return statics.createParen(4, Valueify(vp)))
-DEFINE_STATIC_GETTER(static_paren6_getter,       return statics.createParen(5, Valueify(vp)))
-DEFINE_STATIC_GETTER(static_paren7_getter,       return statics.createParen(6, Valueify(vp)))
-DEFINE_STATIC_GETTER(static_paren8_getter,       return statics.createParen(7, Valueify(vp)))
-DEFINE_STATIC_GETTER(static_paren9_getter,       return statics.createParen(8, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_paren1_getter,       return res->createParen(cx, 0, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_paren2_getter,       return res->createParen(cx, 1, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_paren3_getter,       return res->createParen(cx, 2, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_paren4_getter,       return res->createParen(cx, 3, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_paren5_getter,       return res->createParen(cx, 4, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_paren6_getter,       return res->createParen(cx, 5, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_paren7_getter,       return res->createParen(cx, 6, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_paren8_getter,       return res->createParen(cx, 7, Valueify(vp)))
+DEFINE_STATIC_GETTER(static_paren9_getter,       return res->createParen(cx, 8, Valueify(vp)))
 
 #define DEFINE_STATIC_SETTER(name, code)                                        \
     static JSBool                                                               \
     name(JSContext *cx, JSObject *obj, jsid id, jsval *vp)                      \
     {                                                                           \
-        RegExpStatics &statics = cx->regExpStatics;                             \
+        RegExpStatics *res = cx->regExpStatics();                               \
         code;                                                                   \
         return true;                                                            \
     }
 
 DEFINE_STATIC_SETTER(static_input_setter,
                      if (!JSVAL_IS_STRING(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp))
                          return false;
-                     statics.setInput(JSVAL_TO_STRING(*vp)))
+                     res->setInput(JSVAL_TO_STRING(*vp)))
 DEFINE_STATIC_SETTER(static_multiline_setter,
                      if (!JSVAL_IS_BOOLEAN(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp))
                          return false;
-                     statics.setMultiline(!!JSVAL_TO_BOOLEAN(*vp)))
+                     res->setMultiline(!!JSVAL_TO_BOOLEAN(*vp)))
 
 const uint8 REGEXP_STATIC_PROP_ATTRS    = JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE;
 const uint8 RO_REGEXP_STATIC_PROP_ATTRS = REGEXP_STATIC_PROP_ATTRS | JSPROP_READONLY;
 
 static JSPropertySpec regexp_static_props[] = {
     {"input",        0, REGEXP_STATIC_PROP_ATTRS,    static_input_getter, static_input_setter},
     {"multiline",    0, REGEXP_STATIC_PROP_ATTRS,    static_multiline_getter,
                                                      static_multiline_setter},
@@ -772,33 +666,34 @@ EscapeNakedForwardSlashes(JSContext *cx,
         JSString *escaped = js_NewString(cx, chars, len);
         if (!escaped)
             cx->free(chars);
         return escaped;
     }
     return unescaped;
 }
 
-static inline JSBool
+static bool
 regexp_compile_sub_tail(JSContext *cx, JSObject *obj, Value *rval, JSString *str, uint32 flags = 0)
 {
-    flags |= cx->regExpStatics.getFlags();
+    flags |= cx->regExpStatics()->getFlags();
     RegExp *re = RegExp::create(cx, str, flags);
     if (!re)
         return false;
     SwapObjectRegExp(cx, obj, *re);
     *rval = ObjectValue(*obj);
     return true;
 }
 
 static JSBool
 regexp_compile_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
     if (!InstanceOf(cx, obj, &js_RegExpClass, argv))
         return false;
+
     if (argc == 0)
         return regexp_compile_sub_tail(cx, obj, rval, cx->runtime->emptyString);
 
     Value sourceValue = argv[0];
     if (sourceValue.isObject() && sourceValue.toObject().getClass() == &js_RegExpClass) {
         /*
          * If we get passed in a RegExp object we construct a new
          * RegExp that is a duplicate of it by re-compiling the
@@ -840,16 +735,17 @@ regexp_compile_sub(JSContext *cx, JSObje
         if (!RegExp::parseFlags(cx, flagStr, flags))
             return false;
     }
 
     JSString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
     if (!escapedSourceStr)
         return false;
     argv[0] = StringValue(escapedSourceStr);
+
     return regexp_compile_sub_tail(cx, obj, rval, escapedSourceStr, flags);
 }
 
 static JSBool
 regexp_compile(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = JS_THIS_OBJECT(cx, Jsvalify(vp));
     return obj && regexp_compile_sub(cx, obj, argc, vp + 2, vp);
@@ -883,27 +779,28 @@ regexp_exec_sub(JSContext *cx, JSObject 
             lastIndex = js_DoubleToInteger(lastIndex);
         }
     } else {
         lastIndex = 0;
     }
     JS_UNLOCK_OBJ(cx, obj);
 
     /* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */
+    RegExpStatics *res = cx->regExpStatics();
     JSString *str;
     if (argc) {
         str = js_ValueToString(cx, argv[0]);
         if (!str) {
             ok = JS_FALSE;
             goto out;
         }
         argv[0] = StringValue(str);
     } else {
         /* Need to grab input from statics. */
-        str = cx->regExpStatics.getInput();
+        str = res->getInput();
         if (!str) {
             const char *sourceBytes = js_GetStringBytes(cx, re->getSource());
             if (sourceBytes) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_INPUT, sourceBytes,
                                      re->global() ? "g" : "",
                                      re->ignoreCase() ? "i" : "",
                                      re->multiline() ? "m" : "",
                                      re->sticky() ? "y" : "");
@@ -913,17 +810,17 @@ regexp_exec_sub(JSContext *cx, JSObject 
         }
     }
 
     if (lastIndex < 0 || str->length() < lastIndex) {
         obj->zeroRegExpLastIndex();
         *rval = NullValue();
     } else {
         size_t lastIndexInt = (size_t) lastIndex;
-        ok = re->execute(cx, str, &lastIndexInt, !!test, rval);
+        ok = re->execute(cx, res, str, &lastIndexInt, !!test, rval);
         if (ok && (re->global() || (!rval->isNull() && re->sticky()))) {
             if (rval->isNull())
                 obj->zeroRegExpLastIndex();
             else
                 obj->setRegExpLastIndex(lastIndexInt);
         }
     }
 
@@ -980,34 +877,44 @@ regexp_construct(JSContext *cx, uintN ar
     /* Otherwise, replace obj with a new RegExp object. */
     JSObject *obj = NewBuiltinClassInstance(cx, &js_RegExpClass);
     if (!obj)
         return false;
 
     return regexp_compile_sub(cx, obj, argc, argv, vp);
 }
 
+/* Similar to regexp_compile_sub_tail. */
+static bool
+InitRegExpClassCompile(JSContext *cx, JSObject *obj)
+{
+    RegExp *re = RegExp::create(cx, cx->runtime->emptyString, 0);
+    if (!re)
+        return false;
+    SwapObjectRegExp(cx, obj, *re);
+    return true;
+}
+
 JSObject *
 js_InitRegExpClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto = js_InitClass(cx, obj, NULL, &js_RegExpClass, regexp_construct, 1,
                                    NULL, regexp_methods, regexp_static_props, NULL);
     if (!proto)
         return NULL;
 
     JSObject *ctor = JS_GetConstructor(cx, proto);
     if (!ctor)
         return NULL;
 
     /* Give RegExp.prototype private data so it matches the empty string. */
-    Value rval;
     if (!JS_AliasProperty(cx, ctor, "input",        "$_") ||
         !JS_AliasProperty(cx, ctor, "multiline",    "$*") ||
         !JS_AliasProperty(cx, ctor, "lastMatch",    "$&") ||
         !JS_AliasProperty(cx, ctor, "lastParen",    "$+") ||
         !JS_AliasProperty(cx, ctor, "leftContext",  "$`") ||
         !JS_AliasProperty(cx, ctor, "rightContext", "$'") ||
-        !regexp_compile_sub(cx, proto, 0, NULL, &rval)) {
+        !InitRegExpClassCompile(cx, proto)) {
         return NULL;
     }
 
     return proto;
 }
--- a/js/src/jsregexp.h
+++ b/js/src/jsregexp.h
@@ -40,23 +40,135 @@
 #ifndef jsregexp_h___
 #define jsregexp_h___
 /*
  * JS regular expression interface.
  */
 #include <stddef.h>
 #include "jsprvtd.h"
 #include "jsstr.h"
+#include "jscntxt.h"
+#include "jsvector.h"
 
 #ifdef JS_THREADSAFE
 #include "jsdhash.h"
 #endif
 
 extern js::Class js_RegExpClass;
 
+namespace js {
+
+class RegExpStatics
+{
+    typedef Vector<int, 20, SystemAllocPolicy> MatchPairs;
+    MatchPairs  matchPairs;
+    JSString    *input;
+    uintN       flags;
+
+    bool createDependent(JSContext *cx, size_t start, size_t end, Value *out) const;
+
+    size_t pairCount() const {
+        JS_ASSERT(matchPairs.length() % 2 == 0);
+        return matchPairs.length() / 2;
+    }
+    /*
+     * Check whether the index at |checkValidIndex| is valid (>= 0).
+     * If so, construct a string for it and place it in |*out|.
+     * If not, place undefined in |*out|.
+     */
+    bool makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, Value *out) const;
+    static const uintN allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_STICKY | JSREG_MULTILINE;
+    friend class RegExp;
+
+  public:
+    explicit RegExpStatics() { clear(); }
+    void clone(const RegExpStatics &other);
+    static RegExpStatics *extractFrom(JSObject *global);
+
+    /* Mutators. */
+
+    void setMultiline(bool enabled) {
+        if (enabled)
+            flags = flags | JSREG_MULTILINE;
+        else
+            flags = flags & ~JSREG_MULTILINE;
+    }
+
+    void clear() {
+        input = 0;
+        flags = 0;
+        matchPairs.clear();
+    }
+
+    void checkInvariants() {
+        if (pairCount() > 0) {
+            JS_ASSERT(input);
+            JS_ASSERT(get(0, 0) <= get(0, 1));
+            JS_ASSERT(get(0, 1) <= int(input->length()));
+        }
+    }
+
+    void reset(JSString *newInput, bool newMultiline) {
+        clear();
+        input = newInput;
+        setMultiline(newMultiline);
+        checkInvariants();
+    }
+
+    void setInput(JSString *newInput) {
+        input = newInput;
+    }
+
+    /* Accessors. */
+
+    JSString *getInput() const { return input; }
+    uintN getFlags() const { return flags; }
+    bool multiline() const { return flags & JSREG_MULTILINE; }
+    bool matched() const { JS_ASSERT(pairCount() > 0); return get(0, 1) - get(0, 0) > 0; }
+    size_t getParenCount() const { JS_ASSERT(pairCount() > 0); return pairCount() - 1; }
+
+    void mark(JSTracer *trc) const {
+        if (input)
+            JS_CALL_STRING_TRACER(trc, input, "res->input");
+    }
+
+    size_t getParenLength(size_t parenNum) const {
+        if (pairCount() <= parenNum + 1)
+            return 0;
+        return get(parenNum + 1, 1) - get(parenNum + 1, 0);
+    }
+
+    int get(size_t pairNum, bool which) const {
+        JS_ASSERT(pairNum < pairCount());
+        return matchPairs[2 * pairNum + which];
+    }
+
+    /* Value creators. */
+
+    bool createInput(JSContext *cx, Value *out) const;
+    bool createLastMatch(JSContext *cx, Value *out) const { return makeMatch(cx, 0, 0, out); }
+    bool createLastParen(JSContext *cx, Value *out) const;
+    bool createLeftContext(JSContext *cx, Value *out) const;
+    bool createRightContext(JSContext *cx, Value *out) const;
+
+    bool createParen(JSContext *cx, size_t parenNum, Value *out) const {
+        return makeMatch(cx, (parenNum + 1) * 2, parenNum + 1, out);
+    }
+
+    /* Substring creators. */
+
+    void getParen(size_t num, JSSubString *out) const;
+    void getLastMatch(JSSubString *out) const;
+    void getLastParen(JSSubString *out) const;
+    void getLeftContext(JSSubString *out) const;
+    void getRightContext(JSSubString *out) const;
+};
+
+}
+
 static inline bool
 VALUE_IS_REGEXP(JSContext *cx, js::Value v)
 {
     return !v.isPrimitive() && v.toObject().isRegExp();
 }
 
 inline const js::Value &
 JSObject::getRegExpLastIndex() const
--- a/js/src/jsregexpinlines.h
+++ b/js/src/jsregexpinlines.h
@@ -48,16 +48,35 @@
 #if ENABLE_YARR_JIT
 #include "yarr/yarr/RegexJIT.h"
 #else
 #include "yarr/pcre/pcre.h"
 #endif
 
 namespace js {
 
+/*
+ * res = RegExp statics.
+ */
+
+extern Class regexp_statics_class;
+
+static inline JSObject *
+regexp_statics_construct(JSContext *cx)
+{
+    JSObject *obj = NewObject<WithProto::Given>(cx, &regexp_statics_class, NULL, NULL);
+    if (!obj)
+        return NULL;
+    RegExpStatics *res = cx->create<RegExpStatics>();
+    if (!res)
+        return NULL;
+    obj->setPrivate(static_cast<void *>(res));
+    return obj;
+}
+
 /* Defined in the inlines header to avoid Yarr dependency includes in main header. */
 class RegExp
 {
     jsrefcount                  refCount;
     JSString                    *source;
 #if ENABLE_YARR_JIT
     JSC::Yarr::RegexCodeBlock   compiled;
 #else
@@ -68,16 +87,22 @@ class RegExp
 
     RegExp(JSString *source, uint32 flags)
       : refCount(1), source(source), compiled(), parenCount(), flags(flags) {}
     bool compileHelper(JSContext *cx, UString &pattern);
     bool compile(JSContext *cx);
     static const uint32 allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_MULTILINE | JSREG_STICKY;
     void handlePCREError(JSContext *cx, int error);
     void handleYarrError(JSContext *cx, int error);
+    static inline bool initArena(JSContext *cx);
+    static inline void checkMatchPairs(int *buf, size_t matchItemCount);
+    JSObject *createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount,
+                           size_t inputOffset);
+    inline bool executeInternal(JSContext *cx, RegExpStatics *res, JSString *input,
+                                size_t *lastIndex, bool test, Value *rval);
 
   public:
     ~RegExp() {
 #if !ENABLE_YARR_JIT
         if (compiled)
             jsRegExpFree(compiled);
 #endif
     }
@@ -92,28 +117,40 @@ class RegExp
 
     /*
      * 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, JSString *input, size_t *lastIndex, bool test, Value *rval);
+    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);
+    }
+
+    bool executeNoStatics(JSContext *cx, JSString *input, size_t *lastIndex, bool test,
+                          Value *rval) {
+        return executeInternal(cx, NULL, input, lastIndex, test, rval);
+    }
 
     /* Factories. */
     static RegExp *create(JSContext *cx, JSString *source, uint32 flags);
     static RegExp *createFlagged(JSContext *cx, JSString *source, JSString *flags);
     /*
      * Create an object with new regular expression internals.
      * @note    The context's regexp statics flags are OR'd into the provided flags,
      *          so this function is really meant for object creation during code
      *          execution, as opposed to during something like XDR.
      */
-    static JSObject *createObject(JSContext *cx, const jschar *chars, size_t length, uint32 flags);
+    static JSObject *createObject(JSContext *cx, RegExpStatics *res, const jschar *chars,
+                                  size_t length, uint32 flags);
+    static JSObject *createObjectNoStatics(JSContext *cx, const jschar *chars, size_t length,
+                                           uint32 flags);
     static RegExp *extractFrom(JSObject *obj);
     static RegExp *clone(JSContext *cx, const RegExp &other);
 
     /* Mutators. */
     void incref(JSContext *cx) { JS_ATOMIC_INCREMENT(&refCount); }
     void decref(JSContext *cx);
 
     /* Accessors. */
@@ -122,44 +159,226 @@ class RegExp
     bool ignoreCase() const { return flags & JSREG_FOLD; }
     bool global() const { return flags & JSREG_GLOB; }
     bool multiline() const { return flags & JSREG_MULTILINE; }
     bool sticky() const { return flags & JSREG_STICKY; }
     const uint32 &getFlags() const { JS_ASSERT((flags & allFlags) == flags); return flags; }
     uint32 flagCount() const;
 };
 
+class RegExpMatchBuilder
+{
+    JSContext   * const cx;
+    JSObject    * const array;
+
+  public:
+    RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {}
+
+    bool append(int index, JSString *str) {
+        JS_ASSERT(str);
+        return append(INT_TO_JSID(index), StringValue(str));
+    }
+
+    bool append(jsid id, Value val) {
+        return !!js_DefineProperty(cx, array, id, &val, js::PropertyStub, js::PropertyStub,
+                                   JSPROP_ENUMERATE);
+    }
+
+    bool appendIndex(int index) {
+        return append(ATOM_TO_JSID(cx->runtime->atomState.indexAtom), Int32Value(index));
+    }
+
+    /* Sets the input attribute of the match array. */
+    bool appendInput(JSString *str) {
+        JS_ASSERT(str);
+        return append(ATOM_TO_JSID(cx->runtime->atomState.inputAtom), StringValue(str));
+    }
+};
+
 /* RegExp inlines. */
 
+inline bool
+RegExp::initArena(JSContext *cx)
+{
+    if (cx->regExpPool.first.next)
+        return true;
+
+    /*
+     * The regular expression arena pool is special... we want to hang on to it
+     * until a GC is performed so rapid subsequent regexp executions don't
+     * thrash malloc/freeing arena chunks.
+     *
+     * Stick a timestamp at the base of that pool.
+     */
+    int64 *timestamp;
+    JS_ARENA_ALLOCATE_CAST(timestamp, int64 *, &cx->regExpPool, sizeof *timestamp);
+    if (!timestamp)
+        return false;
+    *timestamp = JS_Now();
+    return true;
+}
+
+inline void
+RegExp::checkMatchPairs(int *buf, size_t matchItemCount)
+{
+#if DEBUG
+    for (size_t i = 0; i < matchItemCount; i += 2)
+        JS_ASSERT(buf[i + 1] >= buf[i]); /* Limit index must be larger than the start index. */
+#endif
+}
+
+inline JSObject *
+RegExp::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount,
+                     size_t inputOffset)
+{
+#define MATCH_VALUE(__index) (buf[(__index)] + inputOffset)
+    /*
+     * Create the result array for a match. Array contents:
+     *  0:              matched string
+     *  1..parenCount:  paren matches
+     */
+    JSObject *array = js_NewSlowArrayObject(cx);
+    if (!array)
+        return NULL;
+
+    RegExpMatchBuilder builder(cx, array);
+    for (size_t i = 0; i < matchItemCount; i += 2) {
+        int start = MATCH_VALUE(i);
+        int end = MATCH_VALUE(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, captured)))
+                return NULL;
+        } else {
+            /* Missing parenthesized match. */
+            JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
+            JS_ASSERT(start == end && end == -1);
+            if (!builder.append(INT_TO_JSID(i / 2), UndefinedValue()))
+                return NULL;
+        }
+    }
+
+    if (!builder.appendIndex(MATCH_VALUE(0)) ||
+        !builder.appendInput(input))
+        return NULL;
+
+    return array;
+#undef MATCH_VALUE
+}
+
+inline bool
+RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *input,
+                        size_t *lastIndex, bool test, Value *rval)
+{
+#if !ENABLE_YARR_JIT
+    JS_ASSERT(compiled);
+#endif
+    const size_t pairCount = parenCount + 1;
+    const size_t bufCount = pairCount * 3; /* Should be x2, but PCRE has... needs. */
+    const size_t matchItemCount = pairCount * 2;
+
+    if (!initArena(cx))
+        return false;
+
+    AutoArenaAllocator aaa(&cx->regExpPool);
+    int *buf = aaa.alloc<int>(bufCount);
+    if (!buf)
+        return false;
+
+    /*
+     * The JIT regexp procedure doesn't always initialize matchPair values.
+     * Maybe we can make this faster by ensuring it does?
+     */
+    for (int *it = buf; it != buf + matchItemCount; ++it)
+        *it = -1;
+
+    const jschar *chars = input->chars();
+    size_t len = input->length();
+    size_t inputOffset = 0;
+
+    if (sticky()) {
+        /* Sticky matches at the last index for the regexp object. */
+        chars += *lastIndex;
+        len -= *lastIndex;
+        inputOffset = *lastIndex;
+    }
+
+#if ENABLE_YARR_JIT
+    int result = JSC::Yarr::executeRegex(cx, compiled, chars, *lastIndex - inputOffset, len, buf,
+                                         bufCount);
+#else
+    int result = jsRegExpExecute(cx, compiled, chars, len, *lastIndex - inputOffset, buf, 
+                                 bufCount) < 0 ? -1 : buf[0];
+#endif
+    if (result == -1) {
+        *rval = NullValue();
+        return true;
+    }
+
+    checkMatchPairs(buf, matchItemCount);
+
+    if (res) {
+        res->input = input;
+        res->matchPairs.clear();
+        if (!res->matchPairs.reserve(matchItemCount))
+            return false;
+        for (size_t i = 0; i < matchItemCount; ++i)
+            JS_ALWAYS_TRUE(res->matchPairs.append(buf[i] + inputOffset));
+    }
+
+    *lastIndex = buf[1] + inputOffset;
+
+    if (test) {
+        *rval = BooleanValue(true);
+        return true;
+    }
+
+    JSObject *array = createResult(cx, input, buf, matchItemCount, inputOffset);
+    if (!array)
+        return false;
+
+    *rval = ObjectValue(*array);
+    return true;
+}
+
 inline RegExp *
 RegExp::create(JSContext *cx, JSString *source, uint32 flags)
 {
     RegExp *self;
     void *mem = cx->malloc(sizeof(*self));
     if (!mem)
         return NULL;
     self = new (mem) RegExp(source, flags);
     if (!self->compile(cx)) {
         cx->destroy<RegExp>(self);
         return NULL;
     }
     return self;
 }
 
 inline JSObject *
-RegExp::createObject(JSContext *cx, const jschar *chars, size_t length, uint32 flags)
+RegExp::createObject(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
+                     uint32 flags)
+{
+    uint32 staticsFlags = res->getFlags();
+    return createObjectNoStatics(cx, chars, length, flags | staticsFlags);
+}
+
+inline JSObject *
+RegExp::createObjectNoStatics(JSContext *cx, const jschar *chars, size_t length, uint32 flags)
 {
     JS_ASSERT((flags & allFlags) == flags);
     JSString *str = js_NewStringCopyN(cx, chars, length);
     if (!str)
         return NULL;
-    AutoValueRooter tvr(cx, StringValue(str));
-    uint32 staticsFlags = cx->regExpStatics.getFlags();
-    JS_ASSERT((staticsFlags & allFlags) == staticsFlags);
-    RegExp *re = RegExp::create(cx, str, flags | staticsFlags);
+    RegExp *re = RegExp::create(cx, str, flags);
     if (!re)
         return NULL;
     JSObject *obj = NewBuiltinClassInstance(cx, &js_RegExpClass);
     if (!obj) {
         re->decref(cx);
         return NULL;
     }
     obj->setPrivate(re);
@@ -262,101 +481,110 @@ RegExp::extractFrom(JSObject *obj)
 inline RegExp *
 RegExp::clone(JSContext *cx, const RegExp &other)
 {
     return create(cx, other.source, other.flags);
 }
 
 /* RegExpStatics inlines. */
 
+
+inline RegExpStatics *
+RegExpStatics::extractFrom(JSObject *global)
+{
+    Value resVal = global->getReservedSlot(JSRESERVED_GLOBAL_REGEXP_STATICS);
+    RegExpStatics *res = static_cast<RegExpStatics *>(resVal.toObject().getPrivate());
+    return res;
+}
+
 inline void
 RegExpStatics::clone(const RegExpStatics &other)
 {
     JS_ASSERT(this != &other);
     clear();
     input = other.input;
     flags = other.flags;
     JS_ASSERT((flags & allFlags) == flags);
     matchPairs.append(other.matchPairs);
 }
 
 inline bool
-RegExpStatics::createDependent(size_t start, size_t end, Value *out) const 
+RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, Value *out) const 
 {
     JS_ASSERT(start <= end);
     JS_ASSERT(end <= input->length());
     JSString *str = js_NewDependentString(cx, input, start, end - start);
     if (!str)
         return false;
     *out = StringValue(str);
     return true;
 }
 
 inline bool
-RegExpStatics::createInput(Value *out) const
+RegExpStatics::createInput(JSContext *cx, Value *out) const
 {
     *out = input ? StringValue(input) : Valueify(JS_GetEmptyStringValue(cx));
     return true;
 }
 
 inline bool
-RegExpStatics::makeMatch(size_t checkValidIndex, size_t pairNum, Value *out) const
+RegExpStatics::makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, Value *out) const
 {
     if (checkValidIndex / 2 >= pairCount() || matchPairs[checkValidIndex] < 0) {
         *out = Valueify(JS_GetEmptyStringValue(cx));
         return true;
     }
-    return createDependent(get(pairNum, 0), get(pairNum, 1), out);
+    return createDependent(cx, get(pairNum, 0), get(pairNum, 1), out);
 }
 
 inline bool
-RegExpStatics::createLastParen(Value *out) const
+RegExpStatics::createLastParen(JSContext *cx, Value *out) const
 {
     if (pairCount() <= 1) {
         *out = Valueify(JS_GetEmptyStringValue(cx));
         return true;
     }
     size_t num = pairCount() - 1;
     int start = get(num, 0);
     int end = get(num, 1);
     if (start == -1) {
         JS_ASSERT(end == -1);
         *out = Valueify(JS_GetEmptyStringValue(cx));
         return true;
     }
     JS_ASSERT(start >= 0 && end >= 0);
-    return createDependent(start, end, out);
+    return createDependent(cx, start, end, out);
 }
 
 inline bool
-RegExpStatics::createLeftContext(Value *out) const
+RegExpStatics::createLeftContext(JSContext *cx, Value *out) const
 {
     if (!pairCount()) {
         *out = Valueify(JS_GetEmptyStringValue(cx));
         return true;
     }
     if (matchPairs[0] < 0) {
         *out = UndefinedValue();
         return true;
     }
-    return createDependent(0, matchPairs[0], out);
+    return createDependent(cx, 0, matchPairs[0], out);
 }
 
 inline bool
-RegExpStatics::createRightContext(Value *out) const
+RegExpStatics::createRightContext(JSContext *cx, Value *out) const
 {
     if (!pairCount()) {
         *out = Valueify(JS_GetEmptyStringValue(cx));
         return true;
     }
     if (matchPairs[1] < 0) {
         *out = UndefinedValue();
         return true;
     }
-    return createDependent(matchPairs[1], input->length(), out);
+    return createDependent(cx, matchPairs[1], input->length(), out);
 }
 
 inline void
 RegExpStatics::getParen(size_t num, JSSubString *out) const
 {
     out->chars = input->chars() + get(num + 1, 0);
     out->length = getParenLength(num);
 }
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1781,17 +1781,17 @@ class RegExpGuard
 
 /* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
 static JS_ALWAYS_INLINE bool
 Matched(bool test, const Value &v)
 {
     return test ? v.isTrue() : !v.isNull();
 }
 
-typedef bool (*DoMatchCallback)(JSContext *cx, size_t count, void *data);
+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.
  */
 enum MatchControlFlags {
    TEST_GLOBAL_BIT         = 0x1, /* use RegExp.test for global regexps */
    TEST_SINGLE_BIT         = 0x2, /* use RegExp.test for non-global regexps */
@@ -1799,43 +1799,43 @@ 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, Value *vp, JSString *str, const RegExpPair &rep,
+DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegExpPair &rep,
         DoMatchCallback callback, void *data, MatchControlFlags flags)
 {
     RegExp &re = rep.re();
     if (re.global()) {
         /* global matching ('g') */
         bool testGlobal = flags & TEST_GLOBAL_BIT;
         if (rep.reobj())
             rep.reobj()->zeroRegExpLastIndex();
         for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
-            if (!re.execute(cx, str, &i, testGlobal, vp))
+            if (!re.execute(cx, res, str, &i, testGlobal, vp))
                 return false;
             if (!Matched(testGlobal, *vp))
                 break;
-            if (!callback(cx, count, data))
+            if (!callback(cx, res, count, data))
                 return false;
-            if (!cx->regExpStatics.matched())
+            if (!res->matched())
                 ++i;
         }
     } else {
         /* single match */
         bool testSingle = !!(flags & TEST_SINGLE_BIT),
              callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
         size_t i = 0;
-        if (!re.execute(cx, str, &i, testSingle, vp))
+        if (!re.execute(cx, res, str, &i, testSingle, vp))
             return false;
-        if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, 0, data))
+        if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, res, 0, data))
             return false;
     }
     return true;
 }
 
 static bool
 BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value *vp)
 {
@@ -1859,29 +1859,29 @@ BuildFlatMatchArray(JSContext *cx, JSStr
 
 typedef JSObject **MatchArgType;
 
 /*
  * DoMatch will only callback on global matches, hence this function builds
  * only the "array of matches" returned by match on global regexps.
  */
 static bool
-MatchCallback(JSContext *cx, size_t count, void *p)
+MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
 {
     JS_ASSERT(count <= JSID_INT_MAX);  /* by max string length */
 
     JSObject *&arrayobj = *static_cast<MatchArgType>(p);
     if (!arrayobj) {
         arrayobj = js_NewArrayObject(cx, 0, NULL);
         if (!arrayobj)
             return false;
     }
 
     Value v;
-    if (!cx->regExpStatics.createLastMatch(&v))
+    if (!res->createLastMatch(cx, &v))
         return false;
 
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
     return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v);
 }
 
 static JSBool
 str_match(JSContext *cx, uintN argc, Value *vp)
@@ -1896,17 +1896,18 @@ str_match(JSContext *cx, uintN argc, Val
         return BuildFlatMatchArray(cx, str, *fm, vp);
 
     const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
     if (!rep)
         return false;
 
     AutoObjectRooter array(cx);
     MatchArgType arg = array.addr();
-    if (!DoMatch(cx, vp, str, *rep, MatchCallback, arg, MATCH_ARGS))
+    RegExpStatics *res = cx->regExpStatics();
+    if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS))
         return false;
 
     /* When not global, DoMatch will leave |RegExp.exec()| in *vp. */
     if (rep->re().global())
         vp->setObjectOrNull(array.object());
     return true;
 }
 
@@ -1922,22 +1923,23 @@ str_search(JSContext *cx, uintN argc, Va
     if (const FlatMatch *fm = g.tryFlatMatch(str, 1, argc)) {
         vp->setInt32(fm->match());
         return true;
     }
     const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
     if (!rep)
         return false;
 
+    RegExpStatics *res = cx->regExpStatics();
     size_t i = 0;
-    if (!rep->re().execute(cx, str, &i, true, vp))
+    if (!rep->re().execute(cx, res, str, &i, true, vp))
         return false;
 
     if (vp->isTrue())
-        vp->setInt32(cx->regExpStatics.get(0, 0));
+        vp->setInt32(res->get(0, 0));
     else
         vp->setInt32(-1);
     return true;
 }
 
 struct ReplaceData
 {
     ReplaceData(JSContext *cx)
@@ -1954,149 +1956,136 @@ struct ReplaceData
     jsint           leftIndex;      /* left context index in str->chars */
     JSSubString     dollarStr;      /* for "$$" InterpretDollar result */
     bool            calledBack;     /* record whether callback has been called */
     InvokeArgsGuard args;           /* arguments for lambda's js_Invoke call */
     JSCharBuffer    cb;             /* buffer built during DoMatch */
 };
 
 static bool
-InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata,
+InterpretDollar(JSContext *cx, RegExpStatics *res, jschar *dp, jschar *ep, ReplaceData &rdata,
                 JSSubString *out, size_t *skip)
 {
     JS_ASSERT(*dp == '$');
 
     /* If there is only a dollar, bail now */
     if (dp + 1 >= ep)
         return false;
 
     /* Interpret all Perl match-induced dollar variables. */
-    RegExpStatics &statics = cx->regExpStatics;
     jschar dc = dp[1];
     if (JS7_ISDEC(dc)) {
         /* ECMA-262 Edition 3: 1-9 or 01-99 */
         uintN num = JS7_UNDEC(dc);
-        if (num > statics.getParenCount())
+        if (num > res->getParenCount())
             return false;
 
         jschar *cp = dp + 2;
         if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
             uintN tmp = 10 * num + JS7_UNDEC(dc);
-            if (tmp <= statics.getParenCount()) {
+            if (tmp <= res->getParenCount()) {
                 cp++;
                 num = tmp;
             }
         }
         if (num == 0)
             return false;
 
         /* Adjust num from 1 $n-origin to 0 array-index-origin. */
         num--;
         *skip = cp - dp;
-        if (num < statics.getParenCount())
-            statics.getParen(num, out);
+        if (num < res->getParenCount())
+            res->getParen(num, out);
         else
             *out = js_EmptySubString;
         return true;
     }
 
     *skip = 2;
     switch (dc) {
       case '$':
         rdata.dollarStr.chars = dp;
         rdata.dollarStr.length = 1;
         *out = rdata.dollarStr;
         return true;
       case '&':
-        statics.getLastMatch(out);
+        res->getLastMatch(out);
         return true;
       case '+':
-        statics.getLastParen(out);
+        res->getLastParen(out);
         return true;
       case '`':
-        statics.getLeftContext(out);
+        res->getLeftContext(out);
         return true;
       case '\'':
-        statics.getRightContext(out);
+        res->getRightContext(out);
         return true;
     }
     return false;
 }
 
-static JS_ALWAYS_INLINE bool
-PushRegExpSubstr(JSContext *cx, const JSSubString &sub, Value *sp)
+class PreserveRegExpStatics
 {
-    JSString *whole = cx->regExpStatics.getInput();
-    size_t off = sub.chars - whole->chars();
-    JSString *str = js_NewDependentString(cx, whole, off, sub.length);
-    if (!str)
-        return false;
-    sp->setString(str);
-    return true;
-}
-
-class PreserveRegExpStatics {
-    JSContext           * const cx;
-    js::RegExpStatics   container;
+    js::RegExpStatics * const saved;
+    js::RegExpStatics container;
 
   public:
-    PreserveRegExpStatics(JSContext *cx) : cx(cx), container(cx) {
-        container.clone(cx->regExpStatics);
+    explicit PreserveRegExpStatics(RegExpStatics *toSave) : saved(toSave) {
+        container.clone(*toSave);
         container.checkInvariants();
-        cx->regExpStatics.checkInvariants();
+        saved->checkInvariants();
     }
 
     ~PreserveRegExpStatics() {
-        cx->regExpStatics.clone(container);
-        cx->regExpStatics.checkInvariants();
+        saved->clone(container);
+        saved->checkInvariants();
     }
 };
 
 static bool
-FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
+FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t *sizep)
 {
     JSObject *lambda = rdata.lambda;
     if (lambda) {
         LeaveTrace(cx);
 
         /*
          * In the lambda case, not only do we find the replacement string's
          * length, we compute repstr and return it via rdata for use within
          * DoReplace.  The lambda is called with arguments ($&, $1, $2, ...,
          * index, input), i.e., all the properties of a regexp match array.
          * For $&, etc., we must create string jsvals from cx->regExpStatics.
          * We grab up stack space to keep the newborn strings GC-rooted.
          */
-        RegExpStatics &statics = cx->regExpStatics;
-        uintN p = statics.getParenCount();
+        uintN p = res->getParenCount();
         uintN argc = 1 + p + 2;
 
         if (!rdata.args.pushed() && !cx->stack().pushInvokeArgs(cx, argc, &rdata.args))
             return false;
 
-        PreserveRegExpStatics save(cx);
+        PreserveRegExpStatics save(res);
 
         /* Push lambda and its 'this' parameter. */
         CallArgs &args = rdata.args;
         args.callee().setObject(*lambda);
         args.thisv().setNull();
 
         Value *sp = args.argv();
 
         /* Push $&, $1, $2, ... */
-        if (!statics.createLastMatch(sp++))
+        if (!res->createLastMatch(cx, sp++))
             return false;
 
-        for (size_t i = 0; i < statics.getParenCount(); ++i) {
-            if (!statics.createParen(i, sp++))
+        for (size_t i = 0; i < res->getParenCount(); ++i) {
+            if (!res->createParen(cx, i, sp++))
                 return false;
         }
 
         /* Push match index and input string. */
-        sp[0].setInt32(statics.get(0, 0));
+        sp[0].setInt32(res->get(0, 0));
         sp[1].setString(rdata.str);
 
         if (!Invoke(cx, rdata.args, 0))
             return false;
 
         /*
          * NB: we count on the newborn string root to hold any string
          * created by this js_ValueToString that would otherwise be GC-
@@ -2112,80 +2101,79 @@ FindReplaceLength(JSContext *cx, Replace
         return true;
     }
 
     JSString *repstr = rdata.repstr;
     size_t replen = repstr->length();
     for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) {
         JSSubString sub;
         size_t skip;
-        if (InterpretDollar(cx, dp, ep, rdata, &sub, &skip)) {
+        if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
             replen += sub.length - skip;
             dp += skip;
         } else {
             dp++;
         }
     }
     *sizep = replen;
     return true;
 }
 
 static void
-DoReplace(JSContext *cx, ReplaceData &rdata, jschar *chars)
+DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, jschar *chars)
 {
     JSString *repstr = rdata.repstr;
     jschar *cp;
     jschar *bp = cp = repstr->chars();
     for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) {
         size_t len = dp - cp;
         js_strncpy(chars, cp, len);
         chars += len;
         cp = dp;
 
         JSSubString sub;
         size_t skip;
-        if (InterpretDollar(cx, dp, ep, rdata, &sub, &skip)) {
+        if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
             len = sub.length;
             js_strncpy(chars, sub.chars, len);
             chars += len;
             cp += skip;
             dp += skip;
         } else {
             dp++;
         }
     }
     js_strncpy(chars, cp, repstr->length() - (cp - bp));
 }
 
 static bool
-ReplaceCallback(JSContext *cx, size_t count, void *p)
+ReplaceCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
 {
     ReplaceData &rdata = *static_cast<ReplaceData *>(p);
-    RegExpStatics &statics = cx->regExpStatics;
 
     rdata.calledBack = true;
     JSString *str = rdata.str;
     size_t leftoff = rdata.leftIndex;
     const jschar *left = str->chars() + leftoff;
-    size_t leftlen = statics.get(0, 0) - leftoff;
-    rdata.leftIndex = statics.get(0, 1);
+    size_t leftlen = res->get(0, 0) - leftoff;
+    rdata.leftIndex = res->get(0, 1);
 
     size_t replen = 0;  /* silence 'unused' warning */
-    if (!FindReplaceLength(cx, rdata, &replen))
+    if (!FindReplaceLength(cx, res, rdata, &replen))
         return false;
 
     size_t growth = leftlen + replen;
     if (!rdata.cb.growByUninitialized(growth))
         return false;
 
     jschar *chars = rdata.cb.begin() + rdata.index;
     rdata.index += growth;
     js_strncpy(chars, left, leftlen);
     chars += leftlen;
-    DoReplace(cx, rdata, chars);
+    DoReplace(cx, res, rdata, chars);
     return true;
 }
 
 static bool
 BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
                      const FlatMatch &fm, Value *vp)
 {
     JSRopeBuilder builder(cx);
@@ -2342,27 +2330,28 @@ str_replace_regexp(JSContext *cx, uintN 
     const RegExpPair *rep = rdata.g.normalizeRegExp(true, 2, argc, vp);
     if (!rep)
         return false;
 
     rdata.index = 0;
     rdata.leftIndex = 0;
     rdata.calledBack = false;
 
-    if (!DoMatch(cx, vp, rdata.str, *rep, ReplaceCallback, &rdata, REPLACE_ARGS))
+    RegExpStatics *res = cx->regExpStatics();
+    if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceCallback, &rdata, REPLACE_ARGS))
         return false;
 
     if (!rdata.calledBack) {
         /* Didn't match, so the string is unmodified. */
         vp->setString(rdata.str);
         return true;
     }
 
     JSSubString sub;
-    cx->regExpStatics.getRightContext(&sub);
+    res->getRightContext(&sub);
     if (!rdata.cb.append(sub.chars, sub.length))
         return false;
 
     JSString *retstr = js_NewStringFromCharBuffer(cx, rdata.cb);
     if (!retstr)
         return false;
 
     vp->setString(retstr);
@@ -2489,17 +2478,18 @@ js::str_replace(JSContext *cx, uintN arg
  * at offset *ip and looking either for the separator substring given by sep, or
  * for the next re match.  In the re case, return the matched separator in *sep,
  * and the possibly updated offset in *ip.
  *
  * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next
  * separator occurrence if found, or str->length if no separator is found.
  */
 static jsint
-find_split(JSContext *cx, JSString *str, js::RegExp *re, jsint *ip, JSSubString *sep)
+find_split(JSContext *cx, RegExpStatics *res, JSString *str, js::RegExp *re, jsint *ip,
+           JSSubString *sep)
 {
     jsint i;
     size_t length;
     jschar *chars;
 
     /*
      * Stop if past end of string.  If at end of string, we will compare the
      * null char stored there (by js_NewString*) to sep->chars[j] in the while
@@ -2525,26 +2515,26 @@ find_split(JSContext *cx, JSString *str,
      */
     if (re) {
         size_t index;
         Value rval;
 
       again:
         /* JS1.2 deviated from Perl by never matching at end of string. */
         index = (size_t)i;
-        if (!re->execute(cx, str, &index, true, &rval))
+        if (!re->execute(cx, res, str, &index, true, &rval))
             return -2;
         if (!rval.isTrue()) {
             /* Mismatch: ensure our caller advances i past end of string. */
             sep->length = 1;
             return length;
         }
         i = (jsint)index;
         JS_ASSERT(sep);
-        cx->regExpStatics.getLastMatch(sep);
+        res->getLastMatch(sep);
         if (sep->length == 0) {
             /*
              * Empty string match: never split on an empty match at the start
              * of a find_split cycle.  Same rule as for an empty global match
              * in DoMatch.
              */
             if (i == *ip) {
                 /*
@@ -2637,39 +2627,39 @@ str_split(JSContext *cx, uintN argc, Val
         /* Clamp limit between 0 and 1 + string length. */
         limit = js_DoubleToECMAUint32(d);
         if (limit > str->length())
             limit = 1 + str->length();
     }
 
     AutoValueVector splits(cx);
 
+    RegExpStatics *res = cx->regExpStatics();
     jsint i, j;
     uint32 len = i = 0;
-    while ((j = find_split(cx, str, re, &i, sep)) >= 0) {
+    while ((j = find_split(cx, res, str, re, &i, sep)) >= 0) {
         if (limited && len >= limit)
             break;
 
         JSString *sub = js_NewDependentString(cx, str, i, size_t(j - i));
         if (!sub || !splits.append(StringValue(sub)))
             return false;
         len++;
 
         /*
          * Imitate perl's feature of including parenthesized substrings that
          * matched part of the delimiter in the new array, after the split
          * substring that was delimited.
          */
         if (re && sep->chars) {
-            RegExpStatics &statics = cx->regExpStatics;
-            for (uintN num = 0; num < statics.getParenCount(); num++) {
+            for (uintN num = 0; num < res->getParenCount(); num++) {
                 if (limited && len >= limit)
                     break;
                 JSSubString parsub;
-                statics.getParen(num, &parsub);
+                res->getParen(num, &parsub);
                 sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
                 if (!sub || !splits.append(StringValue(sub)))
                     return false;
                 len++;
             }
             sep->chars = NULL;
         }
         i = j + sep->length;
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -509,17 +509,16 @@ JSCompartment::purge(JSContext *cx)
 #endif
 }
 
 AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
     : context(cx),
       origin(cx->compartment),
       target(target),
       destination(target->getCompartment(cx)),
-      statics(cx),
       input(cx),
       entered(false)
 {
 }
 
 AutoCompartment::~AutoCompartment()
 {
     if (entered)
@@ -535,28 +534,26 @@ AutoCompartment::enter()
         context->compartment = destination;
         JSObject *scopeChain = target->getGlobal();
         frame.construct();
         if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref())) {
             frame.destroy();
             context->compartment = origin;
             return false;
         }
-        js_SaveAndClearRegExpStatics(context, &statics, &input);
     }
     entered = true;
     return true;
 }
 
 void
 AutoCompartment::leave()
 {
     JS_ASSERT(entered);
     if (origin != destination) {
-        js_RestoreRegExpStatics(context, &statics);
         frame.destroy();
         context->compartment = origin;
         origin->wrapException(context);
     }
     entered = false;
 }
 
 /* Cross compartment wrappers. */
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -149,17 +149,16 @@ class AutoCompartment
   public:
     JSContext * const context;
     JSCompartment * const origin;
     JSObject * const target;
     JSCompartment * const destination;
   private:
     LazilyConstructed<DummyFrameGuard> frame;
     JSFrameRegs regs;
-    RegExpStatics statics;
     AutoStringRooter input;
     bool entered;
 
   public:
     AutoCompartment(JSContext *cx, JSObject *target);
     ~AutoCompartment();
 
     bool enter();
--- a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp
+++ b/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp
@@ -553,56 +553,52 @@ XPC_SJOW_DelProperty(JSContext *cx, JSOb
   }
 
   return XPCWrapper::DelProperty(cx, unsafeObj, id, vp);
 }
 
 NS_STACK_CLASS class SafeCallGuard {
 public:
   SafeCallGuard(JSContext *cx, nsIPrincipal *principal)
-    : cx(cx), statics(cx), tvr(cx) {
+    : cx(cx) {
     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     if (ssm) {
       // Note: We pass null as the target frame pointer because we know that
       // we're about to set aside the frame chain.
       nsresult rv = ssm->PushContextPrincipal(cx, nsnull, principal);
       if (NS_FAILED(rv)) {
         NS_WARNING("Not allowing call because we're out of memory");
         JS_ReportOutOfMemory(cx);
         this->cx = nsnull;
         return;
       }
     }
 
-    js_SaveAndClearRegExpStatics(cx, &statics, &tvr);
     fp = JS_SaveFrameChain(cx);
     options =
       JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);
   }
 
   JSBool ready() {
     return cx != nsnull;
   }
 
   ~SafeCallGuard() {
     if (cx) {
       JS_SetOptions(cx, options);
       JS_RestoreFrameChain(cx, fp);
-      js_RestoreRegExpStatics(cx, &statics);
       nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
       if (ssm) {
         ssm->PopContextPrincipal(cx);
       }
     }
   }
 
 private:
   JSContext *cx;
-  js::RegExpStatics statics;
-  js::AutoStringRooter tvr;
   uint32 options;
   JSStackFrame *fp;
 };
 
 static JSBool
 XPC_SJOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
                           JSBool aIsSet)
 {
--- a/js/src/xpconnect/tests/mochitest/Makefile.in
+++ b/js/src/xpconnect/tests/mochitest/Makefile.in
@@ -57,27 +57,28 @@ include $(topsrcdir)/config/rules.mk
 		test_bug393269.html \
 		test_bug396851.html \
 		test_bug428021.html \
 		test_bug448587.html \
 		test_wrappers.html \
 		test_bug446584.html \
 		test_bug462428.html \
 		test_bug478438.html \
-		test_bug484107.html \
 		test_bug484459.html \
 		test_bug500691.html \
 		test_bug502959.html \
 		test_bug503926.html \
 		test_bug504877.html \
 		test_bug505915.html \
 		test_bug517163.html \
 		test_bug553407.html \
 		test_bug560351.html \
 		test_bug564330.html \
 		test_cows.html \
 		test_frameWrapping.html \
 		test_bug589028.html \
 		bug589028_helper.html \
 		$(NULL)
 
+		#test_bug484107.html \
+
 libs:: $(_TEST_FILES)
 	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)