Back out 61dd23c012ee: lazy regexps. (r=luke, a=LegNeato)
authorChris Leary <cdleary@mozilla.com>
Mon, 14 Nov 2011 19:26:44 -0800
changeset 81375 3f725329f26de473766b9c90681e897d050c59f6
parent 81374 79c9c3483d9796471476cf9980a325624bf25e9f
child 81376 7e62f3dbd056598db533c1dbb6977c76559a5eac
push idunknown
push userunknown
push dateunknown
reviewersluke, LegNeato
milestone10.0a2
backs out61dd23c012eef276495b8956cc8747e3ae4b7abd
Back out 61dd23c012ee: lazy regexps. (r=luke, a=LegNeato)
js/src/builtin/RegExp.cpp
js/src/builtin/RegExp.h
js/src/ds/LifoAlloc.h
js/src/jsapi.cpp
js/src/jsprvtd.h
js/src/jsstr.cpp
js/src/methodjit/Compiler.cpp
js/src/vm/MatchPairs.h
js/src/vm/RegExpObject-inl.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/RegExpStatics.h
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -43,180 +43,66 @@
 #include "builtin/RegExp.h"
 
 #include "vm/RegExpObject-inl.h"
 #include "vm/RegExpStatics-inl.h"
 
 using namespace js;
 using namespace js::types;
 
-class RegExpMatchBuilder
-{
-    JSContext   * const cx;
-    JSObject    * const array;
-
-    bool setProperty(JSAtom *name, Value v) {
-        return !!js_DefineProperty(cx, array, ATOM_TO_JSID(name), &v,
-                                   JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
-    }
-
-  public:
-    RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {}
-
-    bool append(uint32 index, Value v) {
-        JS_ASSERT(!array->getOps()->getElement);
-        return !!js_DefineElement(cx, array, index, &v, JS_PropertyStub, JS_StrictPropertyStub,
-                                  JSPROP_ENUMERATE);
-    }
-
-    bool setIndex(int index) {
-        return setProperty(cx->runtime->atomState.indexAtom, Int32Value(index));
-    }
-
-    bool setInput(JSString *str) {
-        JS_ASSERT(str);
-        return setProperty(cx->runtime->atomState.inputAtom, StringValue(str));
-    }
-};
-
-static bool
-CreateRegExpMatchResult(JSContext *cx, JSString *input, const jschar *chars, size_t length,
-                        MatchPairs *matchPairs, Value *rval)
-{
-    /*
-     * Create the (slow) result array for a match.
-     *
-     * Array contents:
-     *  0:              matched string
-     *  1..pairCount-1: paren matches
-     *  input:          input string
-     *  index:          start index for the match
-     */
-    JSObject *array = NewSlowEmptyArray(cx);
-    if (!array)
-        return false;
-
-    if (!input) {
-        input = js_NewStringCopyN(cx, chars, length);
-        if (!input)
-            return false;
-    }
-
-    RegExpMatchBuilder builder(cx, array);
-
-    for (size_t i = 0; i < matchPairs->pairCount(); ++i) {
-        MatchPair pair = matchPairs->pair(i);
-
-        JSString *captured;
-        if (pair.isUndefined()) {
-            JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
-            if (!builder.append(i, UndefinedValue()))
-                return false;
-        } else {
-            captured = js_NewDependentString(cx, input, pair.start, pair.length());
-            if (!captured || !builder.append(i, StringValue(captured)))
-                return false;
-        }
-    }
-
-    if (!builder.setIndex(matchPairs->pair(0).start) || !builder.setInput(input))
-        return false;
-
-    *rval = ObjectValue(*array);
-    return true;
-}
-
-template <class T>
-bool
-ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T *re, JSLinearString *input,
-                  const jschar *chars, size_t length,
-                  size_t *lastIndex, RegExpExecType type, Value *rval)
-{
-    LifoAllocScope allocScope(&cx->tempLifoAlloc());
-    MatchPairs *matchPairs = NULL;
-    RegExpRunStatus status = re->execute(cx, chars, length, lastIndex, allocScope, &matchPairs);
-
-    switch (status) {
-      case RegExpRunStatus_Error:
-        return false;
-      case RegExpRunStatus_Success_NotFound:
-        *rval = NullValue();
-        return true;
-      default:
-        JS_ASSERT(status == RegExpRunStatus_Success);
-        JS_ASSERT(matchPairs);
-    }
-
-    if (res)
-        res->updateFromMatchPairs(cx, input, matchPairs);
-
-    *lastIndex = matchPairs->pair(0).limit;
-
-    if (type == RegExpTest) {
-        *rval = BooleanValue(true);
-        return true;
-    }
-
-    return CreateRegExpMatchResult(cx, input, chars, length, matchPairs, rval);
-}
-
-bool
-js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpPrivate *rep, JSLinearString *input,
-                  const jschar *chars, size_t length,
-                  size_t *lastIndex, RegExpExecType type, Value *rval)
-{
-    return ExecuteRegExpImpl(cx, res, rep, input, chars, length, lastIndex, type, rval);
-}
-
-bool
-js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj, JSLinearString *input,
-                  const jschar *chars, size_t length,
-                  size_t *lastIndex, RegExpExecType type, Value *rval)
-{
-    return ExecuteRegExpImpl(cx, res, reobj, input, chars, length, lastIndex, type, rval);
-}
-
-/* Note: returns the original if no escaping need be performed. */
-static JSLinearString *
-EscapeNakedForwardSlashes(JSContext *cx, JSLinearString *unescaped)
+/*
+ * Return:
+ * - The original if no escaping need be performed.
+ * - A new string if escaping need be performed.
+ * - NULL on error.
+ */
+static JSString *
+EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped)
 {
     size_t oldLen = unescaped->length();
-    const jschar *oldChars = unescaped->chars();
-
+    const jschar *oldChars = unescaped->getChars(cx);
+    if (!oldChars)
+        return NULL;
     JS::Anchor<JSString *> anchor(unescaped);
 
-    /* We may never need to use |sb|. Start using it lazily. */
-    StringBuffer sb(cx);
-
+    js::Vector<jschar, 128> newChars(cx);
     for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) {
         if (*it == '/' && (it == oldChars || it[-1] != '\\')) {
-            /* There's a forward slash that needs escaping. */
-            if (sb.empty()) {
-                /* This is the first one we've seen, copy everything up to this point. */
-                if (!sb.reserve(oldLen + 1))
+            if (!newChars.length()) {
+                if (!newChars.reserve(oldLen + 1))
                     return NULL;
-                sb.infallibleAppend(oldChars, size_t(it - oldChars));
+                newChars.infallibleAppend(oldChars, size_t(it - oldChars));
             }
-            if (!sb.append('\\'))
+            if (!newChars.append('\\'))
                 return NULL;
         }
 
-        if (!sb.empty() && !sb.append(*it))
+        if (!newChars.empty() && !newChars.append(*it))
             return NULL;
     }
 
-    return sb.empty() ? unescaped : sb.finishString();
+    if (newChars.empty())
+        return unescaped;
+
+    size_t len = newChars.length();
+    if (!newChars.append('\0'))
+        return NULL;
+    jschar *chars = newChars.extractRawBuffer();
+    JSString *escaped = js_NewString(cx, chars, len);
+    if (!escaped)
+        cx->free_(chars);
+    return escaped;
 }
 
 static bool
 ResetRegExpObjectWithStatics(JSContext *cx, RegExpObject *reobj,
-                             JSLinearString *str, RegExpFlag flags = RegExpFlag(0))
+                             JSString *str, RegExpFlag flags = RegExpFlag(0))
 {
     flags = RegExpFlag(flags | cx->regExpStatics()->getFlags());
-    return reobj->reset(cx, str, flags);
+    return ResetRegExpObject(cx, reobj, str, flags);
 }
 
 /*
  * Compile a new |RegExpPrivate| for the |RegExpObject|.
  *
  * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
  * arguments:
  *
@@ -234,62 +120,61 @@ CompileRegExpObject(JSContext *cx, RegEx
         *rval = ObjectValue(*obj);
         return true;
     }
 
     Value sourceValue = argv[0];
     if (ValueIsRegExp(sourceValue)) {
         /*
          * If we get passed in a |RegExpObject| source we return a new
-         * object with the same source/flags.
+         * object with the same |RegExpPrivate|.
          *
          * 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;
         }
 
-        if (!obj->reset(cx, sourceObj.asRegExp()))
+        RegExpPrivate *rep = sourceObj.asRegExp()->getPrivate();
+        if (!rep)
+            return false;
+
+        rep->incref(cx);
+        if (!ResetRegExpObject(cx, obj, AlreadyIncRefed<RegExpPrivate>(rep)))
             return false;
         *rval = ObjectValue(*obj);
         return true;
     }
 
-    JSLinearString *sourceStr;
+    JSString *sourceStr;
     if (sourceValue.isUndefined()) {
         sourceStr = cx->runtime->emptyString;
     } else {
         /* Coerce to string and compile. */
-        JSString *str = js_ValueToString(cx, sourceValue);
-        if (!str)
-            return false;
-        sourceStr = str->ensureLinear(cx);
+        sourceStr = js_ValueToString(cx, sourceValue);
         if (!sourceStr)
             return false;
     }
 
     RegExpFlag flags = RegExpFlag(0);
     if (argc > 1 && !argv[1].isUndefined()) {
         JSString *flagStr = js_ValueToString(cx, argv[1]);
         if (!flagStr)
             return false;
         argv[1].setString(flagStr);
         if (!ParseRegExpFlags(cx, flagStr, &flags))
             return false;
     }
 
