Back out 366d80e91816: RegExpPrivate cache. (r=luke, a=LegNeato)
authorChris Leary <cdleary@mozilla.com>
Mon, 14 Nov 2011 19:24:58 -0800
changeset 81372 7f5ae36a96400d22600d837df72c4182db05b6c2
parent 81371 58b66e80b538c5fa87ca6b6fa4f1a8b022aa0f73
child 81373 94c46b0de39058e2b0edeb3e5249720b2bde463f
push idunknown
push userunknown
push dateunknown
reviewersluke, LegNeato
milestone10.0a2
backs out366d80e918160373ca4f72a0006bcbff0619bdc7
Back out 366d80e91816: RegExpPrivate cache. (r=luke, a=LegNeato)
js/src/builtin/RegExp.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsgc.cpp
js/src/jsprvtd.h
js/src/jsstr.cpp
js/src/vm/RegExpObject-inl.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -201,37 +201,42 @@ EscapeNakedForwardSlashes(JSContext *cx,
 
         if (!sb.empty() && !sb.append(*it))
             return NULL;
     }
 
     return sb.empty() ? unescaped : sb.finishString();
 }
 
+static bool
+ResetRegExpObjectWithStatics(JSContext *cx, RegExpObject *reobj,
+                             JSLinearString *str, RegExpFlag flags = RegExpFlag(0))
+{
+    flags = RegExpFlag(flags | cx->regExpStatics()->getFlags());
+    return reobj->reset(cx, str, flags);
+}
+
 /*
  * Compile a new |RegExpPrivate| for the |RegExpObject|.
  *
  * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
  * arguments:
  *
  *  RegExp, undefined => flags := pattern.flags
  *  RegExp, _ => throw TypeError
  *  _ => pattern := ToString(pattern) if defined(pattern) else ''
  *       flags := ToString(flags) if defined(flags) else ''
  */
 static bool
-CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder,
-                    uintN argc, Value *argv, Value *rval)
+CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, Value *rval)
 {
     if (argc == 0) {
-        RegExpStatics *res = cx->regExpStatics();
-        RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags());
-        if (!reobj)
+        if (!ResetRegExpObjectWithStatics(cx, obj, cx->runtime->emptyString))
             return false;
-        *rval = ObjectValue(*reobj);
+        *rval = ObjectValue(*obj);
         return true;
     }
 
     Value sourceValue = argv[0];
     if (ValueIsRegExp(sourceValue)) {
         /*
          * If we get passed in a |RegExpObject| source we return a new
          * object with the same source/flags.
@@ -239,20 +244,19 @@ CompileRegExpObject(JSContext *cx, RegEx
          * Note: the regexp static flags are not taken into consideration here.
          */
         JSObject &sourceObj = sourceValue.toObject();
         if (argc >= 2 && !argv[1].isUndefined()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED);
             return false;
         }
 
-        RegExpObject *reobj = builder.build(sourceObj.asRegExp());
-        if (!reobj)
+        if (!obj->reset(cx, sourceObj.asRegExp()))
             return false;
