Bug 1345177 - Fix RegExpShared rooting hazards now it's a GC thing r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 28 Mar 2017 11:51:18 +0100
changeset 350107 9fdc31fe714ad67bb66ec2613429d8519624fbb0
parent 350106 9625ba329e864ebc4233b8b48338baaaa59f1f2e
child 350108 4fd1e9b734b931691a5a849482bff0b05fe1ef4f
push id31568
push userkwierso@gmail.com
push dateTue, 28 Mar 2017 20:31:07 +0000
treeherdermozilla-central@272ce6c25721 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1345177
milestone55.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 1345177 - Fix RegExpShared rooting hazards now it's a GC thing r=sfink
js/src/builtin/RegExp.cpp
js/src/irregexp/RegExpEngine.cpp
js/src/irregexp/RegExpEngine.h
js/src/proxy/CrossCompartmentWrapper.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/RegExpStatics.cpp
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -116,28 +116,29 @@ CreateRegExpSearchResult(JSContext* cx, 
     return position | (lastIndex << 15);
 }
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 9-14, except 12.a.i, 12.c.i.1.
  */
 static RegExpRunStatus
-ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLinearString input,
-                  size_t searchIndex, MatchPairs* matches, size_t* endIndex)
+ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, MutableHandleRegExpShared re,
+                  HandleLinearString input, size_t searchIndex, MatchPairs* matches,
+                  size_t* endIndex)
 {
-    RegExpRunStatus status = re.execute(cx, input, searchIndex, matches, endIndex);
+    RegExpRunStatus status = RegExpShared::execute(cx, re, input, searchIndex, matches, endIndex);
 
     /* Out of spec: Update RegExpStatics. */
     if (status == RegExpRunStatus_Success && res) {
         if (matches) {
             if (!res->updateFromMatchPairs(cx, input, *matches))
                 return RegExpRunStatus_Error;
         } else {
-            res->updateLazily(cx, input, &re, searchIndex);
+            res->updateLazily(cx, input, re, searchIndex);
         }
     }
     return status;
 }
 
 /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */
 bool
 js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reobj,
@@ -145,17 +146,17 @@ js::ExecuteRegExpLegacy(JSContext* cx, R
                         MutableHandleValue rval)
 {
     RootedRegExpShared shared(cx);
     if (!RegExpObject::getShared(cx, reobj, &shared))
         return false;
 
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
 
-    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex,
+    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &shared, input, *lastIndex,
                                                &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound) {
         /* ExecuteRegExp() previously returned an array or null. */
         rval.setNull();
         return true;
@@ -963,17 +964,17 @@ ExecuteRegExp(JSContext* cx, HandleObjec
          * However, the spec will change to match our implementation's
          * behavior. See https://github.com/tc39/ecma262/issues/128.
          */
         if (IsTrailSurrogateWithLeadSurrogate(cx, input, lastIndex))
             lastIndex--;
     }
 
     /* Steps 3, 11-14, except 12.a.i, 12.c.i.1. */
-    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, matches, endIndex);
+    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &re, input, lastIndex, matches, endIndex);
     if (status == RegExpRunStatus_Error)
         return RegExpRunStatus_Error;
 
     /* Steps 12.a.i, 12.c.i.i, 15 are done by Self-hosted function. */
 
     return status;
 }
 
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -1709,17 +1709,17 @@ IsNativeRegExpEnabled(JSContext* cx)
 #ifdef JS_CODEGEN_NONE
     return false;
 #else
     return cx->options().nativeRegExp();
 #endif
 }
 
 RegExpCode
-irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
+irregexp::CompilePattern(JSContext* cx, HandleRegExpShared shared, RegExpCompileData* data,
                          HandleLinearString sample, bool is_global, bool ignore_case,
                          bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
                          bool unicode)
 {
     if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) {
         JS_ReportErrorASCII(cx, "regexp too big");
         return RegExpCode();
     }
--- a/js/src/irregexp/RegExpEngine.h
+++ b/js/src/irregexp/RegExpEngine.h
@@ -81,17 +81,17 @@ struct RegExpCode
     }
 
     void destroy() {
         js_free(byteCode);
     }
 };
 
 RegExpCode
-CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
+CompilePattern(JSContext* cx, HandleRegExpShared shared, RegExpCompileData* data,
                HandleLinearString sample,  bool is_global, bool ignore_case,
                bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
                bool unicode);
 
 // Note: this may return RegExpRunStatus_Error if an interrupt was requested
 // while the code was executing.
 template <typename CharT>
 RegExpRunStatus
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -464,18 +464,19 @@ CrossCompartmentWrapper::regexp_toShared
     RootedRegExpShared re(cx);
     {
         AutoCompartment call(cx, wrappedObject(wrapper));
         if (!Wrapper::regexp_toShared(cx, wrapper, &re))
             return false;
     }
 
     // Get an equivalent RegExpShared associated with the current compartment.
-    cx->markAtom(re->getSource());
-    return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), shared);
+    RootedAtom source(cx, re->getSource());
+    cx->markAtom(source);
+    return cx->compartment()->regExps.get(cx, source, re->getFlags(), shared);
 }
 
 bool
 CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx, HandleObject wrapper, MutableHandleValue vp) const
 {
     PIERCE(cx, wrapper,
            NOTHING,
            Wrapper::boxedValue_unbox(cx, wrapper, vp),
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -273,17 +273,18 @@ RegExpObject::create(JSContext* cx, Hand
     return regexp;
 }
 
 /* static */ bool
 RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp,
                            MutableHandleRegExpShared shared)
 {
     MOZ_ASSERT(!regexp->hasShared());
-    if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), shared))
+    RootedAtom source(cx, regexp->getSource());
+    if (!cx->compartment()->regExps.get(cx, source, regexp->getFlags(), shared))
         return false;
 
     regexp->setShared(*shared);
     return true;
 }
 
 Shape*
 RegExpObject::assignInitialShape(JSContext* cx, Handle<RegExpObject*> self)
@@ -498,24 +499,25 @@ RegExpObject::toString(JSContext* cx) co
         return nullptr;
     if (sticky() && !sb.append('y'))
         return nullptr;
 
     return sb.finishString();
 }
 
 #ifdef DEBUG
-bool
-RegExpShared::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input)
+/* static */ bool
+RegExpShared::dumpBytecode(JSContext* cx, MutableHandleRegExpShared re, bool match_only,
+                           HandleLinearString input)
 {
     CompilationMode mode = match_only ? MatchOnly : Normal;
-    if (!compileIfNecessary(cx, input, mode, ForceByteCode))
+    if (!RegExpShared::compileIfNecessary(cx, re, input, mode, ForceByteCode))
         return false;
 
-    const uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
+    const uint8_t* byteCode = re->compilation(mode, input->hasLatin1Chars()).byteCode;
     const uint8_t* pc = byteCode;
 
     auto Load32Aligned = [](const uint8_t* pc) -> int32_t {
         MOZ_ASSERT((reinterpret_cast<uintptr_t>(pc) & 3) == 0);
         return *reinterpret_cast<const int32_t*>(pc);
     };
 
     auto Load16Aligned = [](const uint8_t* pc) -> int32_t {
@@ -888,17 +890,17 @@ RegExpShared::dumpBytecode(JSContext* cx
 /* static */ bool
 RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
                            bool match_only, HandleLinearString input)
 {
     RootedRegExpShared shared(cx);
     if (!getShared(cx, regexp, &shared))
         return false;
 
-    return shared->dumpBytecode(cx, match_only, input);
+    return RegExpShared::dumpBytecode(cx, &shared, match_only, input);
 }
 #endif
 
 template <typename CharT>
 static MOZ_ALWAYS_INLINE bool
 IsRegExpMetaChar(CharT ch)
 {
     switch (ch) {
@@ -965,147 +967,149 @@ RegExpShared::traceChildren(JSTracer* tr
 
 void
 RegExpShared::discardJitCode()
 {
     for (auto& comp : compilationArray)
         comp.jitCode = nullptr;
 }
 
-bool
-RegExpShared::compile(JSContext* cx, HandleLinearString input,
+/* static */ bool
+RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
                       CompilationMode mode, ForceByteCodeEnum force)
 {
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     AutoTraceLog logCompile(logger, TraceLogger_IrregexpCompile);
 
-    RootedAtom pattern(cx, source);
-    return compile(cx, pattern, input, mode, force);
+    RootedAtom pattern(cx, re->source);
+    return compile(cx, re, pattern, input, mode, force);
 }
 
-bool
-RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
-                      CompilationMode mode, ForceByteCodeEnum force)
+/* static */ bool
+RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleAtom pattern,
+                      HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force)
 {
-    if (!ignoreCase() && !StringHasRegExpMetaChars(pattern))
-        canStringMatch = true;
+    if (!re->ignoreCase() && !StringHasRegExpMetaChars(pattern))
+        re->canStringMatch = true;
 
     CompileOptions options(cx);
     TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
     LifoAllocScope scope(&cx->tempLifoAlloc());
 
     /* Parse the pattern. */
     irregexp::RegExpCompileData data;
     if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern,
-                                multiline(), mode == MatchOnly, unicode(), ignoreCase(),
-                                global(), sticky(), &data))
+                                re->multiline(), mode == MatchOnly, re->unicode(),
+                                re->ignoreCase(), re->global(), re->sticky(), &data))
     {
         return false;
     }
 