-    JSLinearString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
+    JSString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
     if (!escapedSourceStr)
         return false;
 
-    if (!RegExpPrivateCode::checkSyntax(cx, NULL, escapedSourceStr))
-        return false;
-
     if (!ResetRegExpObjectWithStatics(cx, obj, escapedSourceStr, flags))
         return false;
     *rval = ObjectValue(*obj);
     return true;
 }
 
 static JSBool
 regexp_compile(JSContext *cx, uintN argc, Value *vp)
@@ -297,17 +182,19 @@ regexp_compile(JSContext *cx, uintN argc
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
     JSObject *obj = NonGenericMethodGuard(cx, args, regexp_compile, &RegExpClass, &ok);
     if (!obj)
         return ok;
 
     RegExpObject *reobj = obj->asRegExp();
-    return CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval());
+    ok = CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval());
+    JS_ASSERT_IF(ok, reobj->getPrivate());
+    return ok;
 }
 
 static JSBool
 regexp_construct(JSContext *cx, uintN argc, Value *vp)
 {
     Value *argv = JS_ARGV(cx, vp);
 
     if (!IsConstructing(vp)) {
@@ -321,20 +208,26 @@ regexp_construct(JSContext *cx, uintN ar
             return true;
         }
     }
 
     JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
     if (!obj)
         return false;
 
-    if (!CompileRegExpObject(cx, obj->asRegExp(), argc, argv, &JS_RVAL(cx, vp)))
+    PreInitRegExpObject pireo(obj);
+    RegExpObject *reobj = pireo.get();
+
+    if (!CompileRegExpObject(cx, reobj, argc, argv, &JS_RVAL(cx, vp))) {
+        pireo.fail();
         return false;
+    }
 
-    *vp = ObjectValue(*obj);
+    pireo.succeed();
+    *vp = ObjectValue(*reobj);
     return true;
 }
 
 static JSBool
 regexp_toString(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -458,19 +351,40 @@ js_InitRegExpClass(JSContext *cx, JSObje
     JS_ASSERT(obj->isNative());
 
     GlobalObject *global = obj->asGlobal();
 
     JSObject *proto = global->createBlankPrototype(cx, &RegExpClass);
     if (!proto)
         return NULL;
 
-    RegExpObject *reproto = proto->asRegExp();
-    if (!reproto->reset(cx, cx->runtime->emptyString, RegExpFlag(0)))
-        return NULL;
+    {
+        AlreadyIncRefed<RegExpPrivate> rep =
+          RegExpPrivate::create(cx, cx->runtime->emptyString, RegExpFlag(0), NULL);
+        if (!rep)
+            return NULL;
+
+        /*
+         * Associate the empty regular expression with |RegExp.prototype|, and define
+         * the initial non-method properties of any regular expression instance.
+         * These must be added before methods to preserve slot layout.
+         */
+#ifdef DEBUG
+        assertSameCompartment(cx, proto, rep->compartment);
+#endif
+
+        PreInitRegExpObject pireo(proto);
+        RegExpObject *reproto = pireo.get();
+        if (!ResetRegExpObject(cx, reproto, rep)) {
+            pireo.fail();
+            return NULL;
+        }
+
+        pireo.succeed();
+    }
 
     if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods))
         return NULL;
 
     JSFunction *ctor = global->createConstructor(cx, regexp_construct, &RegExpClass,
                                                  CLASS_ATOM(cx, RegExp), 2);
     if (!ctor)
         return NULL;
@@ -500,80 +414,73 @@ js_InitRegExpClass(JSContext *cx, JSObje
 }
 
 /*
  * ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2).
  *
  * RegExp.prototype.test doesn't need to create a results array, and we use
  * |execType| to perform this optimization.
  */
-static bool
+static JSBool
 ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     bool ok;
     JSObject *obj = NonGenericMethodGuard(cx, args, native, &RegExpClass, &ok);
     if (!obj)
         return ok;
 
     RegExpObject *reobj = obj->asRegExp();
-    RegExpPrivate *rep = reobj->getOrCreatePrivate(cx);
-    if (!rep)
+    RegExpPrivate *re = reobj->getPrivate();
+    if (!re)
         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));
+    AutoRefCount<RegExpPrivate> arc(cx, NeedsIncRef<RegExpPrivate>(re));
     RegExpStatics *res = cx->regExpStatics();
 
     /* Step 2. */
     JSString *input = js_ValueToString(cx, (args.length() > 0) ? args[0] : UndefinedValue());
     if (!input)
         return false;
 
     /* Step 3. */
-    JSLinearString *linearInput = input->ensureLinear(cx);
-    if (!linearInput)
-        return false;
-    const jschar *chars = linearInput->chars();
     size_t length = input->length();
 
     /* Step 4. */
     const Value &lastIndex = reobj->getLastIndex();
 
     /* Step 5. */
     jsdouble i;
     if (!ToInteger(cx, lastIndex, &i))
         return false;
 
     /* Steps 6-7 (with sticky extension). */
-    if (!rep->global() && !rep->sticky())
+    if (!re->global() && !re->sticky())
         i = 0;
 
     /* Step 9a. */
     if (i < 0 || i > length) {
         reobj->zeroLastIndex();
         args.rval() = NullValue();
         return true;
     }
 
     /* Steps 8-21. */
-    RegExpExecType execType = native == regexp_test ? RegExpTest : RegExpExec;
     size_t lastIndexInt(i);
-    if (!ExecuteRegExp(cx, res, rep, linearInput, chars, length, &lastIndexInt, execType,
-                       &args.rval())) {
+    if (!re->execute(cx, res, input, &lastIndexInt, native == regexp_test, &args.rval()))
         return false;
-    }
 
     /* Step 11 (with sticky extension). */
-    if (rep->global() || (!args.rval().isNull() && rep->sticky())) {
+    if (re->global() || (!args.rval().isNull() && re->sticky())) {
         if (args.rval().isNull())
             reobj->zeroLastIndex();
         else
             reobj->setLastIndex(lastIndexInt);
     }
 
     return true;
 }
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -48,31 +48,16 @@ js_InitRegExpClass(JSContext *cx, JSObje
 
 /*
  * The following builtin natives are extern'd for pointer comparison in
  * other parts of the engine.
  */
 
 namespace js {
 
-/* 
- * |res| may be null if the |RegExpStatics| are not to be updated.
- * |input| may be null if there is no |JSString| corresponding to
- * |chars| and |length|.
- */
-bool
-ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj, JSLinearString *input,
-              const jschar *chars, size_t length,
-              size_t *lastIndex, RegExpExecType type, Value *rval);
-
-bool
-ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpPrivate *rep, JSLinearString *input,
-              const jschar *chars, size_t length,
-              size_t *lastIndex, RegExpExecType type, Value *rval);
-
 extern JSBool
 regexp_exec(JSContext *cx, uintN argc, Value *vp);
 
 extern JSBool
 regexp_test(JSContext *cx, uintN argc, Value *vp);
 
 } /* namespace js */
 
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -339,20 +339,16 @@ class LifoAllocScope {
         mark = lifoAlloc->mark();
     }
 
     ~LifoAllocScope() {
         if (shouldRelease)
             lifoAlloc->release(mark);
     }
 