-        *rval = ObjectValue(*reobj);
+        *rval = ObjectValue(*obj);
         return true;
     }
 
     JSLinearString *sourceStr;
     if (sourceValue.isUndefined()) {
         sourceStr = cx->runtime->emptyString;
     } else {
         /* Coerce to string and compile. */
@@ -276,37 +280,34 @@ CompileRegExpObject(JSContext *cx, RegEx
 
     JSLinearString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
     if (!escapedSourceStr)
         return false;
 
     if (!RegExpPrivateCode::checkSyntax(cx, NULL, escapedSourceStr))
         return false;
 
-    RegExpStatics *res = cx->regExpStatics();
-    RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
-    if (!reobj)
-        return NULL;
-
-    *rval = ObjectValue(*reobj);
+    if (!ResetRegExpObjectWithStatics(cx, obj, escapedSourceStr, flags))
+        return false;
+    *rval = ObjectValue(*obj);
     return true;
 }
 
 static JSBool
 regexp_compile(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
     JSObject *obj = NonGenericMethodGuard(cx, args, regexp_compile, &RegExpClass, &ok);
     if (!obj)
         return ok;
 
-    RegExpObjectBuilder builder(cx, obj->asRegExp());
-    return CompileRegExpObject(cx, builder, args.length(), args.array(), &args.rval());
+    RegExpObject *reobj = obj->asRegExp();
+    return CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval());
 }
 
 static JSBool
 regexp_construct(JSContext *cx, uintN argc, Value *vp)
 {
     Value *argv = JS_ARGV(cx, vp);
 
     if (!IsConstructing(vp)) {
@@ -316,21 +317,24 @@ regexp_construct(JSContext *cx, uintN ar
          * See ECMAv5 15.10.3.1.
          */
         if (argc >= 1 && ValueIsRegExp(argv[0]) && (argc == 1 || argv[1].isUndefined())) {
             *vp = argv[0];
             return true;
         }
     }
 
-    RegExpObjectBuilder builder(cx);
-    if (!CompileRegExpObject(cx, builder, argc, argv, &JS_RVAL(cx, vp)))
+    JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
+    if (!obj)
         return false;
 
-    *vp = ObjectValue(*builder.reobj());
+    if (!CompileRegExpObject(cx, obj->asRegExp(), argc, argv, &JS_RVAL(cx, vp)))
+        return false;
+
+    *vp = ObjectValue(*obj);
     return true;
 }
 
 static JSBool
 regexp_toString(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -453,21 +457,19 @@ js_InitRegExpClass(JSContext *cx, JSObje
 {
     JS_ASSERT(obj->isNative());
 
     GlobalObject *global = obj->asGlobal();
 
     JSObject *proto = global->createBlankPrototype(cx, &RegExpClass);
     if (!proto)
         return NULL;
-    proto->setPrivate(NULL);
 
     RegExpObject *reproto = proto->asRegExp();
-    RegExpObjectBuilder builder(cx, reproto);
-    if (!builder.build(cx->runtime->emptyString, RegExpFlag(0)))
+    if (!reproto->reset(cx, cx->runtime->emptyString, RegExpFlag(0)))
         return NULL;
 
     if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods))
         return NULL;
 
     JSFunction *ctor = global->createConstructor(cx, regexp_construct, &RegExpClass,
                                                  CLASS_ATOM(cx, RegExp), 2);
     if (!ctor)
@@ -519,17 +521,16 @@ ExecuteRegExp(JSContext *cx, Native nati
     if (!rep)
         return true;
 
     /*
      * Code execution under this call could swap out the guts of |reobj|, so we
      * have to take a defensive refcount here.
      */
     AutoRefCount<RegExpPrivate> arc(cx, NeedsIncRef<RegExpPrivate>(rep));
-
     RegExpStatics *res = cx->regExpStatics();
 
     /* Step 2. */
     JSString *input = js_ValueToString(cx, (args.length() > 0) ? args[0] : UndefinedValue());
     if (!input)
         return false;
 
     /* Step 3. */
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -106,17 +106,16 @@ ThreadData::ThreadData()
 #ifdef JS_TRACER
     onTraceCompartment(NULL),
     recordingCompartment(NULL),
     profilingCompartment(NULL),
     maxCodeCacheBytes(DEFAULT_JIT_CACHE_SIZE),
 #endif
     waiveGCQuota(false),
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-    repCache(NULL),
     dtoaState(NULL),
     nativeStackBase(GetNativeStackBase()),
     pendingProxyOperation(NULL),
     interpreterFrames(NULL)
 {
 #ifdef DEBUG
     noGCOrAllocationCheck = 0;
 #endif
@@ -149,41 +148,16 @@ ThreadData::triggerOperationCallback(JSR
 
 #ifdef JS_THREADSAFE
     /* rt->interruptCounter does not reflect suspended threads. */
     if (requestDepth != 0)
         JS_ATOMIC_INCREMENT(&rt->interruptCounter);
 #endif
 }
 
-RegExpPrivateCache *
-ThreadData::createRegExpPrivateCache(JSRuntime *rt)
-{
-    JS_ASSERT(!repCache);
-    RegExpPrivateCache *newCache = rt->new_<RegExpPrivateCache>(rt);
-
-    if (!newCache || !newCache->init()) {
-        rt->delete_<RegExpPrivateCache>(newCache);
-        return NULL;
-    }
-
-    repCache = newCache;
-    return repCache;
-}
-
-void
-ThreadData::purgeRegExpPrivateCache(JSRuntime *rt)
-{
-    if (!repCache)
-        return;
-
-    rt->delete_<RegExpPrivateCache>(repCache);
-    repCache = NULL;
-}
-
 } /* namespace js */
 
 JSScript *
 js_GetCurrentScript(JSContext *cx)
 {
 #ifdef JS_TRACER
     VOUCH_DOES_NOT_REQUIRE_STACK();
     if (JS_ON_TRACE(cx)) {
@@ -336,34 +310,16 @@ js_PurgeThreads(JSContext *cx)
             thread->data.purge(cx);
         }
     }
 #else
     cx->runtime->threadData.purge(cx);
 #endif
 }
 
-void
-js_PurgeThreads_PostGlobalSweep(JSContext *cx)
-{
-#ifdef JS_THREADSAFE
-    for (JSThread::Map::Enum e(cx->runtime->threads);
-         !e.empty();
-         e.popFront())
-    {
-        JSThread *thread = e.front().value;
-
-        JS_ASSERT(!JS_CLIST_IS_EMPTY(&thread->contextList));
-        thread->data.purgeRegExpPrivateCache(cx->runtime);
-    }
-#else
-    cx->runtime->threadData.purgeRegExpPrivateCache(cx->runtime);
-#endif
-}
-
 JSContext *
 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
 {
     JS_AbortIfWrongThread(rt);
 
     /*
      * We need to initialize the new context fully before adding it to the
      * runtime list. After that it can be accessed from another thread via
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -192,35 +192,16 @@ struct ThreadData {
      * because we want allocations to be infallible (except when we hit OOM).
      */
     bool                waiveGCQuota;
 
     /* Temporary arena pool used while compiling and decompiling. */
     static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
     LifoAlloc           tempLifoAlloc;
 
-  private:
-    js::RegExpPrivateCache       *repCache;
-
-    js::RegExpPrivateCache *createRegExpPrivateCache(JSRuntime *rt);
-
-  public:
-    js::RegExpPrivateCache *getRegExpPrivateCache() { return repCache; }
-
-    /* N.B. caller is responsible for reporting OOM. */
-    js::RegExpPrivateCache *getOrCreateRegExpPrivateCache(JSRuntime *rt) {
-        if (repCache)
-            return repCache;
-
-        return createRegExpPrivateCache(rt);
-    }
-
-    /* Called at the end of the global GC sweep phase to deallocate repCache memory. */
-    void purgeRegExpPrivateCache(JSRuntime *rt);
-
     /*
      * The GSN cache is per thread since even multi-cx-per-thread embeddings
      * do not interleave js_GetSrcNote calls.
      */
     GSNCache            gsnCache;
 
     /* Property cache for faster call/get/set invocation. */
     PropertyCache       propertyCache;
@@ -1230,18 +1211,16 @@ struct JSContext
 #ifdef JS_THREADSAFE
     /*
      * When non-null JSContext::free_ delegates the job to the background
      * thread.
      */
     js::GCHelperThread *gcBackgroundFree;
 #endif
 
-    js::ThreadData *threadData() { return JS_THREAD_DATA(this); }
-
     inline void* malloc_(size_t bytes) {
         return runtime->malloc_(bytes, this);
     }
 
     inline void* mallocNoReport(size_t bytes) {
         JS_ASSERT(bytes != 0);
         return runtime->malloc_(bytes, NULL);
     }
@@ -1908,19 +1887,16 @@ extern JSBool
 js_InitThreads(JSRuntime *rt);
 
 extern void
 js_FinishThreads(JSRuntime *rt);
 
 extern void
 js_PurgeThreads(JSContext *cx);
 
-extern void
-js_PurgeThreads_PostGlobalSweep(JSContext *cx);
-
 namespace js {
 
 #ifdef JS_THREADSAFE
 
 /* Iterator over ThreadData from all JSThread instances. */
 class ThreadDataIter : public JSThread::Map::Range
 {
   public:
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2910,19 +2910,16 @@ GCCycle(JSContext *cx, JSCompartment *co
     if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
         if (rt->gcHelperThread.prepareForBackgroundSweep(cx))
             cx->gcBackgroundFree = &rt->gcHelperThread;
     }
 #endif
 
     MarkAndSweep(cx, gckind);
 
-    if (!comp)
-        js_PurgeThreads_PostGlobalSweep(cx);
-
 #ifdef JS_THREADSAFE
     if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
         JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
         cx->gcBackgroundFree = NULL;
         rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK);
     } else {
         JS_ASSERT(!cx->gcBackgroundFree);
     }
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -119,17 +119,16 @@ class JSWrapper;
 
 namespace js {
 
 struct ArgumentsData;
 struct Class;
 
 class RegExpObject;
 class RegExpPrivate;
-class RegExpObjectBuilder;
 class RegExpStatics;
 class MatchPairs;
 
 enum RegExpFlag
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
@@ -228,19 +227,16 @@ typedef Vector<UpvarCookie, 8> UpvarCook
 
 class Breakpoint;
 class BreakpointSite;
 typedef HashMap<jsbytecode *, BreakpointSite *, DefaultHasher<jsbytecode *>, RuntimeAllocPolicy>
     BreakpointSiteMap;
 class Debugger;
 class WatchpointMap;
 
-typedef HashMap<JSAtom *, RegExpPrivate *, DefaultHasher<JSAtom *>, RuntimeAllocPolicy>
-    RegExpPrivateCache;
-
 typedef JSNative             Native;
 typedef JSPropertyOp         PropertyOp;
 typedef JSStrictPropertyOp   StrictPropertyOp;
 typedef JSPropertyDescriptor PropertyDescriptor;
 
 namespace analyze {
 
 struct LifetimeVariable;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1259,35 +1259,28 @@ class FlatMatch
 
     /*
      * Note: The match is -1 when the match is performed successfully,
      * but no match is found.
      */
     int32 match() const { return match_; }
 };
 
-/*
- * Some string methods operate on a RegExpObject, if it is present, but if it
- * is absent create an internal regular expression matcher. This unifies the
- * interface.
- */
 class RegExpPair
 {
-    JSContext                   *cx;
     AutoRefCount<RegExpPrivate> rep_;
     RegExpObject                *reobj_;
 
     explicit RegExpPair(RegExpPair &);
     void operator=(const RegExpPair &);
 
   public:
-    explicit RegExpPair(JSContext *cx) : cx(cx), rep_(cx) {}
+    explicit RegExpPair(JSContext *cx) : rep_(cx) {}
 
     bool resetWithObject(JSContext *cx, RegExpObject *reobj) {
-        JS_ASSERT(cx == this->cx);
         reobj_ = reobj;
         RegExpPrivate *rep = reobj_->asRegExp()->getOrCreatePrivate(cx);
         if (!rep)
             return false;
         rep_.reset(NeedsIncRef<RegExpPrivate>(rep));
         return true;
     }
 
@@ -1304,18 +1297,19 @@ class RegExpPair
         JS_ASSERT(!null());
         return rep_.get();
     }
 };
 
 /*
  * RegExpGuard factors logic out of String regexp operations.
  *
- * |optarg| indicates in which argument position RegExp flags will be found, if
- * present. This is a Mozilla extension and not part of any ECMA spec.
+ * @param optarg    Indicates in which argument position RegExp
+ *                  flags will be found, if present. This is a Mozilla
+ *                  extension and not part of any ECMA spec.
  */
 class RegExpGuard
 {
     RegExpGuard(const RegExpGuard &);
     void operator=(const RegExpGuard &);
 
     JSContext   *cx;
     RegExpPair  rep;
@@ -1369,19 +1363,18 @@ class RegExpGuard
         }
         return true;
     }
 
     /*
      * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
      * pattern string, or a lengthy pattern string can thwart this process.
      *
-     * |checkMetaChars| looks for regexp metachars in the pattern string.
-     *
-     * Return whether flat matching could be used.
+     * @param checkMetaChars    Look for regexp metachars in the pattern string.
+     * @return                  Whether flat matching could be used.
      *
      * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending().
      */
     const FlatMatch *
     tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc,
                  bool checkMetaChars = true)
     {
         if (!rep.null())
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -84,72 +84,79 @@ HasRegExpMetaChars(const jschar *chars, 
 {
     for (size_t i = 0; i < length; ++i) {
         if (IsRegExpMetaChar(chars[i]))
             return true;
     }
     return false;
 }
 
-inline void
-RegExpObject::setPrivate(RegExpPrivate *rep)
-{
-    JSObject::setPrivate(rep);
-}
-
 inline RegExpObject *
 RegExpObject::create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
                      RegExpFlag flags, TokenStream *tokenStream)
 {
     RegExpFlag staticsFlags = res->getFlags();
     return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), tokenStream);
 }
 
 inline RegExpObject *
 RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length,
                               RegExpFlag flags, TokenStream *tokenStream)
 {
-    JSAtom *source = js_AtomizeChars(cx, chars, length);
+    JSLinearString *source = js_NewStringCopyN(cx, chars, length);
     if (!source)
         return NULL;
 
-    return createNoStatics(cx, source, flags, tokenStream);
-}
+    /* |NewBuiltinClassInstance| can GC. */
+    JS::Anchor<JSString *> anchor(source);
 