-    this->parenCount = data.capture_count;
+    re->parenCount = data.capture_count;
 
-    irregexp::RegExpCode code = irregexp::CompilePattern(cx, this, &data, input,
+    irregexp::RegExpCode code = irregexp::CompilePattern(cx, re, &data, input,
                                                          false /* global() */,
-                                                         ignoreCase(),
+                                                         re->ignoreCase(),
                                                          input->hasLatin1Chars(),
                                                          mode == MatchOnly,
                                                          force == ForceByteCode,
-                                                         sticky(), unicode());
+                                                         re->sticky(),
+                                                         re->unicode());
     if (code.empty())
         return false;
 
     MOZ_ASSERT(!code.jitCode || !code.byteCode);
     MOZ_ASSERT_IF(force == ForceByteCode, code.byteCode);
 
-    RegExpCompilation& compilation = this->compilation(mode, input->hasLatin1Chars());
+    RegExpCompilation& compilation = re->compilation(mode, input->hasLatin1Chars());
     if (code.jitCode)
         compilation.jitCode = code.jitCode;
     else if (code.byteCode)
         compilation.byteCode = code.byteCode;
 
     return true;
 }
 
-bool
-RegExpShared::compileIfNecessary(JSContext* cx, HandleLinearString input,
-                                 CompilationMode mode, ForceByteCodeEnum force)
+/* static */ bool
+RegExpShared::compileIfNecessary(JSContext* cx, MutableHandleRegExpShared re,
+                                 HandleLinearString input, CompilationMode mode,
+                                 ForceByteCodeEnum force)
 {
-    if (isCompiled(mode, input->hasLatin1Chars(), force))
+    if (re->isCompiled(mode, input->hasLatin1Chars(), force))
         return true;
-    return compile(cx, input, mode, force);
+    return compile(cx, re, input, mode, force);
 }
 