-    LifoAlloc &alloc() {
-        return *lifoAlloc;
-    }
-
     void releaseEarly() {
         JS_ASSERT(shouldRelease);
         lifoAlloc->release(mark);
         shouldRelease = false;
     }
 };
 
 } /* namespace js */
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6040,19 +6040,26 @@ JS_ClearRegExpStatics(JSContext *cx, JSO
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteRegExp(JSContext *cx, JSObject *obj, JSObject *reobj, jschar *chars, size_t length,
                  size_t *indexp, JSBool test, jsval *rval)
 {
     CHECK_REQUEST(cx);
 
+    RegExpPrivate *rep = reobj->asRegExp()->getPrivate();
+    if (!rep)
+        return false;
+
+    JSString *str = js_NewStringCopyN(cx, chars, length);
+    if (!str)
+        return false;
+
     RegExpStatics *res = obj->asGlobal()->getRegExpStatics();
-    return ExecuteRegExp(cx, res, reobj->asRegExp(), NULL, chars, length,
-                         indexp, test ? RegExpTest : RegExpExec, rval);
+    return rep->execute(cx, res, str, indexp, test, rval);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewRegExpObjectNoStatics(JSContext *cx, char *bytes, size_t length, uintN flags)
 {
     CHECK_REQUEST(cx);
     jschar *chars = InflateString(cx, bytes, &length);
     if (!chars)
@@ -6070,18 +6077,25 @@ JS_NewUCRegExpObjectNoStatics(JSContext 
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *obj, jschar *chars, size_t length,
                           size_t *indexp, JSBool test, jsval *rval)
 {
     CHECK_REQUEST(cx);
 
-    return ExecuteRegExp(cx, NULL, obj->asRegExp(), NULL, chars, length, indexp,
-                         test ? RegExpTest : RegExpExec, rval);
+    RegExpPrivate *rep = obj->asRegExp()->getPrivate();
+    if (!rep)
+        return false;
+
+    JSString *str = js_NewStringCopyN(cx, chars, length);
+    if (!str)
+        return false;
+
+    return rep->executeNoStatics(cx, str, indexp, test, rval);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ObjectIsRegExp(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj);
     return obj->isRegExp();
 }
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -117,38 +117,30 @@ class JSRope;
 class JSAtom;
 class JSWrapper;
 
 namespace js {
 
 struct ArgumentsData;
 struct Class;
 
-class RegExpObject;
 class RegExpPrivate;
 class RegExpStatics;
-class MatchPairs;
 
 enum RegExpFlag
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
     StickyFlag      = 0x08,
 
     NoFlags         = 0x00,
     AllFlags        = 0x0f
 };
 
-enum RegExpExecType
-{
-    RegExpExec,
-    RegExpTest
-};
-
 class AutoStringRooter;
 class ExecuteArgsGuard;
 class InvokeFrameGuard;
 class InvokeArgsGuard;
 class StringBuffer;
 class TraceRecorder;
 struct TraceMonitor;
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -67,17 +67,16 @@
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsprobes.h"
 #include "jsscope.h"
 #include "jsstr.h"
 #include "jsversion.h"
 
-#include "builtin/RegExp.h"
 #include "vm/GlobalObject.h"
 #include "vm/RegExpObject.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsautooplen.h"        // generated headers last
 
 #include "vm/RegExpObject-inl.h"
@@ -513,31 +512,31 @@ js_str_toString(JSContext *cx, uintN arg
 
     args.rval().setString(str);
     return true;
 }
 
 /*
  * Java-like string native methods.
  */
-
+ 
 JS_ALWAYS_INLINE bool
 ValueToIntegerRange(JSContext *cx, const Value &v, int32 *out)
 {
     if (v.isInt32()) {
         *out = v.toInt32();
     } else {
         double d;
         if (!ToInteger(cx, v, &d))
             return false;
         if (d > INT32_MAX)
             *out = INT32_MAX;
         else if (d < INT32_MIN)
             *out = INT32_MIN;
-        else
+        else 
             *out = int32(d);
     }
 
     return true;
 }
 
 static JSBool
 str_substring(JSContext *cx, uintN argc, Value *vp)
@@ -1249,59 +1248,53 @@ class FlatMatch
     const jschar    *pat;
     size_t          patlen;
     int32           match_;
 
     friend class RegExpGuard;
 
   public:
     FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
-    JSLinearString *pattern() const { return patstr; }
+    JSString *pattern() const { return patstr; }
     size_t patternLength() const { return patlen; }
 
     /*
      * Note: The match is -1 when the match is performed successfully,
      * but no match is found.
      */
     int32 match() const { return match_; }
 };
 
+/* A regexp and optional associated object. */
 class RegExpPair
 {
     AutoRefCount<RegExpPrivate> rep_;
-    RegExpObject                *reobj_;
+    JSObject                    *reobj_;
 
     explicit RegExpPair(RegExpPair &);
-    void operator=(const RegExpPair &);
 
   public:
     explicit RegExpPair(JSContext *cx) : rep_(cx) {}
 
-    bool resetWithObject(JSContext *cx, RegExpObject *reobj) {
-        reobj_ = reobj;
-        RegExpPrivate *rep = reobj_->asRegExp()->getOrCreatePrivate(cx);
-        if (!rep)
-            return false;
+    void reset(JSObject &obj) {
+        reobj_ = &obj;
+        RegExpPrivate *rep = reobj_->asRegExp()->getPrivate();
+        JS_ASSERT(rep);
         rep_.reset(NeedsIncRef<RegExpPrivate>(rep));
-        return true;
     }
 
-    void resetWithPrivate(AlreadyIncRefed<RegExpPrivate> rep) {
+    void reset(AlreadyIncRefed<RegExpPrivate> rep) {
         reobj_ = NULL;
         rep_.reset(rep);
     }
 
-    bool null() const { return rep_.null(); }
-
-    RegExpObject *reobj() const { return reobj_; }
-
-    RegExpPrivate *getPrivate() const {
-        JS_ASSERT(!null());
-        return rep_.get();
-    }
+    /* Note: May be null. */
+    JSObject *reobj() const { return reobj_; }
+    bool hasRegExp() const { return !rep_.null(); }
+    RegExpPrivate &re() const { JS_ASSERT(hasRegExp()); return *rep_; }
 };
 
 /*
  * RegExpGuard factors logic out of String regexp operations.
  *
  * @param optarg    Indicates in which argument position RegExp
  *                  flags will be found, if present. This is a Mozilla
  *                  extension and not part of any ECMA spec.
@@ -1316,17 +1309,17 @@ class RegExpGuard
     FlatMatch   fm;
 
     /*
      * Upper bound on the number of characters we are willing to potentially
      * waste on searching for RegExp meta-characters.
      */
     static const size_t MAX_FLAT_PAT_LEN = 256;
 
-    static JSLinearString *flattenPattern(JSContext *cx, JSLinearString *patstr) {
+    static JSString *flattenPattern(JSContext *cx, JSLinearString *patstr) {
         StringBuffer sb(cx);
         if (!sb.reserve(patstr->length()))
             return NULL;
 
         static const jschar ESCAPE_CHAR = '\\';
         const jschar *chars = patstr->chars();
         size_t len = patstr->length();
         for (const jschar *it = chars; it != chars + len; ++it) {
@@ -1345,23 +1338,23 @@ class RegExpGuard
     explicit RegExpGuard(JSContext *cx) : cx(cx), rep(cx) {}
     ~RegExpGuard() {}
 
     /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
     bool
     init(uintN argc, Value *vp, bool convertVoid = false)
     {
         if (argc != 0 && ValueIsRegExp(vp[2])) {
-            rep.resetWithObject(cx, vp[2].toObject().asRegExp());
+            rep.reset(vp[2].toObject());
         } else {
             if (convertVoid && (argc == 0 || vp[2].isUndefined())) {
                 fm.patstr = cx->runtime->emptyString;
                 return true;
             }
-
+            
             fm.patstr = ArgToRootedString(cx, argc, vp, 0);
             if (!fm.patstr)
                 return false;
         }
         return true;
     }
 
     /*
@@ -1372,17 +1365,17 @@ class RegExpGuard
      * @return                  Whether flat matching could be used.
      *
      * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending().
      */
     const FlatMatch *
     tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc,
                  bool checkMetaChars = true)
     {
-        if (!rep.null())
+        if (rep.hasRegExp())
             return NULL;
 
         fm.pat = fm.patstr->chars();
         fm.patlen = fm.patstr->length();
 
         if (optarg < argc)
             return NULL;
 
@@ -1405,56 +1398,56 @@ class RegExpGuard
         }
         return &fm;
     }
 
     /* If the pattern is not already a regular expression, make it so. */
     const RegExpPair *
     normalizeRegExp(bool flat, uintN optarg, uintN argc, Value *vp)
     {
-        if (!rep.null())
+        if (rep.hasRegExp())
             return &rep;
 
         /* Build RegExp from pattern string. */
         JSString *opt;
         if (optarg < argc) {
             opt = js_ValueToString(cx, vp[2 + optarg]);
             if (!opt)
                 return NULL;
         } else {
             opt = NULL;
         }
 
-        JSLinearString *patstr;
+        JSString *patstr;
         if (flat) {
             patstr = flattenPattern(cx, fm.patstr);
             if (!patstr)
                 return NULL;
         } else {
             patstr = fm.patstr;
         }
         JS_ASSERT(patstr);
 
-        AlreadyIncRefed<RegExpPrivate> re = RegExpPrivate::create(cx, patstr, opt, NULL);
+        AlreadyIncRefed<RegExpPrivate> re = RegExpPrivate::createFlagged(cx, patstr, opt, NULL);
         if (!re)
             return NULL;
-        rep.resetWithPrivate(re);
+        rep.reset(re);
         return &rep;
     }
 
 #if DEBUG
-    bool hasRegExpPair() const { return !rep.null(); }
+    bool hasRegExpPair() const { return rep.hasRegExp(); }
 #endif
 };
 
-/* ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
+/* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
 static JS_ALWAYS_INLINE bool
-Matched(RegExpExecType type, const Value &v)
+Matched(bool test, const Value &v)
 {
-    return type == RegExpTest ? v.isTrue() : !v.isNull();
+    return test ? v.isTrue() : !v.isNull();
 }
 
 typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data);
 
 /*
  * BitOR-ing these flags allows the DoMatch caller to control when how the
  * RegExp engine is called and when callbacks are fired.
  */
@@ -1465,50 +1458,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, RegExpStatics *res, JSString *str, const RegExpPair &regExpPair,
+DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &rep,
         DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
 {
-    RegExpPrivate *rep = regExpPair.getPrivate();
-    JSLinearString *linearStr = str->ensureLinear(cx);
-    if (!linearStr)
-        return false;
-    const jschar *chars = linearStr->chars();
-    size_t length = linearStr->length();
-
-    if (rep->global()) {
+    RegExpPrivate &re = rep.re();
+    if (re.global()) {
         /* global matching ('g') */
-        RegExpExecType type = flags & TEST_GLOBAL_BIT ? RegExpTest : RegExpExec;
-        if (RegExpObject *reobj = regExpPair.reobj())
-            reobj->zeroLastIndex();
-
+        bool testGlobal = flags & TEST_GLOBAL_BIT;
+        if (rep.reobj())
+            rep.reobj()->asRegExp()->zeroLastIndex();
         for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
-            if (!ExecuteRegExp(cx, res, rep, linearStr, chars, length, &i, type, rval))
+            if (!re.execute(cx, res, str, &i, testGlobal, rval))
                 return false;
-            if (!Matched(type, *rval))
+            if (!Matched(testGlobal, *rval))
                 break;
             if (!callback(cx, res, count, data))
                 return false;
             if (!res->matched())
                 ++i;
         }
     } else {
         /* single match */
-        RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec;
-        bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
+        bool testSingle = !!(flags & TEST_SINGLE_BIT),
+             callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
         size_t i = 0;
-        if (!ExecuteRegExp(cx, res, rep, linearStr, chars, length, &i, type, rval))
+        if (!re.execute(cx, res, str, &i, testSingle, rval))
             return false;
-        if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data))
+        if (callbackOnSingle && Matched(testSingle, *rval) && !callback(cx, res, 0, data))
             return false;
     }
     return true;
 }
 
 static bool
 BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value *vp)
 {
@@ -1551,17 +1537,17 @@ MatchCallback(JSContext *cx, RegExpStati
 }
 
 JSBool
 js::str_match(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *str = ThisToStringForStringProto(cx, vp);
     if (!str)
         return false;
-
+    
     RegExpGuard g(cx);
     if (!g.init(argc, vp, true))
         return false;
     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc))
         return BuildFlatMatchArray(cx, str, *fm, vp);
     if (cx->isExceptionPending())  /* from tryFlatMatch */
         return false;
 
@@ -1571,17 +1557,17 @@ js::str_match(JSContext *cx, uintN argc,
 
     AutoObjectRooter array(cx);
     MatchArgType arg = array.addr();
     RegExpStatics *res = cx->regExpStatics();
     Value rval;
     if (!DoMatch(cx, res, str, *rep, MatchCallback, arg, MATCH_ARGS, &rval))
         return false;
 
-    if (rep->getPrivate()->global())
+    if (rep->re().global())
         vp->setObjectOrNull(array.object());
     else
         *vp = rval;
     return true;
 }
 
 JSBool
 js::str_search(JSContext *cx, uintN argc, Value *vp)
@@ -1594,30 +1580,24 @@ js::str_search(JSContext *cx, uintN argc
     if (!g.init(argc, vp, true))
         return false;
     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc)) {
         vp->setInt32(fm->match());
         return true;
     }
     if (cx->isExceptionPending())  /* from tryFlatMatch */
         return false;
