Bug 808245, Part 3/6 - Merge RegExpCode into RegExpShared. r=dvander
authorSean Stangl <sstangl@mozilla.com>
Wed, 12 Dec 2012 16:54:05 -0800
changeset 125218 ea7d93401f966c3cb68fe07d8b989514747f3a1a
parent 125217 787527f064da7720dab2b28002e887462ee56bd6
child 125219 d229008d60acd336e99efc1b44d3f774634fa939
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs808245
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 808245, Part 3/6 - Merge RegExpCode into RegExpShared. r=dvander
js/src/builtin/RegExp.cpp
js/src/jsprvtd.h
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
@@ -279,17 +279,17 @@ CompileRegExpObject(JSContext *cx, RegEx
         if (!ParseRegExpFlags(cx, flagStr, &flags))
             return false;
     }
 
     RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source));
     if (!escapedSourceStr)
         return false;
 
-    if (!js::detail::RegExpCode::checkSyntax(cx, NULL, escapedSourceStr))
+    if (!js::RegExpShared::checkSyntax(cx, NULL, escapedSourceStr))
         return false;
 
     RegExpStatics *res = cx->regExpStatics();
     RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
     if (!reobj)
         return false;
 
     args.rval().setObject(*reobj);
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -89,18 +89,16 @@ struct Class;
 class RegExpGuard;
 class RegExpObject;
 class RegExpObjectBuilder;
 class RegExpShared;
 class RegExpStatics;
 class MatchPairs;
 class PropertyName;
 
-namespace detail { class RegExpCode; }
-
 enum RegExpFlag
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
     StickyFlag      = 0x08,
 
     NoFlags         = 0x00,
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -94,28 +94,30 @@ RegExpObject::setMultiline(bool enabled)
 }
 
 inline void
 RegExpObject::setSticky(bool enabled)
 {
     setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled));
 }
 
-#if ENABLE_YARR_JIT
 /* This function should be deleted once bad Android platforms phase out. See bug 604774. */
 inline bool
-detail::RegExpCode::isJITRuntimeEnabled(JSContext *cx)
+RegExpShared::isJITRuntimeEnabled(JSContext *cx)
 {
-#if defined(ANDROID) && defined(JS_METHODJIT)
+#if ENABLE_YARR_JIT
+# if defined(ANDROID) && defined(JS_METHODJIT)
     return cx->methodJitEnabled;
+# else
+    return true;
+# endif
 #else
-    return true;
+    return false;
 #endif
 }
-#endif
 
 inline bool
 RegExpToShared(JSContext *cx, JSObject &obj, RegExpGuard *g)
 {
     JS_ASSERT(ObjectClassIs(obj, ESClass_RegExp, cx));
     if (obj.isRegExp())
         return obj.asRegExp().getShared(cx, g);
     return Proxy::regexp_toShared(cx, &obj, g);
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -12,17 +12,16 @@
 #include "vm/Xdr.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/RegExpObject-inl.h"
 #include "vm/RegExpStatics-inl.h"
 
 using namespace js;
-using js::detail::RegExpCode;
 using js::frontend::TokenStream;
 
 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 */
@@ -131,121 +130,16 @@ MatchPairs::checkAgainst(size_t inputLen
         p.check();
         if (p.isUndefined())
             continue;
         JS_ASSERT(size_t(p.limit) <= inputLength);
     }
 #endif
 }
 