-inline RegExpObject *
-RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags,
-                              TokenStream *tokenStream)
-{
     if (!RegExpPrivateCode::checkSyntax(cx, tokenStream, source))
         return NULL;
 
-    RegExpObjectBuilder builder(cx);
-    return builder.build(source, flags);
-}
+    JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
+    if (!obj)
+        return NULL;
 
-inline void
-RegExpObject::purge(JSContext *cx)
-{
-    if (RegExpPrivate *rep = getPrivate()) {
-        rep->decref(cx);
-        setPrivate(NULL);
-    }
+    RegExpObject *reobj = obj->asRegExp();
+    return reobj->reset(cx, source, flags) ? reobj : NULL;
 }
 
 inline void
 RegExpObject::finalize(JSContext *cx)
 {
-    purge(cx);
+    if (RegExpPrivate *rep = getPrivate())
+        rep->decref(cx);
 #ifdef DEBUG
-    setPrivate((RegExpPrivate *) 0x1); /* Non-null but still in the zero page. */
+    setPrivate((void *) 0x1); /* Non-null but still in the zero page. */
 #endif
 }
 
 inline bool
-RegExpObject::init(JSContext *cx, JSLinearString *source, RegExpFlag flags)
+RegExpObject::reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep)
+{
+    if (!reset(cx, rep->getSource(), rep->getFlags()))
+        return false;
+
+    setPrivate(rep.get());
+    return true;
+}
+
+inline bool
+RegExpObject::reset(JSContext *cx, RegExpObject *other)
+{
+    if (RegExpPrivate *rep = other->getPrivate()) {
+        rep->incref(cx);
+        return reset(cx, AlreadyIncRefed<RegExpPrivate>(rep));
+    }
+
+    return reset(cx, other->getSource(), other->getFlags());
+}
+
+inline bool
+RegExpObject::reset(JSContext *cx, JSLinearString *source, RegExpFlag flags)
 {
     if (nativeEmpty()) {
         const js::Shape **shapep = &cx->compartment->initialRegExpShape;
         if (!*shapep) {
             *shapep = assignInitialShape(cx);
             if (!*shapep)
                 return false;
         }
@@ -162,93 +169,46 @@ RegExpObject::init(JSContext *cx, JSLine
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->sourceAtom))->slot == SOURCE_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->globalAtom))->slot == GLOBAL_FLAG_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->ignoreCaseAtom))->slot ==
                                  IGNORE_CASE_FLAG_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->multilineAtom))->slot ==
                                  MULTILINE_FLAG_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot == STICKY_FLAG_SLOT);
 