-
+    
     const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
     if (!rep)
         return false;
 
-    JSLinearString *linearStr = str->ensureLinear(cx);
-    if (!linearStr)
-        return false;
-    const jschar *chars = linearStr->chars();
-    size_t length = linearStr->length();
     RegExpStatics *res = cx->regExpStatics();
-    /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
     size_t i = 0;
-    if (!ExecuteRegExp(cx, res, rep->getPrivate(), linearStr, chars, length, &i, RegExpTest, vp))
+    if (!rep->re().execute(cx, res, str, &i, true, vp))
         return false;
 
     if (vp->isTrue())
         vp->setInt32(res->matchStart());
     else
         vp->setInt32(-1);
     return true;
 }
@@ -1670,17 +1650,17 @@ InterpretDollar(JSContext *cx, RegExpSta
         }
         if (num == 0)
             return false;
 
         *skip = cp - dp;
 
         JS_ASSERT(num <= res->parenCount());
 
-        /*
+        /* 
          * Note: we index to get the paren with the (1-indexed) pair
          * number, as opposed to a (0-indexed) paren number.
          */
         res->getParen(num, out);
         return true;
     }
 
     *skip = 2;
@@ -1828,17 +1808,17 @@ FindReplaceLength(JSContext *cx, RegExpS
         } else {
             dp++;
         }
     }
     *sizep = replen;
     return true;
 }
 
-/*
+/* 
  * Precondition: |rdata.sb| already has necessary growth space reserved (as
  * derived from FindReplaceLength).
  */
 static void
 DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata)
 {
     JSLinearString *repstr = rdata.repstr;
     const jschar *cp;
@@ -2225,17 +2205,17 @@ js::str_replace(JSContext *cx, uintN arg
     if (fm->match() < 0) {
         vp->setString(rdata.str);
         return true;
     }
 
     if (rdata.lambda)
         return str_replace_flat_lambda(cx, argc, vp, rdata, *fm);
 
-    /*
+    /* 
      * Note: we could optimize the text.length == pattern.length case if we wanted,
      * even in the presence of dollar metachars.
      */
     if (rdata.dollar)
         return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollar, *fm, vp);
 
     return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, vp);
 }
@@ -2396,32 +2376,30 @@ SplitHelper(JSContext *cx, JSLinearStrin
  * The SplitMatch operation from ES5 15.5.4.14 is implemented using different
  * matchers for regular expression and string separators.
  *
  * The algorithm differs from the spec in that the matchers return the next
  * index at which a match happens.
  */
 class SplitRegExpMatcher {
     RegExpStatics *res;
-    RegExpPrivate *rep;
+    RegExpPrivate *re;
 
   public:
     static const bool returnsCaptures = true;
-    SplitRegExpMatcher(RegExpPrivate *rep, RegExpStatics *res) : res(res), rep(rep) {}
+    SplitRegExpMatcher(RegExpPrivate *re, RegExpStatics *res) : res(res), re(re) {}
 
     inline bool operator()(JSContext *cx, JSLinearString *str, size_t index,
                            SplitMatchResult *result) {
         Value rval
 #ifdef __GNUC__ /* quell GCC overwarning */
             = UndefinedValue()
 #endif
         ;
-        const jschar *chars = str->chars();
-        size_t length = str->length();
-        if (!ExecuteRegExp(cx, res, rep, str, chars, length, &index, RegExpTest, &rval))
+        if (!re->execute(cx, res, str, &index, true, &rval))
             return false;
         if (!rval.isTrue()) {
             result->setFailure();
             return true;
         }
         JSSubString sep;
         res->getLastMatch(&sep);
 
@@ -2480,19 +2458,17 @@ js::str_split(JSContext *cx, uintN argc,
     }
 
     /* Step 8. */
     RegExpPrivate *re = NULL;
     JSLinearString *sepstr = NULL;
     bool sepUndefined = (argc == 0 || vp[2].isUndefined());
     if (!sepUndefined) {
         if (ValueIsRegExp(vp[2])) {
-            re = vp[2].toObject().asRegExp()->getOrCreatePrivate(cx);
-            if (!re)
-                return false;
+            re = vp[2].toObject().asRegExp()->getPrivate();
         } else {
             JSString *sep = js_ValueToString(cx, vp[2]);
             if (!sep)
                 return false;
             vp[2].setString(sep);
 
             sepstr = sep->ensureLinear(cx);
             if (!sepstr)
@@ -2563,17 +2539,17 @@ str_substr(JSContext *cx, uintN argc, Va
             begin += length; /* length + INT_MIN will always be less then 0 */
             if (begin < 0)
                 begin = 0;
         }
 
         if (argc == 1 || vp[3].isUndefined()) {
             len = length - begin;
         } else {
-            if (!ValueToIntegerRange(cx, vp[3], &len))
+            if (!ValueToIntegerRange(cx, vp[3], &len))  
                 return false;
 
             if (len <= 0) {
                 str = cx->runtime->emptyString;
                 goto out;
             }
 
             if (uint32(length) < uint32(begin + len))
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -6515,19 +6515,19 @@ mjit::Compiler::jsop_regexp()
                                        types::OBJECT_FLAG_REGEXP_FLAGS_SET)) {
         prepareStubCall(Uses(0));
         masm.move(ImmPtr(obj), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
         frame.pushSynced(JSVAL_TYPE_OBJECT);
         return;
     }
 
-    RegExpObject *reobj = obj->asRegExp();
-
-    DebugOnly<uint32> origFlags = reobj->getFlags();
+    RegExpPrivate *regexp = static_cast<RegExpObject *>(obj)->getPrivate();
+
+    DebugOnly<uint32> origFlags = regexp->getFlags();
     DebugOnly<uint32> staticsFlags = res->getFlags();
     JS_ASSERT((origFlags & staticsFlags) == staticsFlags);
 
     /*
      * JS semantics require regular expression literals to create different
      * objects every time they execute. We only need to do this cloning if the
      * script could actually observe the effect of such cloning, by getting
      * or setting properties on it. Particular RegExp and String natives take
@@ -6569,16 +6569,20 @@ mjit::Compiler::jsop_regexp()
     Jump emptyFreeList = masm.getNewObject(cx, result, obj);
 
     stubcc.linkExit(emptyFreeList, Uses(0));
     stubcc.leave();
 
     stubcc.masm.move(ImmPtr(obj), Registers::ArgReg1);
     OOL_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
 
+    /* Bump the refcount on the wrapped RegExp. */
+    size_t *refcount = regexp->addressOfRefCount();
+    masm.add32(Imm32(1), AbsoluteAddress(refcount));
+
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result);
 
     stubcc.rejoin(Changes(1));
 }
 
 bool
 mjit::Compiler::startLoop(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
 {
deleted file mode 100644
--- a/js/src/vm/MatchPairs.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sw=4 et tw=99 ft=cpp:
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla SpiderMonkey JavaScript code.
- *
- * The Initial Developer of the Original Code is
- * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Chris Leary <cdleary@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef MatchPairs_h__
-#define MatchPairs_h__
-
-/*
- * RegExp match results are succinctly represented by pairs of integer
- * indices delimiting (start, limit] segments of the input string.
- *
- * The pair count for a given RegExp match is the capturing parentheses
- * count plus one for the "0 capturing paren" whole text match.
- */
-
-namespace js {
-
-struct MatchPair
-{
-    int start;
-    int limit;
-
-    MatchPair(int start, int limit) : start(start), limit(limit) {}
-
-    size_t length() const {
-        JS_ASSERT(!isUndefined());
-        return limit - start;
-    }
-
-    bool isUndefined() const {
-        return start == -1;
-    }
-
-    void check() const {
-        JS_ASSERT(limit >= start);
-        JS_ASSERT_IF(!isUndefined(), start >= 0);
-    }
-};
-
-class MatchPairs
-{
-    size_t  pairCount_;
-    int     buffer_[1];
-
-    explicit MatchPairs(size_t pairCount) : pairCount_(pairCount) {
-        initPairValues();
-    }
-
-    void initPairValues() {
-        for (int *it = buffer_; it < buffer_ + 2 * pairCount_; ++it)
-            *it = -1;
-    }
-
-    static size_t calculateSize(size_t backingPairCount) {
-        return sizeof(MatchPairs) - sizeof(int) + sizeof(int) * backingPairCount;
-    }
-
-    int *buffer() { return buffer_; }
-
-    friend class RegExpPrivate;
-
-  public:
-    /*
-     * |backingPairCount| is necessary because PCRE uses extra space
-     * after the actual results in the buffer.
-     */
-    static MatchPairs *create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount);
-
-    size_t pairCount() const { return pairCount_; }
-
-    MatchPair pair(size_t i) {
-        JS_ASSERT(i < pairCount());
-        return MatchPair(buffer_[2 * i], buffer_[2 * i + 1]);
-    }
-
-    void displace(size_t amount) {
-        if (!amount)
-            return;
-
-        for (int *it = buffer_; it < buffer_ + 2 * pairCount_; ++it)
-            *it = *it < 0 ? -1 : *it + amount;
-    }
-
-    inline void checkAgainst(size_t length);
-};
-
-} /* namespace js */
-
-#endif
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -49,21 +49,59 @@
 #include "jsobjinlines.h"
 #include "jsstrinlines.h"
 #include "RegExpStatics-inl.h"
 
 inline js::RegExpObject *
 JSObject::asRegExp()
 {
     JS_ASSERT(isRegExp());
-    return static_cast<js::RegExpObject *>(this);
+    js::RegExpObject *reobj = static_cast<js::RegExpObject *>(this);
+    JS_ASSERT(reobj->getPrivate());
+    return reobj;
 }
 
 namespace js {
 
+/*
+ * Maintains the post-initialization invariant of having a RegExpPrivate.
+ *
+ * N.B. If initialization fails, the |RegExpPrivate| will be null, so
+ * finalization must consider that as a possibility.
+ */
+class PreInitRegExpObject
+{
+    RegExpObject    *reobj;
+    DebugOnly<bool> gotResult;
+
+  public:
+    explicit PreInitRegExpObject(JSObject *obj) {
+        JS_ASSERT(obj->isRegExp());
+        reobj = static_cast<RegExpObject *>(obj);
+        gotResult = false;
+    }
+
+    ~PreInitRegExpObject() {
+        JS_ASSERT(gotResult);
+    }
+
+    RegExpObject *get() { return reobj; }
+
+    void succeed() {
+        JS_ASSERT(!gotResult);
+        JS_ASSERT(reobj->getPrivate());
+        gotResult = true;
+    }
+
+    void fail() {
+        JS_ASSERT(!gotResult);
+        gotResult = true;
+    }
+};
+
 inline bool
 ValueIsRegExp(const Value &v)
 {
     return !v.isPrimitive() && v.toObject().isRegExp();
 }
 
 inline bool
 IsRegExpMetaChar(jschar c)
@@ -84,80 +122,78 @@ HasRegExpMetaChars(const jschar *chars, 
 {
     for (size_t i = 0; i < length; ++i) {
         if (IsRegExpMetaChar(chars[i]))
             return true;
     }
     return false;
 }
 
+inline bool
+ResetRegExpObject(JSContext *cx, RegExpObject *reobj, JSString *str, RegExpFlag flags)
+{
+    AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, str, flags, NULL);
+    if (!rep)
+        return false;
+
+    return reobj->reset(cx, rep);
+}
+
+inline bool
+ResetRegExpObject(JSContext *cx, RegExpObject *reobj, AlreadyIncRefed<RegExpPrivate> rep)
+{
+    return reobj->reset(cx, rep);
+}
+
 inline RegExpObject *
 RegExpObject::create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
-                     RegExpFlag flags, TokenStream *tokenStream)
+                     RegExpFlag flags, TokenStream *ts)
 {
     RegExpFlag staticsFlags = res->getFlags();
-    return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), tokenStream);
+    return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), ts);
 }
 
 inline RegExpObject *
 RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length,