-RegExpRunStatus
-RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
-                      MatchPairs* matches, size_t* endIndex)
+/* static */ RegExpRunStatus
+RegExpShared::execute(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
+                      size_t start, MatchPairs* matches, size_t* endIndex)
 {
     MOZ_ASSERT_IF(matches, !endIndex);
     MOZ_ASSERT_IF(!matches, endIndex);
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
 
     CompilationMode mode = matches ? Normal : MatchOnly;
 
     /* Compile the code at point-of-use. */
-    if (!compileIfNecessary(cx, input, mode, DontForceByteCode))
+    if (!compileIfNecessary(cx, re, input, mode, DontForceByteCode))
         return RegExpRunStatus_Error;
 
     /*
      * Ensure sufficient memory for output vector.
      * No need to initialize it. The RegExp engine fills them in on a match.
      */
-    if (matches && !matches->allocOrExpandArray(pairCount())) {
+    if (matches && !matches->allocOrExpandArray(re->pairCount())) {
         ReportOutOfMemory(cx);
         return RegExpRunStatus_Error;
     }
 
     size_t length = input->length();
 
     // Reset the Irregexp backtrack stack if it grows during execution.
     irregexp::RegExpStackScope stackScope(cx);
 
-    if (canStringMatch) {
-        MOZ_ASSERT(pairCount() == 1);
-        size_t sourceLength = source->length();
-        if (sticky()) {
+    if (re->canStringMatch) {
+        MOZ_ASSERT(re->pairCount() == 1);
+        size_t sourceLength = re->source->length();
+        if (re->sticky()) {
             // First part checks size_t overflow.
             if (sourceLength + start < sourceLength || sourceLength + start > length)
                 return RegExpRunStatus_Success_NotFound;
-            if (!HasSubstringAt(input, source, start))
+            if (!HasSubstringAt(input, re->source, start))
                 return RegExpRunStatus_Success_NotFound;
 
             if (matches) {
                 (*matches)[0].start = start;
                 (*matches)[0].limit = start + sourceLength;
 
                 matches->checkAgainst(length);
             } else if (endIndex) {
                 *endIndex = start + sourceLength;
             }
             return RegExpRunStatus_Success;
         }
 
-        int res = StringFindPattern(input, source, start);
+        int res = StringFindPattern(input, re->source, start);
         if (res == -1)
             return RegExpRunStatus_Success_NotFound;
 
         if (matches) {
             (*matches)[0].start = res;
             (*matches)[0].limit = res + sourceLength;
 
             matches->checkAgainst(length);
         } else if (endIndex) {
             *endIndex = res + sourceLength;
         }
         return RegExpRunStatus_Success;
     }
 
     do {
-        jit::JitCode* code = compilation(mode, input->hasLatin1Chars()).jitCode;
+        jit::JitCode* code = re->compilation(mode, input->hasLatin1Chars()).jitCode;
         if (!code)
             break;
 
         RegExpRunStatus result;
         {
             AutoTraceLog logJIT(logger, TraceLogger_IrregexpExecute);
             AutoCheckCannotGC nogc;
             if (input->hasLatin1Chars()) {
@@ -1134,20 +1138,20 @@ RegExpShared::execute(JSContext* cx, Han
         MOZ_ASSERT(result == RegExpRunStatus_Success);
 
         if (matches)
             matches->checkAgainst(length);
         return RegExpRunStatus_Success;
     } while (false);
 
     // Compile bytecode for the RegExp if necessary.
-    if (!compileIfNecessary(cx, input, mode, ForceByteCode))
+    if (!compileIfNecessary(cx, re, input, mode, ForceByteCode))
         return RegExpRunStatus_Error;
 
-    uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
+    uint8_t* byteCode = re->compilation(mode, input->hasLatin1Chars()).byteCode;
     AutoTraceLog logInterpreter(logger, TraceLogger_IrregexpExecute);
 
     AutoStableStringChars inputChars(cx);
     if (!inputChars.init(cx, input))
         return RegExpRunStatus_Error;
 
     RegExpRunStatus result;
     if (inputChars.isLatin1()) {
@@ -1276,17 +1280,17 @@ RegExpCompartment::sweep(JSRuntime* rt)
     if (optimizableRegExpInstanceShape_ &&
         IsAboutToBeFinalized(&optimizableRegExpInstanceShape_))
     {
         optimizableRegExpInstanceShape_.set(nullptr);
     }
 }
 
 bool
-RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags,
+RegExpCompartment::get(JSContext* cx, HandleAtom source, RegExpFlag flags,
                        MutableHandleRegExpShared result)
 {
     DependentAddPtr<Set> p(cx, set_.get(), Key(source, flags));
     if (p) {
         result.set(*p);
         return true;
     }
 
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -37,16 +37,20 @@
  */
 namespace js {
 
 struct MatchPair;
 class MatchPairs;
 class RegExpShared;
 class RegExpStatics;
 
+using RootedRegExpShared = JS::Rooted<RegExpShared*>;
+using HandleRegExpShared = JS::Handle<RegExpShared*>;
+using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
+
 namespace frontend { class TokenStream; }
 
 enum RegExpFlag : uint8_t
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
     StickyFlag      = 0x08,
@@ -140,39 +144,41 @@ class RegExpShared : public gc::TenuredC
     }
 
     // Tables referenced by JIT code.
     Vector<uint8_t*, 0, SystemAllocPolicy> tables;
 
     /* Internal functions. */
     RegExpShared(JSAtom* source, RegExpFlag flags);
 
-    bool compile(JSContext* cx, HandleLinearString input,
-                 CompilationMode mode, ForceByteCodeEnum force);
-    bool compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
-                 CompilationMode mode, ForceByteCodeEnum force);
+    static bool compile(JSContext* cx, MutableHandleRegExpShared res, HandleLinearString input,
+                        CompilationMode mode, ForceByteCodeEnum force);
+    static bool compile(JSContext* cx, MutableHandleRegExpShared res, HandleAtom pattern,
+                        HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force);
 
-    bool compileIfNecessary(JSContext* cx, HandleLinearString input,
-                            CompilationMode mode, ForceByteCodeEnum force);
+    static bool compileIfNecessary(JSContext* cx, MutableHandleRegExpShared res,
+                                   HandleLinearString input, CompilationMode mode,
+                                   ForceByteCodeEnum force);
 
     const RegExpCompilation& compilation(CompilationMode mode, bool latin1) const {
         return compilationArray[CompilationIndex(mode, latin1)];
     }
 
     RegExpCompilation& compilation(CompilationMode mode, bool latin1) {
         return compilationArray[CompilationIndex(mode, latin1)];
     }
 
   public:
     ~RegExpShared();
 
     // Execute this RegExp on input starting from searchIndex, filling in
     // matches if specified and otherwise only determining if there is a match.
-    RegExpRunStatus execute(JSContext* cx, HandleLinearString input, size_t searchIndex,
-                            MatchPairs* matches, size_t* endIndex);
+    static RegExpRunStatus execute(JSContext* cx, MutableHandleRegExpShared res,
+                                   HandleLinearString input, size_t searchIndex,
+                                   MatchPairs* matches, size_t* endIndex);
 
     // Register a table with this RegExpShared, and take ownership.
     bool addTable(uint8_t* table) {
         return tables.append(table);
     }
 
     /* Accessors */
 
@@ -225,24 +231,21 @@ class RegExpShared : public gc::TenuredC
         return offsetof(RegExpShared, compilationArray)
              + (CompilationIndex(mode, false) * sizeof(RegExpCompilation))
              + offsetof(RegExpCompilation, jitCode);
     }
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
 #ifdef DEBUG
-    bool dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input);
+    static bool dumpBytecode(JSContext* cx, MutableHandleRegExpShared res, bool match_only,
+                             HandleLinearString input);
 #endif
 };
 
-using RootedRegExpShared = JS::Rooted<RegExpShared*>;
-using HandleRegExpShared = JS::Handle<RegExpShared*>;
-using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
-
 class RegExpCompartment
 {
     struct Key {
         JSAtom* atom;
         uint16_t flag;
 
         Key() {}
         Key(JSAtom* atom, RegExpFlag flag)
@@ -303,17 +306,17 @@ class RegExpCompartment
     explicit RegExpCompartment(Zone* zone);
     ~RegExpCompartment();
 
     bool init(JSContext* cx);
     void sweep(JSRuntime* rt);
 
     bool empty() { return set_.empty(); }
 
-    bool get(JSContext* cx, JSAtom* source, RegExpFlag flags, MutableHandleRegExpShared shared);
+    bool get(JSContext* cx, HandleAtom source, RegExpFlag flags, MutableHandleRegExpShared shared);
 
     /* Like 'get', but compile 'maybeOpt' (if non-null). */
     bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt,
              MutableHandleRegExpShared shared);
 
     /* Get or create template object used to base the result of .exec() on. */
     ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
         if (matchResultTemplateObject_)
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -77,27 +77,29 @@ RegExpStatics::executeLazy(JSContext* cx
         return true;
 
     MOZ_ASSERT(lazySource);
     MOZ_ASSERT(matchesInput);
     MOZ_ASSERT(lazyIndex != size_t(-1));
 
     /* Retrieve or create the RegExpShared in this compartment. */
     RootedRegExpShared shared(cx);
-    if (!cx->compartment()->regExps.get(cx, lazySource, lazyFlags, &shared))
+    RootedAtom source(cx, lazySource);
+    if (!cx->compartment()->regExps.get(cx, source, lazyFlags, &shared))
         return false;
 
     /*
      * It is not necessary to call aboutToWrite(): evaluation of
      * implicit copies is safe.
      */
 
     /* Execute the full regular expression. */
     RootedLinearString input(cx, matchesInput);
-    RegExpRunStatus status = shared->execute(cx, input, lazyIndex, &this->matches, nullptr);
+    RegExpRunStatus status = RegExpShared::execute(cx, &shared, input, lazyIndex, &this->matches,
+                                                   nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     /*
      * RegExpStatics are only updated on successful (matching) execution.
      * Re-running the same expression must therefore produce a matching result.
      */
     MOZ_ASSERT(status == RegExpRunStatus_Success);