-    JS_ASSERT(!getPrivate());
     zeroLastIndex();
+    setPrivate(NULL);
     setSource(source);
     setGlobal(flags & GlobalFlag);
     setIgnoreCase(flags & IgnoreCaseFlag);
     setMultiline(flags & MultilineFlag);
     setSticky(flags & StickyFlag);
     return true;
 }
 
 /* RegExpPrivate inlines. */
 
 inline AlreadyIncRefed<RegExpPrivate>
 RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts)
 {
     typedef AlreadyIncRefed<RegExpPrivate> RetType;
 
-    /*
-     * We choose to only cache |RegExpPrivate|s who have atoms as
-     * sources, under the unverified premise that non-atoms will have a
-     * low hit rate (both hit ratio and absolute number of hits).
-     */
-    bool cacheable = source->isAtom();
-
-    /*
-     * Refcount note: not all |RegExpPrivate|s are cached so we need to
-     * keep a refcount. The cache holds a "weak ref", where the
-     * |RegExpPrivate|'s deallocation decref will first cause it to
-     * remove itself from the cache.
-     */
-
-    JSRuntime *rt = cx->runtime;
-    RegExpPrivateCache *cache = NULL; /* Quell "may be used uninitialized". */
-    RegExpPrivateCache::AddPtr addPtr;
-    if (cacheable) {
-        cache = cx->threadData()->getOrCreateRegExpPrivateCache(rt);
-        if (!cache) {
-            js_ReportOutOfMemory(cx);
-            return RetType(NULL);
-        }
-
-        addPtr = cache->lookupForAdd(&source->asAtom());
-        if (addPtr) {
-            RegExpPrivate *cached = addPtr->value;
-            if (cached->getFlags() == flags) {
-                cached->incref(cx);
-                return RetType(cached);
-            }
-            /* Note: on flag mismatch, we clobber the existing entry. */
-        }
-    }
-
     JSLinearString *flatSource = source->ensureLinear(cx);
     if (!flatSource)
         return RetType(NULL);
 
-    RegExpPrivate *self = cx->new_<RegExpPrivate>(flatSource, flags);
+    RegExpPrivate *self = cx->new_<RegExpPrivate>(flatSource, flags, cx->compartment);
     if (!self)
         return RetType(NULL);
 
     if (!self->compile(cx, ts)) {
         Foreground::delete_(self);
         return RetType(NULL);
     }
 
-    if (cacheable) {
-        if (addPtr) {
-            JS_ASSERT(addPtr->key == &self->getSource()->asAtom());
-            addPtr->value = self;
-        } else {
-            if (!cache->add(addPtr, &source->asAtom(), self)) {
-                js_ReportOutOfMemory(cx);
-                return RetType(NULL);
-            }
-        }
-    }
-
     return RetType(self);
 }
 
 /* This function should be deleted once bad Android platforms phase out. See bug 604774. */
 inline bool
 RegExpPrivateCode::isJITRuntimeEnabled(JSContext *cx)
 {
 #if defined(ANDROID) && defined(JS_TRACER) && defined(JS_METHODJIT)
@@ -366,30 +326,47 @@ RegExpPrivateCode::execute(JSContext *cx
 
     JS_ASSERT(result >= 0);
     return RegExpRunStatus_Success;
 }
 
 inline void
 RegExpPrivate::incref(JSContext *cx)
 {
+#ifdef DEBUG
+    assertSameCompartment(cx, compartment);
+#endif
     ++refCount;
 }
 
 inline void
 RegExpPrivate::decref(JSContext *cx)
 {
-    if (--refCount != 0)
-        return;
+#ifdef DEBUG
+    assertSameCompartment(cx, compartment);
+#endif
+    if (--refCount == 0)
+        cx->delete_(this);
+}
+
+inline RegExpPrivate *
+RegExpObject::getOrCreatePrivate(JSContext *cx)
+{
+    if (RegExpPrivate *rep = getPrivate())
+        return rep;
 
-    RegExpPrivateCache *cache;
-    if (source->isAtom() && (cache = cx->threadData()->getRegExpPrivateCache())) {
-        RegExpPrivateCache::Ptr ptr = cache->lookup(&source->asAtom());
-        if (ptr && ptr->value == this)
-            cache->remove(ptr);
-    }
+    return makePrivate(cx) ? getPrivate() : NULL;
+}
 
-    cx->delete_(this);
+inline RegExpPrivate *
+RegExpObject::getPrivate() const
+{
+    RegExpPrivate *rep = static_cast<RegExpPrivate *>(JSObject::getPrivate());
+#ifdef DEBUG
+    if (rep)
+        CompartmentChecker::check(compartment(), rep->compartment);
+#endif
+    return rep;
 }
 
 } /* namespace js */
 
 #endif
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -54,116 +54,16 @@ using namespace nanojit;
 
 using namespace js;
 
 JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
 JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
 JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
 JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
 