-                              RegExpFlag flags, TokenStream *tokenStream)
+                              RegExpFlag flags, TokenStream *ts)
 {
-    JSLinearString *source = js_NewStringCopyN(cx, chars, length);
-    if (!source)
+    JSString *str = js_NewStringCopyN(cx, chars, length);
+    if (!str)
         return NULL;
 
-    /* |NewBuiltinClassInstance| can GC. */
-    JS::Anchor<JSString *> anchor(source);
-
-    if (!RegExpPrivateCode::checkSyntax(cx, tokenStream, source))
+    /*
+     * |NewBuiltinClassInstance| can GC before we store |re| in the
+     * private field of the object. At that point the only reference to
+     * the source string could be from the malloc-allocated GC-invisible
+     * |re|. So we must anchor.
+     */
+    JS::Anchor<JSString *> anchor(str);
+    AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, str, flags, ts);
+    if (!rep)
         return NULL;
 
     JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
     if (!obj)
         return NULL;
 
-    RegExpObject *reobj = obj->asRegExp();
-    return reobj->reset(cx, source, flags) ? reobj : NULL;
-}
+    PreInitRegExpObject pireo(obj);
+    RegExpObject *reobj = pireo.get();
+    if (!ResetRegExpObject(cx, reobj, rep)) {
+        rep->decref(cx);
+        pireo.fail();
+        return NULL;
+    }
 
-inline void
-RegExpObject::finalize(JSContext *cx)
-{
-    if (RegExpPrivate *rep = getPrivate())
-        rep->decref(cx);
-#ifdef DEBUG
-    setPrivate((void *) 0x1); /* Non-null but still in the zero page. */
-#endif
+    pireo.succeed();
+    return reobj;
 }
 
 inline bool
 RegExpObject::reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep)
 {
-    if (!reset(cx, rep->getSource(), rep->getFlags()))
-        return false;
-
-    setPrivate(rep.get());
-    return true;
-}
-
-inline bool
-RegExpObject::reset(JSContext *cx, RegExpObject *other)
-{
-    if (RegExpPrivate *rep = other->getPrivate()) {
-        rep->incref(cx);
-        return reset(cx, AlreadyIncRefed<RegExpPrivate>(rep));
-    }
-
-    return reset(cx, other->getSource(), other->getFlags());
-}
-
-inline bool
-RegExpObject::reset(JSContext *cx, JSLinearString *source, RegExpFlag flags)
-{
     if (nativeEmpty()) {
         const js::Shape **shapep = &cx->compartment->initialRegExpShape;
         if (!*shapep) {
             *shapep = assignInitialShape(cx);
             if (!*shapep)
                 return false;
         }
         setLastProperty(*shapep);
@@ -169,30 +205,114 @@ RegExpObject::reset(JSContext *cx, JSLin
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->sourceAtom))->slot == SOURCE_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->globalAtom))->slot == GLOBAL_FLAG_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->ignoreCaseAtom))->slot ==
                                  IGNORE_CASE_FLAG_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->multilineAtom))->slot ==
                                  MULTILINE_FLAG_SLOT);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot == STICKY_FLAG_SLOT);
 
+    setPrivate(rep.get());
     zeroLastIndex();
-    setPrivate(NULL);
-    setSource(source);
-    setGlobal(flags & GlobalFlag);
-    setIgnoreCase(flags & IgnoreCaseFlag);
-    setMultiline(flags & MultilineFlag);
-    setSticky(flags & StickyFlag);
+    setSource(rep->getSource());
+    setGlobal(rep->global());
+    setIgnoreCase(rep->ignoreCase());
+    setMultiline(rep->multiline());
+    setSticky(rep->sticky());
     return true;
 }
 
 /* RegExpPrivate inlines. */
 
+class RegExpMatchBuilder
+{
+    JSContext   * const cx;
+    JSObject    * const array;
+
+    bool setProperty(JSAtom *name, Value v) {
+        return !!js_DefineProperty(cx, array, ATOM_TO_JSID(name), &v,
+                                   JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
+    }
+
+  public:
+    RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {}
+
+    bool append(uint32 index, Value v) {
+        JS_ASSERT(!array->getOps()->getElement);
+        return !!js_DefineElement(cx, array, index, &v, JS_PropertyStub, JS_StrictPropertyStub,
+                                  JSPROP_ENUMERATE);
+    }
+
+    bool setIndex(int index) {
+        return setProperty(cx->runtime->atomState.indexAtom, Int32Value(index));
+    }
+
+    bool setInput(JSString *str) {
+        JS_ASSERT(str);
+        return setProperty(cx->runtime->atomState.inputAtom, StringValue(str));
+    }
+};
+
+inline void
+RegExpPrivate::checkMatchPairs(JSString *input, int *buf, size_t matchItemCount)
+{
+#if DEBUG
+    size_t inputLength = input->length();
+    for (size_t i = 0; i < matchItemCount; i += 2) {
+        int start = buf[i];
+        int limit = buf[i + 1];
+        JS_ASSERT(limit >= start); /* Limit index must be larger than the start index. */
+        if (start == -1)
+            continue;
+        JS_ASSERT(start >= 0);
+        JS_ASSERT(size_t(limit) <= inputLength);
+    }
+#endif
+}
+
+inline JSObject *
+RegExpPrivate::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount)
+{
+    /*
+     * Create the result array for a match. Array contents:
+     *  0:              matched string
+     *  1..pairCount-1: paren matches
+     */
+    JSObject *array = NewSlowEmptyArray(cx);
+    if (!array)
+        return NULL;
+
+    RegExpMatchBuilder builder(cx, array);
+    for (size_t i = 0; i < matchItemCount; i += 2) {
+        int start = buf[i];
+        int end = buf[i + 1];
+
+        JSString *captured;
+        if (start >= 0) {
+            JS_ASSERT(start <= end);
+            JS_ASSERT(unsigned(end) <= input->length());
+            captured = js_NewDependentString(cx, input, start, end - start);
+            if (!captured || !builder.append(i / 2, StringValue(captured)))
+                return NULL;
+        } else {
+            /* Missing parenthesized match. */
+            JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
+            if (!builder.append(i / 2, UndefinedValue()))
+                return NULL;
+        }
+    }
+
+    if (!builder.setIndex(buf[0]) || !builder.setInput(input))
+        return NULL;
+
+    return array;
+}
+
 inline AlreadyIncRefed<RegExpPrivate>
-RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts)
+RegExpPrivate::create(JSContext *cx, JSString *source, RegExpFlag flags, TokenStream *ts)
 {
     typedef AlreadyIncRefed<RegExpPrivate> RetType;
 
     JSLinearString *flatSource = source->ensureLinear(cx);
     if (!flatSource)
         return RetType(NULL);
 
     RegExpPrivate *self = cx->new_<RegExpPrivate>(flatSource, flags, cx->compartment);
@@ -294,43 +414,43 @@ RegExpPrivate::compile(JSContext *cx, To
     sb.infallibleAppend(postfix, ArrayLength(postfix));
 
     JSLinearString *fakeySource = sb.finishString();
     if (!fakeySource)
         return false;
     return code.compile(cx, *fakeySource, ts, &parenCount, getFlags());
 }
 
-inline RegExpRunStatus
-RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
+inline RegExpPrivateCode::ExecuteResult
+RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t start, size_t length,
                            int *output, size_t outputCount)
 {
     int result;
 #if ENABLE_YARR_JIT
     (void) cx; /* Unused. */
     if (codeBlock.isFallBack())
         result = JSC::Yarr::interpret(byteCode, chars, start, length, output);
     else
         result = JSC::Yarr::execute(codeBlock, chars, start, length, output);
 #else
     result = jsRegExpExecute(cx, compiled, chars, length, start, output, outputCount);
 #endif
 
     if (result == -1)
-        return RegExpRunStatus_Success_NotFound;
+        return Success_NotFound;
 
 #if !ENABLE_YARR_JIT
     if (result < 0) {
         reportPCREError(cx, result);
-        return RegExpRunStatus_Error;
+        return Error;
     }
 #endif
 
     JS_ASSERT(result >= 0);
-    return RegExpRunStatus_Success;
+    return Success;
 }
 
 inline void
 RegExpPrivate::incref(JSContext *cx)
 {
 #ifdef DEBUG
     assertSameCompartment(cx, compartment);
 #endif
@@ -343,25 +463,16 @@ RegExpPrivate::decref(JSContext *cx)
 #ifdef DEBUG
     assertSameCompartment(cx, compartment);
 #endif
     if (--refCount == 0)
         cx->delete_(this);
 }
 
 inline RegExpPrivate *
-RegExpObject::getOrCreatePrivate(JSContext *cx)
-{
-    if (RegExpPrivate *rep = getPrivate())
-        return rep;
-
-    return makePrivate(cx) ? getPrivate() : NULL;
-}
-
-inline RegExpPrivate *
 RegExpObject::getPrivate() const
 {
     RegExpPrivate *rep = static_cast<RegExpPrivate *>(JSObject::getPrivate());
 #ifdef DEBUG
     if (rep)
         CompartmentChecker::check(compartment(), rep->compartment);
 #endif
     return rep;
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -35,17 +35,16 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "frontend/TokenStream.h"
 #include "vm/RegExpStatics.h"
-#include "vm/MatchPairs.h"
 
 #include "jsobjinlines.h"
 #include "jsstrinlines.h"
 
 #include "vm/RegExpObject-inl.h"
 
 #ifdef JS_TRACER
 #include "jstracer.h"
@@ -54,109 +53,100 @@ 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);
 
-MatchPairs *
-MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount)
-{
-    void *mem = alloc.alloc(calculateSize(backingPairCount));
-    if (!mem)
-        return NULL;
-
-    return new (mem) MatchPairs(pairCount);
-}
-
-inline void
-MatchPairs::checkAgainst(size_t inputLength)
+bool
+RegExpPrivate::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inputstr,
+                               size_t *lastIndex, bool test, Value *rval)
 {
-#if DEBUG
-    for (size_t i = 0; i < pairCount(); ++i) {
-        MatchPair p = pair(i);
-        p.check();
-        if (p.isUndefined())
-            continue;
-        JS_ASSERT(size_t(p.limit) <= inputLength);
-    }
-#endif
-}
+    const size_t pairCount = parenCount + 1;
+    const size_t matchItemCount = pairCount * 2;
+    const size_t bufCount = RegExpPrivateCode::getOutputSize(pairCount);
 
-RegExpRunStatus
-RegExpPrivate::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
-                       LifoAllocScope &allocScope, MatchPairs **output)
-{
-    const size_t origLength = length;
-    size_t backingPairCount = RegExpPrivateCode::getOutputSize(pairCount());
-
-    MatchPairs *matchPairs = MatchPairs::create(allocScope.alloc(), pairCount(), backingPairCount);
-    if (!matchPairs)
-        return RegExpRunStatus_Error;
+    LifoAllocScope las(&cx->tempLifoAlloc());
+    int *buf = cx->tempLifoAlloc().newArray<int>(bufCount);
+    if (!buf)
+        return false;
 
     /*
-     * |displacement| emulates sticky mode by matching from this offset
-     * into the char buffer and subtracting the delta off at the end.
+     * The JIT regexp procedure doesn't always initialize matchPair values.
+     * Maybe we can make this faster by ensuring it does?
      */
-    size_t start = *lastIndex;
-    size_t displacement = 0;
+    for (int *it = buf; it != buf + matchItemCount; ++it)
+        *it = -1;
+    
+    JSLinearString *input = inputstr->ensureLinear(cx);
+    if (!input)
+        return false;
+
+    JS::Anchor<JSString *> anchor(input);
+    size_t len = input->length();
+    const jschar *chars = input->chars();
+
+    /* 
+     * inputOffset emulates sticky mode by matching from this offset into the char buf and
+     * subtracting the delta off at the end.
+     */
+    size_t inputOffset = 0;
 
     if (sticky()) {
-        displacement = *lastIndex;
-        chars += displacement;
-        length -= displacement;
-        start = 0;
+        /* Sticky matches at the last index for the regexp object. */
+        chars += *lastIndex;
+        len -= *lastIndex;
+        inputOffset = *lastIndex;
     }
 
-    RegExpRunStatus status = code.execute(cx, chars, length, start,
-                                          matchPairs->buffer(), backingPairCount);
+    size_t start = *lastIndex - inputOffset;
+    RegExpPrivateCode::ExecuteResult result = code.execute(cx, chars, start, len, buf, bufCount);
 
-    switch (status) {
-      case RegExpRunStatus_Error:
-        return status;
-      case RegExpRunStatus_Success_NotFound:
-        *output = matchPairs;
-        return status;
+    switch (result) {
+      case RegExpPrivateCode::Error:
+        return false;
+      case RegExpPrivateCode::Success_NotFound:
+        *rval = NullValue();
+        return true;
       default:
-        JS_ASSERT(status == RegExpRunStatus_Success);
+        JS_ASSERT(result == RegExpPrivateCode::Success);
     }
 
-    matchPairs->displace(displacement);
-    matchPairs->checkAgainst(origLength);
+    /* 
+     * Adjust buf for the inputOffset. Use of sticky is rare and the matchItemCount is small, so
+     * just do another pass.
+     */
+    if (JS_UNLIKELY(inputOffset)) {
+        for (size_t i = 0; i < matchItemCount; ++i)
+            buf[i] = buf[i] < 0 ? -1 : buf[i] + inputOffset;
+    }
 
-    *lastIndex = matchPairs->pair(0).limit;
-    *output = matchPairs;
+    /* Make sure the populated contents of |buf| are sane values against |input|. */
+    checkMatchPairs(input, buf, matchItemCount);
 
-    return RegExpRunStatus_Success;
-}
+    if (res)
+        res->updateFromMatch(cx, input, buf, matchItemCount);
+
+    *lastIndex = buf[1];
 
-bool
-RegExpObject::makePrivate(JSContext *cx)
-{
-    JS_ASSERT(!getPrivate());
-    AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, getSource(), getFlags(), NULL);
-    if (!rep)
+    if (test) {
+        *rval = BooleanValue(true);
+        return true;
+    }
+
+    JSObject *array = createResult(cx, input, buf, matchItemCount);
+    if (!array)
         return false;
 
-    setPrivate(rep.get());
+    *rval = ObjectValue(*array);
     return true;
 }
 