-/* detail::RegExpCode */
-
-void
-RegExpCode::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error)
-{
-    switch (error) {
-      case JSC::Yarr::NoError:
-        JS_NOT_REACHED("Called reportYarrError with value for no error");
-        return;
-#define COMPILE_EMSG(__code, __msg)                                                              \
-      case JSC::Yarr::__code:                                                                    \
-        if (ts)                                                                                  \
-            ts->reportError(__msg);                                                              \
-        else                                                                                     \
-            JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
-        return
-      COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX);
-      COMPILE_EMSG(QuantifierOutOfOrder, JSMSG_BAD_QUANTIFIER);
-      COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER);
-      COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
-      COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN);
-      COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */
-      COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE);
-      COMPILE_EMSG(CharacterClassInvalidRange, JSMSG_BAD_CLASS_RANGE);
-      COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE);
-      COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
-      COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH);
-#undef COMPILE_EMSG
-      default:
-        JS_NOT_REACHED("Unknown Yarr error code");
-    }
-}
-
-bool
-RegExpCode::compile(JSContext *cx, JSLinearString &pattern, unsigned *parenCount, RegExpFlag flags)
-{
-    /* Parse the pattern. */
-    ErrorCode yarrError;
-    YarrPattern yarrPattern(pattern, bool(flags & IgnoreCaseFlag), bool(flags & MultilineFlag),
-                            &yarrError);
-    if (yarrError) {
-        reportYarrError(cx, NULL, yarrError);
-        return false;
-    }
-    *parenCount = yarrPattern.m_numSubpatterns;
-
-    /*
-     * The YARR JIT compiler attempts to compile the parsed pattern. If
-     * it cannot, it informs us via |codeBlock.isFallBack()|, in which
-     * case we have to bytecode compile it.
-     */
-
-#if ENABLE_YARR_JIT && defined(JS_METHODJIT)
-    if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) {
-        JSC::ExecutableAllocator *execAlloc = cx->runtime->getExecAlloc(cx);
-        if (!execAlloc)
-            return false;
-
-        JSGlobalData globalData(execAlloc);
-        jitCompile(yarrPattern,
-                   JSC::Yarr::Char16,
-                   &globalData,
-                   codeBlock);
-        if (!codeBlock.isFallBack())
-            return true;
-    }
-#endif
-
-    WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx);
-    if (!bumpAlloc) {
-        js_ReportOutOfMemory(cx);
-        return false;
-    }
-
-#if ENABLE_YARR_JIT
-    codeBlock.setFallBack(true);
-#endif
-    byteCode = byteCompile(yarrPattern, bumpAlloc).get();
-    return true;
-}
-
-RegExpRunStatus
-RegExpCode::execute(JSContext *cx, StableCharPtr chars, size_t length, size_t start,
-                    int *output, size_t outputCount)
-{
-    unsigned result;
-#if ENABLE_YARR_JIT
-    (void) cx; /* Unused. */
-    if (codeBlock.isFallBack()) {
-        result = JSC::Yarr::interpret(byteCode, chars.get(), length, start,
-                                      reinterpret_cast<unsigned *>(output));
-    } else {
-        result = codeBlock.execute(chars.get(), start, length, output).start;
-    }
-#else
-    result = JSC::Yarr::interpret(byteCode, chars.get(), length, start,
-                                  reinterpret_cast<unsigned *>(output));
-#endif
-
-    if (result == JSC::Yarr::offsetNoMatch)
-        return RegExpRunStatus_Success_NotFound;
-
-    return RegExpRunStatus_Success;
-}
-
 /* RegExpObject */
 
 static void
 regexp_trace(JSTracer *trc, RawObject obj)
 {
      /*
       * We have to check both conditions, since:
       *   1. During TraceRuntime, isHeapBusy() is true
@@ -270,20 +164,16 @@ Class js::RegExpClass = {
     NULL,                    /* finalize */
     NULL,                    /* checkAccess */
     NULL,                    /* call */
     NULL,                    /* construct */
     NULL,                    /* hasInstance */
     regexp_trace
 };
 
-RegExpShared::RegExpShared(JSRuntime *rt, RegExpFlag flags)
-  : parenCount(0), flags(flags), activeUseCount(0), gcNumberWhenUsed(rt->gcNumber)
-{}
-
 RegExpObject *
 RegExpObject::create(JSContext *cx, RegExpStatics *res, StableCharPtr chars, size_t length,
                      RegExpFlag flags, TokenStream *tokenStream)
 {
     RegExpFlag staticsFlags = res->getFlags();
     return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), tokenStream);
 }
 
@@ -297,17 +187,17 @@ RegExpObject::createNoStatics(JSContext 
 
     return createNoStatics(cx, source, flags, tokenStream);
 }
 
 RegExpObject *
 RegExpObject::createNoStatics(JSContext *cx, HandleAtom source, RegExpFlag flags,
                               TokenStream *tokenStream)
 {
-    if (!RegExpCode::checkSyntax(cx, tokenStream, source))
+    if (!RegExpShared::checkSyntax(cx, tokenStream, source))
         return NULL;
 
     RegExpObjectBuilder builder(cx);
     return builder.build(source, flags);
 }
 
 bool
 RegExpObject::createShared(JSContext *cx, RegExpGuard *g)
@@ -435,21 +325,80 @@ RegExpObject::toString(JSContext *cx) co
     if (sticky() && !sb.append('y'))
         return NULL;
 
     return sb.finishString();
 }
 
 /* RegExpShared */
 