-/* RegExpObjectBuilder */
-
-bool
-RegExpObjectBuilder::getOrCreate()
-{
-    if (reobj_)
-        return true;
-
-    JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
-    if (!obj)
-        return false;
-    obj->setPrivate(NULL);
-
-    reobj_ = obj->asRegExp();
-    return true;
-}
-
-bool
-RegExpObjectBuilder::getOrCreateClone(RegExpObject *proto)
-{
-    JS_ASSERT(!reobj_);
-
-    JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent());
-    if (!clone)
-        return false;
-    clone->setPrivate(NULL);
-
-    reobj_ = clone->asRegExp();
-    return true;
-}
-
-RegExpObject *
-RegExpObjectBuilder::build(AlreadyIncRefed<RegExpPrivate> rep)
-{
-    if (!getOrCreate()) {
-        rep->decref(cx);
-        return NULL;
-    }
-
-    reobj_->purge(cx);
-    if (!reobj_->init(cx, rep->getSource(), rep->getFlags())) {
-        rep->decref(cx);
-        return NULL;
-    }
-    reobj_->setPrivate(rep.get());
-
-    return reobj_;
-}
-
-RegExpObject *
-RegExpObjectBuilder::build(JSLinearString *source, RegExpFlag flags)
-{
-    if (!getOrCreate())
-        return NULL;
-
-    reobj_->purge(cx);
-    return reobj_->init(cx, source, flags) ? reobj_ : NULL;
-}
-
-RegExpObject *
-RegExpObjectBuilder::build(RegExpObject *other)
-{
-    RegExpPrivate *rep = other->getOrCreatePrivate(cx);
-    if (!rep)
-        return NULL;
-
-    /* Now, incref it for the RegExpObject being built. */
-    rep->incref(cx);
-    return build(AlreadyIncRefed<RegExpPrivate>(rep));
-}
-
-RegExpObject *
-RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto)
-{
-    if (!getOrCreateClone(proto))
-        return NULL;
-
-    /*
-     * Check that the RegExpPrivate for the original is okay to use in
-     * the clone -- if the |RegExpStatics| provides more flags we'll
-     * need a different |RegExpPrivate|.
-     */
-    RegExpStatics *res = cx->regExpStatics();
-    RegExpFlag origFlags = other->getFlags();
-    RegExpFlag staticsFlags = res->getFlags();
-    if ((origFlags & staticsFlags) != staticsFlags) {
-        RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
-        return build(other->getSource(), newFlags);
-    }
-
-    RegExpPrivate *toShare = other->getOrCreatePrivate(cx);
-    if (!toShare)
-        return NULL;
-
-    toShare->incref(cx);
-    return build(AlreadyIncRefed<RegExpPrivate>(toShare));
-}
-
-/* MatchPairs */
-
 MatchPairs *
 MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount)
 {
     void *mem = alloc.alloc(calculateSize(backingPairCount));
     if (!mem)
         return NULL;
 
     return new (mem) MatchPairs(pairCount);
@@ -225,26 +125,26 @@ RegExpPrivate::execute(JSContext *cx, co
     matchPairs->checkAgainst(origLength);
 
     *lastIndex = matchPairs->pair(0).limit;
     *output = matchPairs;
 
     return RegExpRunStatus_Success;
 }
 
-RegExpPrivate *
+bool
 RegExpObject::makePrivate(JSContext *cx)
 {
     JS_ASSERT(!getPrivate());
     AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, getSource(), getFlags(), NULL);
     if (!rep)
-        return NULL;
+        return false;
 
     setPrivate(rep.get());
-    return rep.get();
+    return true;
 }
 
 RegExpRunStatus
 RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
                       LifoAllocScope &allocScope, MatchPairs **output)
 {
     if (!getPrivate() && !makePrivate(cx))
         return RegExpRunStatus_Error;
@@ -304,21 +204,23 @@ js_XDRRegExpObject(JSXDRState *xdr, JSOb
         JS_ASSERT(objp);
         RegExpObject *reobj = (*objp)->asRegExp();
         source = reobj->getSource();
         flagsword = reobj->getFlags();
     }
     if (!JS_XDRString(xdr, &source) || !JS_XDRUint32(xdr, &flagsword))
         return false;
     if (xdr->mode == JSXDR_DECODE) {
-        JSAtom *atom = js_AtomizeString(xdr->cx, source);
-        if (!atom)
+        JS::Anchor<JSString *> anchor(source);
+        const jschar *chars = source->getChars(xdr->cx);
+        if (!chars)
             return false;
-        RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, atom, RegExpFlag(flagsword),
-                                                            NULL);
+        size_t len = source->length();
+        RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, chars, len,
+                                                            RegExpFlag(flagsword), NULL);
         if (!reobj)
             return false;
 
         reobj->clearParent();
         reobj->clearType();
         *objp = reobj;
     }
     return true;