-RegExpRunStatus
-RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
-                      LifoAllocScope &allocScope, MatchPairs **output)
-{
-    if (!getPrivate() && !makePrivate(cx))
-        return RegExpRunStatus_Error;
-
-    return getPrivate()->execute(cx, chars, length, lastIndex, allocScope, output);
-}
-
 const Shape *
 RegExpObject::assignInitialShape(JSContext *cx)
 {
     JS_ASSERT(!cx->compartment->initialRegExpShape);
     JS_ASSERT(isRegExp());
     JS_ASSERT(nativeEmpty());
 
     JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
@@ -230,17 +220,18 @@ js_XDRRegExpObject(JSXDRState *xdr, JSOb
 
 #define js_XDRRegExpObject NULL
 
 #endif /* !JS_HAS_XDR */
 
 static void
 regexp_finalize(JSContext *cx, JSObject *obj)
 {
-    obj->asRegExp()->finalize(cx);
+    if (RegExpPrivate *rep = static_cast<RegExpObject *>(obj)->getPrivate())
+        rep->decref(cx);
 }
 
 Class js::RegExpClass = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     JS_PropertyStub,         /* addProperty */
@@ -297,19 +288,19 @@ RegExpPrivateCode::reportYarrError(JSCon
 void
 RegExpPrivateCode::reportPCREError(JSContext *cx, int error)
 {
 #define REPORT(msg_) \
     JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, msg_); \
     return
     switch (error) {
       case -2: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
-      case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred.");
+      case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred."); 
       case 1: REPORT(JSMSG_TRAILING_SLASH);
-      case 2: REPORT(JSMSG_TRAILING_SLASH);
+      case 2: REPORT(JSMSG_TRAILING_SLASH); 
       case 3: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
       case 4: REPORT(JSMSG_BAD_QUANTIFIER);
       case 5: REPORT(JSMSG_BAD_QUANTIFIER);
       case 6: REPORT(JSMSG_BAD_CLASS_RANGE);
       case 7: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
       case 8: REPORT(JSMSG_BAD_CLASS_RANGE);
       case 9: REPORT(JSMSG_BAD_QUANTIFIER);
       case 10: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN);
@@ -320,17 +311,16 @@ RegExpPrivateCode::reportPCREError(JSCon
       case 15: REPORT(JSMSG_BAD_BACKREF);
       case 16: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
       case 17: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
       default:
         JS_NOT_REACHED("Precondition violation: unknown PCRE error code.");
     }
 #undef REPORT
 }
-
 #endif /* ENABLE_YARR_JIT */
 
 bool
 js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut)
 {
     size_t n = flagStr->length();
     const jschar *s = flagStr->getChars(cx);
     if (!s)
@@ -361,68 +351,74 @@ js::ParseRegExpFlags(JSContext *cx, JSSt
           }
         }
 #undef HANDLE_FLAG
     }
     return true;
 }
 
 AlreadyIncRefed<RegExpPrivate>
-RegExpPrivate::create(JSContext *cx, JSLinearString *str, JSString *opt, TokenStream *ts)
+RegExpPrivate::createFlagged(JSContext *cx, JSString *str, JSString *opt, TokenStream *ts)
 {
     if (!opt)
         return create(cx, str, RegExpFlag(0), ts);
 
     RegExpFlag flags = RegExpFlag(0);
     if (!ParseRegExpFlags(cx, opt, &flags))
         return AlreadyIncRefed<RegExpPrivate>(NULL);
 
     return create(cx, str, flags, ts);
 }
 
 RegExpObject *
-RegExpObject::clone(JSContext *cx, RegExpObject *reobj, RegExpObject *proto)
+RegExpObject::clone(JSContext *cx, RegExpObject *obj, 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);
+    assertSameCompartment(cx, obj, clone);
 
     RegExpStatics *res = cx->regExpStatics();
-    RegExpObject *reclone = clone->asRegExp();
 
-    /*
+    /* 
      * Check that the RegExpPrivate for the original is okay to use in
-     * the clone -- if the |RegExpStatics| provides more flags we'll
-     * need a different |RegExpPrivate|.
+     * the clone -- if the RegExpStatics provides more flags we'll need
+     * a different RegExpPrivate.
      */
-    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;
+    AlreadyIncRefed<RegExpPrivate> rep(NULL);
+    {
+        RegExpFlag origFlags = obj->getFlags();
+        RegExpFlag staticsFlags = res->getFlags();
+        if ((origFlags & staticsFlags) != staticsFlags) {
+            RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
+            rep = RegExpPrivate::create(cx, obj->getSource(), newFlags, NULL);
+            if (!rep)
+                return NULL;
+        } else {
+            RegExpPrivate *toShare = obj->getPrivate();
+            toShare->incref(cx);
+            rep = AlreadyIncRefed<RegExpPrivate>(toShare);
+        }
     }
 
-    RegExpPrivate *toShare = reobj->getPrivate();
-    if (toShare) {
-        toShare->incref(cx);
-        if (!reclone->reset(cx, AlreadyIncRefed<RegExpPrivate>(toShare))) {
-            toShare->decref(cx);
-            return NULL;
-        }
-    } else {
-        if (!reclone->reset(cx, reobj))
-            return NULL;
+    JS_ASSERT(rep);
+
+    PreInitRegExpObject pireo(clone);
+    RegExpObject *reclone = pireo.get();
+    if (!ResetRegExpObject(cx, reclone, rep)) {
+        pireo.fail();
+        return NULL;
     }
 
+    pireo.succeed();
     return reclone;
 }
 
 JSObject * JS_FASTCALL
 js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
 {
     JS_ASSERT(obj->isRegExp());
     JS_ASSERT(proto->isRegExp());
@@ -433,31 +429,35 @@ js_CloneRegExpObject(JSContext *cx, JSOb
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_CloneRegExpObject, CONTEXT, OBJECT, OBJECT, 0,
                      ACCSET_STORE_ANY)
 #endif
 
 JSFlatString *
 RegExpObject::toString(JSContext *cx) const
 {
-    JSLinearString *src = getSource();
+    RegExpPrivate *rep = getPrivate();
+    if (!rep)
+        return cx->runtime->emptyString;
+
+    JSLinearString *src = rep->getSource();
     StringBuffer sb(cx);
     if (size_t len = src->length()) {
         if (!sb.reserve(len + 2))
             return NULL;
         sb.infallibleAppend('/');
         sb.infallibleAppend(src->chars(), len);
         sb.infallibleAppend('/');
     } else {
         if (!sb.append("/(?:)/"))
             return NULL;
     }
-    if (global() && !sb.append('g'))
+    if (rep->global() && !sb.append('g'))
         return NULL;
-    if (ignoreCase() && !sb.append('i'))
+    if (rep->ignoreCase() && !sb.append('i'))
         return NULL;
-    if (multiline() && !sb.append('m'))
+    if (rep->multiline() && !sb.append('m'))
         return NULL;
-    if (sticky() && !sb.append('y'))
+    if (rep->sticky() && !sb.append('y'))
         return NULL;
 
     return sb.finishString();
 }
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -44,29 +44,23 @@
 #include <stddef.h>
 #include "jsobj.h"
 
 #include "js/TemplateLib.h"
 
 #include "yarr/Yarr.h"
 #if ENABLE_YARR_JIT
 #include "yarr/YarrJIT.h"
-#include "yarr/YarrSyntaxChecker.h"
 #else
 #include "yarr/pcre/pcre.h"
 #endif
 
 namespace js {
 
-enum RegExpRunStatus
-{
-    RegExpRunStatus_Error,
-    RegExpRunStatus_Success,
-    RegExpRunStatus_Success_NotFound
-};
+class RegExpPrivate;
 
 class RegExpObject : public ::JSObject
 {
     static const uintN LAST_INDEX_SLOT          = 0;
     static const uintN SOURCE_SLOT              = 1;
     static const uintN GLOBAL_FLAG_SLOT         = 2;
     static const uintN IGNORE_CASE_FLAG_SLOT    = 3;
     static const uintN MULTILINE_FLAG_SLOT      = 4;
@@ -88,107 +82,79 @@ class RegExpObject : public ::JSObject
     createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
                     TokenStream *ts);
 
     static RegExpObject *clone(JSContext *cx, RegExpObject *obj, RegExpObject *proto);
 
     /* Note: fallible. */
     JSFlatString *toString(JSContext *cx) const;
 
-    /*
-     * Run the regular expression over the input text.
-     *
-     * Results are placed in |output| as integer pairs. For eaxmple,
-     * |output[0]| and |output[1]| represent the text indices that make
-     * up the "0" (whole match) pair. Capturing parens will result in
-     * more output.
-     *
-     * N.B. it's the responsibility of the caller to hook the |output|
-     * into the |RegExpStatics| appropriately, if necessary.
-     */
-    RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
-                            LifoAllocScope &allocScope, MatchPairs **output);
-
     /* Accessors. */
 
     const Value &getLastIndex() const {
         return getSlot(LAST_INDEX_SLOT);
     }
     void setLastIndex(const Value &v) {
         setSlot(LAST_INDEX_SLOT, v);
     }
     void setLastIndex(double d) {
         setSlot(LAST_INDEX_SLOT, NumberValue(d));
     }
     void zeroLastIndex() {
         setSlot(LAST_INDEX_SLOT, Int32Value(0));
     }
 
-    JSLinearString *getSource() const {
-        return &getSlot(SOURCE_SLOT).toString()->asLinear();
+    JSString *getSource() const {
+        return getSlot(SOURCE_SLOT).toString();
     }
-    void setSource(JSLinearString *source) {
+    void setSource(JSString *source) {
         setSlot(SOURCE_SLOT, StringValue(source));
     }
     RegExpFlag getFlags() const {
         uintN flags = 0;
-        flags |= global() ? GlobalFlag : 0;
-        flags |= ignoreCase() ? IgnoreCaseFlag : 0;
-        flags |= multiline() ? MultilineFlag : 0;
-        flags |= sticky() ? StickyFlag : 0;
+        flags |= getSlot(GLOBAL_FLAG_SLOT).toBoolean() ? GlobalFlag : 0;
+        flags |= getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean() ? IgnoreCaseFlag : 0;
+        flags |= getSlot(MULTILINE_FLAG_SLOT).toBoolean() ? MultilineFlag : 0;
+        flags |= getSlot(STICKY_FLAG_SLOT).toBoolean() ? StickyFlag : 0;
         return RegExpFlag(flags);
     }
+    inline RegExpPrivate *getPrivate() const;
 
     /* Flags. */
 
     void setIgnoreCase(bool enabled)    { setSlot(IGNORE_CASE_FLAG_SLOT, BooleanValue(enabled)); }
     void setGlobal(bool enabled)        { setSlot(GLOBAL_FLAG_SLOT, BooleanValue(enabled)); }
     void setMultiline(bool enabled)     { setSlot(MULTILINE_FLAG_SLOT, BooleanValue(enabled)); }
     void setSticky(bool enabled)        { setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled)); }
-    bool ignoreCase() const { return getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
-    bool global() const     { return getSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
-    bool multiline() const  { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
-    bool sticky() const     { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
-
-    /*
-     * N.B. |RegExpObject|s can be mutated in place because of |RegExp.prototype.compile|, hence
-     * |reset| for re-initialization.
-     */
-
-    inline bool reset(JSContext *cx, RegExpObject *other);
-    inline bool reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
-    inline bool reset(JSContext *cx, JSLinearString *source, RegExpFlag flags);
-
-    inline RegExpPrivate *getOrCreatePrivate(JSContext *cx);
-    inline void finalize(JSContext *cx);
 
   private:
-    /* The |RegExpPrivate| is lazily created at the time of use. */
-    inline RegExpPrivate *getPrivate() const;
+    /* N.B. |RegExpObject|s can be mutated in place because of |RegExp.prototype.compile|. */
+    inline bool reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
 
-    /*
-     * Precondition: the syntax for |source| has already been validated.
-     * Side effect: sets the private field.
-     */
-    bool makePrivate(JSContext *cx);
-
-    friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSLinearString *, RegExpFlag);
+    friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSString *, RegExpFlag);
     friend bool ResetRegExpObject(JSContext *, RegExpObject *, AlreadyIncRefed<RegExpPrivate>);
 
     /*
      * Compute the initial shape to associate with fresh RegExp objects,
      * encoding their initial properties. Return the shape after
      * changing this regular expression object's last property to it.
      */
     const Shape *assignInitialShape(JSContext *cx);
 
     RegExpObject();
     RegExpObject &operator=(const RegExpObject &reo);
 }; /* class RegExpObject */
 
+inline bool
+ResetRegExpObject(JSContext *cx, RegExpObject *reobj, JSString *str, RegExpFlag flags);
+
+/* N.B. On failure, caller must decref |rep|. */
+inline bool
+ResetRegExpObject(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
+
 /* Abstracts away the gross |RegExpPrivate| backend details. */
 class RegExpPrivateCode
 {
 #if ENABLE_YARR_JIT
     typedef JSC::Yarr::BytecodePattern BytecodePattern;
     typedef JSC::Yarr::ErrorCode ErrorCode;
     typedef JSC::Yarr::JSGlobalData JSGlobalData;
     typedef JSC::Yarr::YarrCodeBlock YarrCodeBlock;
@@ -218,41 +184,30 @@ class RegExpPrivateCode
         if (byteCode)
             Foreground::delete_<BytecodePattern>(byteCode);
 #else
         if (compiled)
             jsRegExpFree(compiled);
 #endif
     }
 
-    static bool checkSyntax(JSContext *cx, TokenStream *tokenStream, JSLinearString *source) {
-#if ENABLE_YARR_JIT
-        ErrorCode error = JSC::Yarr::checkSyntax(*source);
-        if (error == JSC::Yarr::NoError)
-            return true;
-
-        reportYarrError(cx, tokenStream, error);
-        return false;
-#else
-# error "Syntax checking not implemented for !ENABLE_YARR_JIT"
-#endif
-    }
-
 #if ENABLE_YARR_JIT
     static inline bool isJITRuntimeEnabled(JSContext *cx);
-    static void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error);
+    void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error);
 #else
-    static void reportPCREError(JSContext *cx, int error);
+    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);
+    enum ExecuteResult { Error, Success, Success_NotFound };
+
+    inline ExecuteResult execute(JSContext *cx, const jschar *chars, size_t start, size_t length,
+                                 int *output, size_t outputCount);
 
     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
     }