+RegExpShared::RegExpShared(JSRuntime *rt, JSAtom *source, RegExpFlag flags)
+  : source(source), flags(flags), parenCount(0),
+#if ENABLE_YARR_JIT
+    codeBlock(),
+#endif
+    bytecode(NULL), activeUseCount(0), gcNumberWhenUsed(rt->gcNumber)
+{}
+
+RegExpShared::~RegExpShared()
+{
+#if ENABLE_YARR_JIT
+    codeBlock.release();
+#endif
+    if (bytecode)
+        js_delete<BytecodePattern>(bytecode);
+}
+
+void
+RegExpShared::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error)
+{
+    switch (error) {
+      case JSC::Yarr::NoError:
+        JS_NOT_REACHED("Called reportYarrError with value for no error");
+        return;
+#define COMPILE_EMSG(__code, __msg)                                                              \
+      case JSC::Yarr::__code:                                                                    \
+        if (ts)                                                                                  \
+            ts->reportError(__msg);                                                              \
+        else                                                                                     \
+            JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
+        return
+      COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX);
+      COMPILE_EMSG(QuantifierOutOfOrder, JSMSG_BAD_QUANTIFIER);
+      COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER);
+      COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
+      COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN);
+      COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */
+      COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE);
+      COMPILE_EMSG(CharacterClassInvalidRange, JSMSG_BAD_CLASS_RANGE);
+      COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE);
+      COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
+      COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH);
+#undef COMPILE_EMSG
+      default:
+        JS_NOT_REACHED("Unknown Yarr error code");
+    }
+}
+
 bool