@@ -331,22 +233,16 @@ js_XDRRegExpObject(JSXDRState *xdr, JSOb
 #endif /* !JS_HAS_XDR */
 
 static void
 regexp_finalize(JSContext *cx, JSObject *obj)
 {
     obj->asRegExp()->finalize(cx);
 }
 
-static void
-regexp_trace(JSTracer *trc, JSObject *obj)
-{
-    obj->asRegExp()->purge(trc->context);
-}
-
 Class js::RegExpClass = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     JS_PropertyStub,         /* addProperty */
     JS_PropertyStub,         /* delProperty */
     JS_PropertyStub,         /* getProperty */
@@ -356,17 +252,17 @@ Class js::RegExpClass = {
     JS_ConvertStub,
     regexp_finalize,
     NULL,                    /* reserved0 */
     NULL,                    /* checkAccess */
     NULL,                    /* call */
     NULL,                    /* construct */
     js_XDRRegExpObject,
     NULL,                    /* hasInstance */
-    regexp_trace
+    NULL                     /* trace */
 };
 
 #if ENABLE_YARR_JIT
 void
 RegExpPrivateCode::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error)
 {
     switch (error) {
       case JSC::Yarr::NoError:
@@ -477,24 +373,64 @@ RegExpPrivate::create(JSContext *cx, JSL
 
     RegExpFlag flags = RegExpFlag(0);
     if (!ParseRegExpFlags(cx, opt, &flags))
         return AlreadyIncRefed<RegExpPrivate>(NULL);
 
     return create(cx, str, flags, ts);
 }
 
+RegExpObject *
+RegExpObject::clone(JSContext *cx, RegExpObject *reobj, RegExpObject *proto)
+{
+    JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent());
+    if (!clone)
+        return NULL;
+
+    /*
+     * This clone functionality does not duplicate the JIT'd code blob,
+     * which is necessary for cross-compartment cloning functionality.
+     */
+    assertSameCompartment(cx, reobj, clone);
+
+    RegExpStatics *res = cx->regExpStatics();
+    RegExpObject *reclone = clone->asRegExp();
+
+    /*
+     * Check that the RegExpPrivate for the original is okay to use in
+     * the clone -- if the |RegExpStatics| provides more flags we'll
+     * need a different |RegExpPrivate|.
+     */
+    RegExpFlag origFlags = reobj->getFlags();
+    RegExpFlag staticsFlags = res->getFlags();
+    if ((origFlags & staticsFlags) != staticsFlags) {
+        RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
+        return reclone->reset(cx, reobj->getSource(), newFlags) ? reclone : NULL;
+    }
+
+    RegExpPrivate *toShare = reobj->getOrCreatePrivate(cx);
+    if (!toShare)
+        return NULL;
+
+    toShare->incref(cx);
+    if (!reclone->reset(cx, AlreadyIncRefed<RegExpPrivate>(toShare))) {
+        toShare->decref(cx);
+        return NULL;
+    }
+
+    return reclone;
+}
+
 JSObject * JS_FASTCALL
 js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
 {
     JS_ASSERT(obj->isRegExp());
     JS_ASSERT(proto->isRegExp());
 
-    RegExpObjectBuilder builder(cx);
-    return builder.clone(obj->asRegExp(), proto->asRegExp());
+    return RegExpObject::clone(cx, obj->asRegExp(), proto->asRegExp());
 }
 
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_CloneRegExpObject, CONTEXT, OBJECT, OBJECT, 0,
                      ACCSET_STORE_ANY)
 #endif
 
 JSFlatString *
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -77,24 +77,23 @@ class RegExpObject : public ::JSObject
 
     /*
      * Note: The 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 inline RegExpObject *
     create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length, RegExpFlag flags,
-           TokenStream *tokenStream);
+           TokenStream *ts);
 
     static inline RegExpObject *
     createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
-                    TokenStream *tokenStream);
+                    TokenStream *ts);
 
-    static inline RegExpObject *
-    createNoStatics(JSContext *cx, JSAtom *atom, RegExpFlag flags, TokenStream *tokenStream);
+    static RegExpObject *clone(JSContext *cx, RegExpObject *obj, RegExpObject *proto);
 
     /* Note: fallible. */
     JSFlatString *toString(JSContext *cx) const;
 
     /*
      * Run the regular expression over the input text.
      *
      * Results are placed in |output| as integer pairs. For eaxmple,
@@ -144,86 +143,52 @@ class RegExpObject : public ::JSObject
     void setGlobal(bool enabled)        { setSlot(GLOBAL_FLAG_SLOT, BooleanValue(enabled)); }
     void setMultiline(bool enabled)     { setSlot(MULTILINE_FLAG_SLOT, BooleanValue(enabled)); }
     void setSticky(bool enabled)        { setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled)); }
     bool ignoreCase() const { return getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
     bool global() const     { return getSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
     bool multiline() const  { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
     bool sticky() const     { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
 
+    /*
+     * N.B. |RegExpObject|s can be mutated in place because of |RegExp.prototype.compile|, hence
+     * |reset| for re-initialization.
+     */
+
+    inline bool reset(JSContext *cx, RegExpObject *other);
+    inline bool reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
+    inline bool reset(JSContext *cx, JSLinearString *source, RegExpFlag flags);
+
+    inline RegExpPrivate *getOrCreatePrivate(JSContext *cx);
     inline void finalize(JSContext *cx);
 