@@ -287,49 +242,66 @@ class RegExpPrivate
     RegExpPrivate(JSLinearString *source, RegExpFlag flags, JSCompartment *compartment)
       : source(source), refCount(1), parenCount(0), flags(flags), compartment(compartment)
     { }
 
     JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
 
     bool compile(JSContext *cx, TokenStream *ts);
     static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
+    static JSObject *createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount);
+    bool executeInternal(JSContext *cx, RegExpStatics *res, JSString *input,
+                         size_t *lastIndex, bool test, Value *rval);
 
   public:
-    static AlreadyIncRefed<RegExpPrivate>
-    create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts);
+    /*
+     * Execute regexp on |input| at |*lastIndex|.
+     *
+     * On match:    Update |*lastIndex| and RegExp class statics.
+     *              Return true if test is true. Place an array in |*rval| if test is false.
+     * On mismatch: Make |*rval| null.
+     */
+    bool execute(JSContext *cx, RegExpStatics *res, JSString *input, size_t *lastIndex, bool test,
+                 Value *rval) {
+        JS_ASSERT(res);
+        return executeInternal(cx, res, input, lastIndex, test, rval);
+    }
 
-    static AlreadyIncRefed<RegExpPrivate>
-    create(JSContext *cx, JSLinearString *source, JSString *flags, TokenStream *ts);
+    bool executeNoStatics(JSContext *cx, JSString *input, size_t *lastIndex, bool test,
+                          Value *rval) {
+        return executeInternal(cx, NULL, input, lastIndex, test, rval);
+    }
+
+    /* Factories */
 
-    RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
-                            LifoAllocScope &allocScope, MatchPairs **output);
+    static AlreadyIncRefed<RegExpPrivate> create(JSContext *cx, JSString *source, RegExpFlag flags,
+                                                 TokenStream *ts);
+
+    /* Would overload |create|, but |0| resolves ambiguously against pointer and uint. */
+    static AlreadyIncRefed<RegExpPrivate> createFlagged(JSContext *cx, JSString *source,
+                                                        JSString *flags, TokenStream *ts);
 
     /* Mutators */
 
     void incref(JSContext *cx);
     void decref(JSContext *cx);
 
     /* For JIT access. */
     size_t *addressOfRefCount() { return &refCount; }
 
     /* Accessors */
 
-    JSLinearString *getSource() const   { return source; }
-    size_t getParenCount() const        { return parenCount; }
-
-    /* Accounts for the "0" (whole match) pair. */
-    size_t pairCount() const            { return parenCount + 1; }
-
-    RegExpFlag getFlags() const         { return flags; }
+    JSLinearString *getSource() const { return source; }
+    size_t getParenCount() const { return parenCount; }
+    RegExpFlag getFlags() const { return flags; }
     bool ignoreCase() const { return flags & IgnoreCaseFlag; }
     bool global() const     { return flags & GlobalFlag; }
     bool multiline() const  { return flags & MultilineFlag; }
     bool sticky() const     { return flags & StickyFlag; }
-};
+}; /* class RegExpPrivate */
 
 /*
  * Parse regexp flags. Report an error and return false if an invalid
  * sequence of flags is encountered (repeat/invalid flag).
  *
  * N.B. flagStr must be rooted.
  */
 bool
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -40,24 +40,22 @@
 
 #ifndef RegExpStatics_h__
 #define RegExpStatics_h__
 
 #include "jscntxt.h"
 
 #include "js/Vector.h"
 
-#include "vm/MatchPairs.h"
-
 namespace js {
 
 class RegExpStatics
 {
-    typedef Vector<int, 20, SystemAllocPolicy> Pairs;
-    Pairs           matchPairs;
+    typedef Vector<int, 20, SystemAllocPolicy> MatchPairs;
+    MatchPairs      matchPairs;
     /* The input that was used to produce matchPairs. */
     JSLinearString  *matchPairsInput;
     /* The input last set on the statics. */
     JSString        *pendingInput;
     RegExpFlag      flags;
     RegExpStatics   *bufferLink;
     bool            copied;
 
@@ -159,30 +157,27 @@ class RegExpStatics
 
   public:
     RegExpStatics() : bufferLink(NULL), copied(false) { clear(); }
 
     static JSObject *create(JSContext *cx, GlobalObject *parent);
 
     /* Mutators. */
 
-    bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs *newPairs) {
-        JS_ASSERT(input);
+    bool updateFromMatch(JSContext *cx, JSLinearString *input, int *buf, size_t matchItemCount) {
         aboutToWrite();
         pendingInput = input;
 
-        if (!matchPairs.resizeUninitialized(2 * newPairs->pairCount())) {
+        if (!matchPairs.resizeUninitialized(matchItemCount)) {
             js_ReportOutOfMemory(cx);
             return false;
         }
 
-        for (size_t i = 0; i < newPairs->pairCount(); ++i) {
-            matchPairs[2 * i] = newPairs->pair(i).start;
-            matchPairs[2 * i + 1] = newPairs->pair(i).limit;
-        }
+        for (size_t i = 0; i < matchItemCount; ++i)
+            matchPairs[i] = buf[i];
 
         matchPairsInput = input;
         return true;
     }
 
     inline void setMultiline(JSContext *cx, bool enabled);
 
     void clear() {