-RegExpShared::compile(JSContext *cx, JSAtom *source)
+RegExpShared::checkSyntax(JSContext *cx, TokenStream *tokenStream, JSLinearString *source)
+{
+    ErrorCode error = JSC::Yarr::checkSyntax(*source);
+    if (error == JSC::Yarr::NoError)
+        return true;
+
+    reportYarrError(cx, tokenStream, error);
+    return false;
+}
+
+bool
+RegExpShared::compile(JSContext *cx)
 {
     if (!sticky())
-        return code.compile(cx, *source, &parenCount, getFlags());
+        return compile(cx, *source);
 
     /*
      * The sticky case we implement hackily by prepending a caret onto the front
      * and relying on |::execute| to pseudo-slice the string when it sees a sticky regexp.
      */
     static const jschar prefix[] = {'^', '(', '?', ':'};
     static const jschar postfix[] = {')'};
 
@@ -459,25 +408,76 @@ RegExpShared::compile(JSContext *cx, JSA
         return false;
     sb.infallibleAppend(prefix, ArrayLength(prefix));
     sb.infallibleAppend(source->chars(), source->length());
     sb.infallibleAppend(postfix, ArrayLength(postfix));
 
     JSAtom *fakeySource = sb.finishAtom();
     if (!fakeySource)
         return false;
-    return code.compile(cx, *fakeySource, &parenCount, getFlags());
+
+    return compile(cx, *fakeySource);
+}
+
+bool
+RegExpShared::compile(JSContext *cx, JSLinearString &pattern)
+{
+    /* Parse the pattern. */
+    ErrorCode yarrError;
+    YarrPattern yarrPattern(pattern, ignoreCase(), multiline(), &yarrError);
+    if (yarrError) {
+        reportYarrError(cx, NULL, yarrError);
+        return false;
+    }
+    this->parenCount = yarrPattern.m_numSubpatterns;
+
+#if ENABLE_YARR_JIT
+    if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) {
+        JSC::ExecutableAllocator *execAlloc = cx->runtime->getExecAlloc(cx);
+        if (!execAlloc)
+            return false;
+
+        JSGlobalData globalData(execAlloc);
+        YarrJITCompileMode compileMode = JSC::Yarr::IncludeSubpatterns;
+
+        jitCompile(yarrPattern, JSC::Yarr::Char16, &globalData, codeBlock, compileMode);
+
+        /* Unset iff the Yarr JIT compilation was successful. */
+        if (!codeBlock.isFallBack())
+            return true;
+    }
+    codeBlock.setFallBack(true);
+#endif
+
+    WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx);
+    if (!bumpAlloc) {
+        js_ReportOutOfMemory(cx);
+        return false;
+    }
+
+    bytecode = byteCompile(yarrPattern, bumpAlloc).get();
+    return true;
+}
+
+bool
+RegExpShared::compileIfNecessary(JSContext *cx)
+{
+    if (hasCode() || hasBytecode())
+        return true;
+    return compile(cx);
 }
 
 RegExpRunStatus
 RegExpShared::execute(JSContext *cx, StableCharPtr chars, size_t length, size_t *lastIndex,
                       MatchPairs **output)
 {
+    JS_ASSERT(isCompiled());
+
     const size_t origLength = length;
-    size_t backingPairCount = RegExpCode::getOutputSize(pairCount());
+    size_t backingPairCount = pairCount() * 2;
 
     LifoAlloc &alloc = cx->tempLifoAlloc();
     MatchPairs *matchPairs = MatchPairs::create(alloc, pairCount(), backingPairCount);
     if (!matchPairs)
         return RegExpRunStatus_Error;
 
     /*
      * |displacement| emulates sticky mode by matching from this offset
@@ -488,35 +488,36 @@ RegExpShared::execute(JSContext *cx, Sta
 
     if (sticky()) {
         displacement = *lastIndex;
         chars += displacement;
         length -= displacement;
         start = 0;
     }
 
-    RegExpRunStatus status = code.execute(cx, chars, length, start,
-                                          matchPairs->buffer(), backingPairCount);
+    unsigned *outputBuf = (unsigned *)matchPairs->buffer();
+    unsigned result;
 
-    switch (status) {
-      case RegExpRunStatus_Error:
-        return status;
-      case RegExpRunStatus_Success_NotFound:
-        *output = matchPairs;
-        return status;
-      default:
-        JS_ASSERT(status == RegExpRunStatus_Success);
-    }
+#if ENABLE_YARR_JIT
+    if (codeBlock.isFallBack())
+        result = JSC::Yarr::interpret(bytecode, chars.get(), length, start, outputBuf);
+    else
+        result = codeBlock.execute(chars.get(), start, length, (int *)outputBuf).start;
+#else
+    result = JSC::Yarr::interpret(bytecode, chars.get(), length, start, outputBuf);
+#endif
+
+    *output = matchPairs;
+
+    if (result == JSC::Yarr::offsetNoMatch)
+        return RegExpRunStatus_Success_NotFound;
 
     matchPairs->displace(displacement);
     matchPairs->checkAgainst(origLength);
-
     *lastIndex = matchPairs->pair(0).limit;
-    *output = matchPairs;
-
     return RegExpRunStatus_Success;
 }
 
 /* RegExpCompartment */
 
 RegExpCompartment::RegExpCompartment(JSRuntime *rt)
   : map_(rt), inUse_(rt)
 {}
@@ -564,21 +565,21 @@ RegExpCompartment::get(JSContext *cx, JS
 {
     Key key(source, flags);
     Map::AddPtr p = map_.lookupForAdd(key);
     if (p) {
         g->init(*p->value);
         return true;
     }
 
-    ScopedDeletePtr<RegExpShared> shared(cx->new_<RegExpShared>(cx->runtime, flags));
+    ScopedDeletePtr<RegExpShared> shared(cx->new_<RegExpShared>(cx->runtime, source, flags));
     if (!shared)
         return false;
 
-    if (!shared->compile(cx, source))
+    if (!shared->compile(cx))
         return false;
 
     /* Re-lookup in case there was a GC. */
     if (!map_.relookupOrAdd(p, key, shared)) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -26,18 +26,16 @@
  * JavaScript Regular Expressions
  *
  * There are several engine concepts associated with a single logical regexp:
  *
  *   RegExpObject - The JS-visible object whose .[[Class]] equals "RegExp"
  *
  *   RegExpShared - The compiled representation of the regexp.
  *
- *   RegExpCode - The low-level implementation jit details.
- *
  *   RegExpCompartment - Owns all RegExpShared instances in a compartment.
  *
  * To save memory, a RegExpShared is not created for a RegExpObject until it is
  * needed for execution. When a RegExpShared needs to be created, it is looked
  * up in a per-compartment table to allow reuse between objects. Lastly, on
  * GC, every RegExpShared (that is not active on the callstack) is discarded.
  * Because of the last point, any code using a RegExpShared (viz., by executing
  * a regexp) must indicate the RegExpShared is active via RegExpGuard.
@@ -69,80 +67,16 @@ class RegExpObjectBuilder
 
     /* Perform a VM-internal clone. */
     RegExpObject *clone(Handle<RegExpObject*> other, Handle<RegExpObject*> proto);
 };
 
 JSObject *
 CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto);
 
-namespace detail {
-
-class RegExpCode
-{
-    typedef JSC::Yarr::BytecodePattern BytecodePattern;
-    typedef JSC::Yarr::ErrorCode ErrorCode;
-    typedef JSC::Yarr::YarrPattern YarrPattern;
-#if ENABLE_YARR_JIT
-    typedef JSC::Yarr::JSGlobalData JSGlobalData;
-    typedef JSC::Yarr::YarrCodeBlock YarrCodeBlock;
-
-    /* Note: Native code is valid only if |codeBlock.isFallBack() == false|. */
-    YarrCodeBlock   codeBlock;
-#endif
-    BytecodePattern *byteCode;
-
-  public:
-    RegExpCode()
-      :
-#if ENABLE_YARR_JIT
-        codeBlock(),
-#endif
-        byteCode(NULL)
-    { }
-
-    ~RegExpCode() {
-#if ENABLE_YARR_JIT
-        codeBlock.release();
-#endif
-        if (byteCode)
-            js_delete<BytecodePattern>(byteCode);
-    }
-
-    static bool checkSyntax(JSContext *cx, frontend::TokenStream *tokenStream,
-                            JSLinearString *source)
-    {
-        ErrorCode error = JSC::Yarr::checkSyntax(*source);
-        if (error == JSC::Yarr::NoError)
-            return true;
-
-        reportYarrError(cx, tokenStream, error);
-        return false;
-    }
-
-#if ENABLE_YARR_JIT
-    static inline bool isJITRuntimeEnabled(JSContext *cx);
-#endif
-    static void reportYarrError(JSContext *cx, frontend::TokenStream *ts,
-                                JSC::Yarr::ErrorCode error);
-
-    static size_t getOutputSize(size_t pairCount) {
-        return pairCount * 2;
-    }
-
-    bool compile(JSContext *cx, JSLinearString &pattern, unsigned *parenCount, RegExpFlag flags);
-
-
-    RegExpRunStatus
-    execute(JSContext *cx, StableCharPtr chars, size_t length, size_t start,
-            int *output, size_t outputCount);
-};
-
-}  /* namespace detail */
-
 /*
  * A RegExpShared is the compiled representation of a regexp. A RegExpShared is
  * potentially pointed to by multiple RegExpObjects. Additionally, C++ code may
  * have pointers to RegExpShareds on the stack. The RegExpShareds are kept in a
  * cache so that they can be reused when compiling the same regex string.
  *
  * During a GC, the trace hook for RegExpObject clears any pointers to
  * RegExpShareds so that there will be no dangling pointers when they are
@@ -168,50 +102,89 @@ class RegExpCode
  * separate tables since map_ *must* be fully cleared on each GC since the Key
  * points to a JSAtom that can become garbage.
  */
 class RegExpShared
 {
     friend class RegExpCompartment;
     friend class RegExpGuard;
 
-    detail::RegExpCode code;
-    unsigned           parenCount;
+    typedef frontend::TokenStream TokenStream;
+    typedef JSC::Yarr::BytecodePattern BytecodePattern;
+    typedef JSC::Yarr::ErrorCode ErrorCode;
+    typedef JSC::Yarr::YarrPattern YarrPattern;
+#if ENABLE_YARR_JIT
+    typedef JSC::Yarr::JSGlobalData JSGlobalData;
+    typedef JSC::Yarr::YarrCodeBlock YarrCodeBlock;
+    typedef JSC::Yarr::YarrJITCompileMode YarrJITCompileMode;
+#endif
+
+    /*
+     * Source to the RegExp. Safe to hold: if the RegExpShared is active,
+     * then at least one RegExpObject must be referencing the RegExpShared,
+     * and the RegExpObject keeps alive the source JSAtom.
+     */
+    JSAtom *           source;
     RegExpFlag         flags;
-    size_t             activeUseCount;   /* See comment above. */
-    uint64_t           gcNumberWhenUsed; /* See comment above. */
+    unsigned           parenCount;
+
+#if ENABLE_YARR_JIT
+    /* Note: Native code is valid only if |codeBlock.isFallBack() == false|. */
+    YarrCodeBlock   codeBlock;
+#endif
+    BytecodePattern *bytecode;
 
-    bool compile(JSContext *cx, JSAtom *source);
+    /* Lifetime-preserving variables: see class-level comment above. */
+    size_t             activeUseCount;
+    uint64_t           gcNumberWhenUsed;
+
+    /* Internal functions. */
+    bool compile(JSContext *cx);
+    bool compile(JSContext *cx, JSLinearString &pattern);
+
+    bool compileIfNecessary(JSContext *cx);
 
   public:
-    RegExpShared(JSRuntime *rt, RegExpFlag flags);
+    RegExpShared(JSRuntime *rt, JSAtom *source, RegExpFlag flags);
+    ~RegExpShared();
+
+    /* Static functions to expose some Yarr logic. */
+    static inline bool isJITRuntimeEnabled(JSContext *cx);
+    static void reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error);
+    static bool checkSyntax(JSContext *cx, TokenStream *tokenStream, JSLinearString *source);
 
     /* Called when a RegExpShared is installed into a RegExpObject. */
     inline void prepareForUse(JSContext *cx);
 
     /* Primary interface: run this regular expression on the given string. */
-
-    RegExpRunStatus
-    execute(JSContext *cx, StableCharPtr chars, size_t length, size_t *lastIndex,
-            MatchPairs **output);
+    RegExpRunStatus execute(JSContext *cx, StableCharPtr chars, size_t length,
+                            size_t *lastIndex, MatchPairs **matches);
 
     /* Accessors */
 
-    size_t getParenCount() const        { return parenCount; }
+    size_t getParenCount() const        { JS_ASSERT(isCompiled()); return parenCount; }
     void incRef()                       { activeUseCount++; }
     void decRef()                       { JS_ASSERT(activeUseCount > 0); activeUseCount--; }
 
     /* Accounts for the "0" (whole match) pair. */
-    size_t pairCount() const            { return parenCount + 1; }
+    size_t pairCount() const            { return getParenCount() + 1; }
 
     RegExpFlag getFlags() const         { return flags; }
     bool ignoreCase() const             { return flags & IgnoreCaseFlag; }
     bool global() const                 { return flags & GlobalFlag; }
     bool multiline() const              { return flags & MultilineFlag; }
     bool sticky() const                 { return flags & StickyFlag; }
+
+#ifdef ENABLE_YARR_JIT
+    bool hasCode() const                { return codeBlock.has16BitCode(); }
+#else
+    bool hasCode() const                { return false; }
+#endif
+    bool hasBytecode() const            { return bytecode != NULL; }
+    bool isCompiled() const             { return hasBytecode() || hasCode(); }
 };
 
 /*
  * Extend the lifetime of a given RegExpShared to at least the lifetime of
  * the guard object. See Regular Expression comment at the top.
  */
 class RegExpGuard
 {
@@ -283,18 +256,16 @@ class RegExpCompartment
     /* Like 'get', but compile 'maybeOpt' (if non-null). */
     bool get(JSContext *cx, JSAtom *source, JSString *maybeOpt, RegExpGuard *g);
 
     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf);
 };
 
 class RegExpObject : public JSObject
 {
-    typedef detail::RegExpCode RegExpCode;
-
     static const unsigned LAST_INDEX_SLOT          = 0;
     static const unsigned SOURCE_SLOT              = 1;
     static const unsigned GLOBAL_FLAG_SLOT         = 2;
     static const unsigned IGNORE_CASE_FLAG_SLOT    = 3;
     static const unsigned MULTILINE_FLAG_SLOT      = 4;
     static const unsigned STICKY_FLAG_SLOT         = 5;
 
   public:
@@ -328,27 +299,23 @@ class RegExpObject : public JSObject
      * into the |RegExpStatics| appropriately, if necessary.
      */
     RegExpRunStatus
     execute(JSContext *cx, StableCharPtr chars, size_t length, size_t *lastIndex,
             MatchPairs **output);
 
     /* Accessors. */
 
-    const Value &getLastIndex() const {
-        return getSlot(LAST_INDEX_SLOT);
-    }
+    const Value &getLastIndex() const { return getSlot(LAST_INDEX_SLOT); }
     inline void setLastIndex(double d);
     inline void zeroLastIndex();
 
     JSFlatString *toString(JSContext *cx) const;
 
-    JSAtom *getSource() const {
-        return &getSlot(SOURCE_SLOT).toString()->asAtom();
-    }
+    JSAtom *getSource() const { return &getSlot(SOURCE_SLOT).toString()->asAtom(); }
     inline void setSource(JSAtom *source);
 
     RegExpFlag getFlags() const {
         unsigned flags = 0;
         flags |= global() ? GlobalFlag : 0;
         flags |= ignoreCase() ? IgnoreCaseFlag : 0;
         flags |= multiline() ? MultilineFlag : 0;
         flags |= sticky() ? StickyFlag : 0;