-    /* Clear out lazy |RegExpPrivate|. */
-    inline void purge(JSContext *x);
-
-    RegExpPrivate *getOrCreatePrivate(JSContext *cx) {
-        if (RegExpPrivate *rep = getPrivate())
-            return rep;
-
-        return makePrivate(cx);
-    }
-
   private:
-    friend class RegExpObjectBuilder;
-
-    inline bool init(JSContext *cx, JSLinearString *source, RegExpFlag flags);
-
     /* The |RegExpPrivate| is lazily created at the time of use. */
-    RegExpPrivate *getPrivate() const {
-        return static_cast<RegExpPrivate *>(JSObject::getPrivate());
-    }
-
-    inline void setPrivate(RegExpPrivate *rep);
+    inline RegExpPrivate *getPrivate() const;
 
     /*
      * Precondition: the syntax for |source| has already been validated.
      * Side effect: sets the private field.
      */
-    RegExpPrivate *makePrivate(JSContext *cx);
+    bool makePrivate(JSContext *cx);
 
     friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSLinearString *, RegExpFlag);
     friend bool ResetRegExpObject(JSContext *, RegExpObject *, AlreadyIncRefed<RegExpPrivate>);
 
     /*
      * Compute the initial shape to associate with fresh RegExp objects,
      * encoding their initial properties. Return the shape after
      * changing this regular expression object's last property to it.
      */
     const Shape *assignInitialShape(JSContext *cx);
 
     RegExpObject();
     RegExpObject &operator=(const RegExpObject &reo);
 }; /* class RegExpObject */
 
-/* Either builds a new RegExpObject or re-initializes an existing one. */
-class RegExpObjectBuilder
-{
-    JSContext       *cx;
-    RegExpObject    *reobj_;
-
-    bool getOrCreate();
-    bool getOrCreateClone(RegExpObject *proto);
-
-  public:
-    RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj = NULL)
-      : cx(cx), reobj_(reobj)
-    { }
-
-    RegExpObject *reobj() { return reobj_; }
-
-    /* Note: In case of failure, |rep| will be decrefed. */
-    RegExpObject *build(AlreadyIncRefed<RegExpPrivate> rep);
-
-    RegExpObject *build(JSLinearString *str, RegExpFlag flags);
-    RegExpObject *build(RegExpObject *other);
-
-    /* Perform a VM-internal clone. */
-    RegExpObject *clone(RegExpObject *other, RegExpObject *proto);
-};
-
 /* Abstracts away the gross |RegExpPrivate| backend details. */
 class RegExpPrivateCode
 {
 #if ENABLE_YARR_JIT
     typedef JSC::Yarr::BytecodePattern BytecodePattern;
     typedef JSC::Yarr::ErrorCode ErrorCode;
     typedef JSC::Yarr::JSGlobalData JSGlobalData;
     typedef JSC::Yarr::YarrCodeBlock YarrCodeBlock;
@@ -273,57 +238,59 @@ class RegExpPrivateCode
 
 #if ENABLE_YARR_JIT
     static inline bool isJITRuntimeEnabled(JSContext *cx);
     static void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error);
 #else
     static void reportPCREError(JSContext *cx, int error);
 #endif
 
+    inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
+                        RegExpFlag flags);
+
+    inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
+                                   int *output, size_t outputCount);
+
     static size_t getOutputSize(size_t pairCount) {
 #if ENABLE_YARR_JIT
         return pairCount * 2;
 #else
         return pairCount * 3; /* Should be x2, but PCRE has... needs. */
 #endif
     }
-
-    inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
-                        RegExpFlag flags);
-
-
-    inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
-                                   int *output, size_t outputCount);
 };
 
 /*
  * The "meat" of the builtin regular expression objects: it contains the
  * mini-program that represents the source of the regular expression.
  * Excepting refcounts, this is an immutable datastructure after
  * compilation.
  *
  * Non-atomic refcounting is used, so single-thread invariants must be
- * maintained. |RegExpPrivate|s are currently shared within a single
- * |ThreadData|.
+ * maintained: we check regexp operations are performed in a single
+ * compartment.
  *
  * Note: refCount cannot overflow because that would require more
  * referring regexp objects than there is space for in addressable
  * memory.
  */
 class RegExpPrivate
 {
     RegExpPrivateCode   code;
     JSLinearString      *source;
     size_t              refCount;
     uintN               parenCount;
     RegExpFlag          flags;
 
+  public:
+    DebugOnly<JSCompartment *> compartment;
+
   private:
-    RegExpPrivate(JSLinearString *source, RegExpFlag flags)
-      : source(source), refCount(1), parenCount(0), flags(flags)
+    RegExpPrivate(JSLinearString *source, RegExpFlag flags, JSCompartment *compartment)
+      : source(source), refCount(1), parenCount(0), flags(flags), compartment(compartment)
     { }
 
     JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
 
     bool compile(JSContext *cx, TokenStream *ts);
     static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
 
   public: