Back out 047534c22207 and f95b0378d4ee (bug 820124) for reftest manifest parsing failures.
authorCameron McCormack <cam@mcc.id.au>
Sat, 22 Dec 2012 12:54:38 +1100
changeset 125956 95c2a38b92adb1729d2fd204a6f298d472ee3987
parent 125955 5aa4d9daa13c5d4c4a2b9cc4aa2fcd8d1ed80b8a
child 125957 cd54f3557ae6203e8339fd9de4a74b3177cfd71a
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)
bugs820124
milestone20.0a1
backs out047534c2220799f6e74de47f0b23af1ee78df4ee
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
Back out 047534c22207 and f95b0378d4ee (bug 820124) for reftest manifest parsing failures.
js/src/builtin/RegExp.cpp
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/gc/RootMarking.cpp
js/src/jsstr.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/RegExpStatics-inl.h
js/src/vm/RegExpStatics.cpp
js/src/vm/RegExpStatics.h
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -109,29 +109,29 @@ js::CreateRegExpMatchResult(JSContext *c
 {
     Rooted<JSStableString*> input(cx, string->ensureStable(cx));
     if (!input)
         return false;
     return CreateRegExpMatchResult(cx, input, input->chars(), input->length(), matches, rval);
 }
 
 RegExpRunStatus
-ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re,
+ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re, RegExpObject &regexp,
                   JSLinearString *input, StableCharPtr chars, size_t length,
                   size_t *lastIndex, MatchConduit &matches)
 {
     RegExpRunStatus status;
 
     /* Switch between MatchOnly and IncludeSubpatterns modes. */
     if (matches.isPair) {
         size_t lastIndex_orig = *lastIndex;
         /* Only one MatchPair slot provided: execute short-circuiting regexp. */
         status = re.executeMatchOnly(cx, chars, length, lastIndex, *matches.u.pair);
         if (status == RegExpRunStatus_Success && res)
-            res->updateLazily(cx, input, &re, lastIndex_orig);
+            res->updateLazily(cx, input, &regexp, lastIndex_orig);
     } else {
         /* Vector of MatchPairs provided: execute full regexp. */
         status = re.execute(cx, chars, length, lastIndex, *matches.u.pairs);
         if (status == RegExpRunStatus_Success && res)
             res->updateFromMatchPairs(cx, input, *matches.u.pairs);
     }
 
     return status;
@@ -146,17 +146,17 @@ js::ExecuteRegExpLegacy(JSContext *cx, R
     RegExpGuard shared;
     if (!reobj.getShared(cx, &shared))
         return false;
 
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     MatchConduit conduit(&matches);
 
     RegExpRunStatus status =
-        ExecuteRegExpImpl(cx, res, *shared, input, chars, length, lastIndex, conduit);
+        ExecuteRegExpImpl(cx, res, *shared, reobj, input, chars, length, lastIndex, conduit);
 
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound) {
         /* ExecuteRegExp() previously returned an array or null. */
         rval->setNull();
         return true;
@@ -586,17 +586,17 @@ js::ExecuteRegExp(JSContext *cx, HandleO
     if (i < 0 || i > length) {
         reobj->zeroLastIndex();
         return RegExpRunStatus_Success_NotFound;
     }
 
     /* Steps 8-21. */
     size_t lastIndexInt(i);
     RegExpRunStatus status =
-        ExecuteRegExpImpl(cx, res, *re, stableInput, chars, length, &lastIndexInt, matches);
+        ExecuteRegExpImpl(cx, res, *re, *reobj, stableInput, chars, length, &lastIndexInt, matches);
 
     if (status == RegExpRunStatus_Error)
         return RegExpRunStatus_Error;
 
     /* Step 11 (with sticky extension). */
     if (re->global() || (status == RegExpRunStatus_Success && re->sticky())) {
         if (status == RegExpRunStatus_Success_NotFound)
             reobj->zeroLastIndex();
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -327,16 +327,17 @@ Is##base##AboutToBeFinalized(Encapsulate
 DeclMarkerImpl(BaseShape, BaseShape)
 DeclMarkerImpl(BaseShape, UnownedBaseShape)
 DeclMarkerImpl(IonCode, ion::IonCode)
 DeclMarkerImpl(Object, ArgumentsObject)
 DeclMarkerImpl(Object, DebugScopeObject)
 DeclMarkerImpl(Object, GlobalObject)
 DeclMarkerImpl(Object, JSObject)
 DeclMarkerImpl(Object, JSFunction)
+DeclMarkerImpl(Object, RegExpObject)
 DeclMarkerImpl(Object, ScopeObject)
 DeclMarkerImpl(Script, JSScript)
 DeclMarkerImpl(Shape, Shape)
 DeclMarkerImpl(String, JSAtom)
 DeclMarkerImpl(String, JSString)
 DeclMarkerImpl(String, JSFlatString)
 DeclMarkerImpl(String, JSLinearString)
 DeclMarkerImpl(String, PropertyName)
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -92,16 +92,17 @@ bool Is##base##AboutToBeFinalized(Encaps
 DeclMarker(BaseShape, BaseShape)
 DeclMarker(BaseShape, UnownedBaseShape)
 DeclMarker(IonCode, ion::IonCode)
 DeclMarker(Object, ArgumentsObject)
 DeclMarker(Object, DebugScopeObject)
 DeclMarker(Object, GlobalObject)
 DeclMarker(Object, JSObject)
 DeclMarker(Object, JSFunction)
+DeclMarker(Object, RegExpObject)
 DeclMarker(Object, ScopeObject)
 DeclMarker(Script, JSScript)
 DeclMarker(Shape, Shape)
 DeclMarker(String, JSAtom)
 DeclMarker(String, JSString)
 DeclMarker(String, JSFlatString)
 DeclMarker(String, JSLinearString)
 DeclMarker(String, PropertyName)
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -669,16 +669,19 @@ Shape::Range::AutoRooter::trace(JSTracer
 {
     if (r->cursor)
         MarkShapeRoot(trc, const_cast<Shape**>(&r->cursor), "Shape::Range::AutoRooter");
 }
 
 void
 RegExpStatics::AutoRooter::trace(JSTracer *trc)
 {
+    if (statics->regexp)
+        MarkObjectRoot(trc, reinterpret_cast<JSObject**>(&statics->regexp),
+                       "RegExpStatics::AutoRooter regexp");
     if (statics->matchesInput)
         MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics->matchesInput),
                        "RegExpStatics::AutoRooter matchesInput");
     if (statics->pendingInput)
         MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics->pendingInput),
                        "RegExpStatics::AutoRooter pendingInput");
 }
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2303,141 +2303,28 @@ BuildDollarReplacement(JSContext *cx, JS
            builder.append(newReplace) &&
            builder.append(rightSide));
 #undef ENSURE
 
     args->rval().setString(builder.result());
     return true;
 }
 
-struct StringRange
-{
-    size_t start;
-    size_t length;
-
-    StringRange(size_t s, size_t l)
-      : start(s), length(l)
-    { }
-};
-
-static JSString *
-AppendSubstrings(JSContext *cx, Handle<JSStableString*> stableStr,
-                 const StringRange *ranges, size_t rangesLen)
-{
-    JS_ASSERT(rangesLen);
-
-    /* For single substrings, construct a dependent string. */
-    if (rangesLen == 1)
-        return js_NewDependentString(cx, stableStr, ranges[0].start, ranges[0].length);
-
-    /* Collect substrings into a rope. */
-    RopeBuilder rope(cx);
-    for (size_t i = 0; i < rangesLen; i++) {
-        const StringRange &sr = ranges[i];
-
-        RootedString substr(cx, js_NewDependentString(cx, stableStr, sr.start, sr.length));
-        if (!substr)
-            return NULL;
-
-        /* Appending to the rope permanently roots the substring. */
-        rope.append(substr);
-    }
-
-    return rope.result();
-}
-
-static bool
-str_replace_regexp_remove(JSContext *cx, CallArgs args, HandleString str, RegExpShared &re)
-{
-    Rooted<JSStableString*> stableStr(cx, str->ensureStable(cx));
-    if (!stableStr)
-        return false;
-
-    Vector<StringRange, 16, SystemAllocPolicy> ranges;
-
-    StableCharPtr chars = stableStr->chars();
-    size_t charsLen = stableStr->length();
-
-    MatchPair match;
-    size_t lastIndex = 0;
-
-    /* Accumulate StringRanges for unmatched substrings. */
-    while (lastIndex <= charsLen) {
-        if (!JS_CHECK_OPERATION_LIMIT(cx))
-            return false;
-
-        size_t startIndex = lastIndex;
-
-        RegExpRunStatus status = re.executeMatchOnly(cx, chars, charsLen, &lastIndex, match);
-        if (status == RegExpRunStatus_Error)
-            return false;
-        if (status == RegExpRunStatus_Success_NotFound)
-            break;
-
-        /* Include the latest unmatched substring. */
-        if (size_t(match.start) > startIndex) {
-            if (!ranges.append(StringRange(startIndex, match.start - startIndex)))
-                return false;
-        }
-
-        if (match.isEmpty())
-            lastIndex++;
-
-        /* Non-global removal executes at most once. */
-        if (!re.global())
-            break;
-    }
-
-    /* If unmatched, return the input string. */
-    if (!lastIndex) {
-        args.rval().setString(str);
-        return true;
-    }
-
-    /* The last successful match updates the RegExpStatics. */
-    cx->regExpStatics()->updateLazily(cx, stableStr, &re, lastIndex);
-
-    /* Include any remaining part of the string. */
-    if (lastIndex < charsLen) {
-        if (!ranges.append(StringRange(lastIndex, charsLen - lastIndex)))
-            return false;
-    }
-
-    /* Handle the empty string before calling .begin(). */
-    if (ranges.empty()) {
-        args.rval().setString(cx->runtime->emptyString);
-        return true;
-    }
-
-    JSString *result = AppendSubstrings(cx, stableStr, ranges.begin(), ranges.length());
-    if (!result)
-        return false;
-
-    args.rval().setString(result);
-    return true;
-}
-
 static inline bool
 str_replace_regexp(JSContext *cx, CallArgs args, ReplaceData &rdata)
 {
     if (!rdata.g.normalizeRegExp(cx, true, 2, args))
         return false;
 
     rdata.leftIndex = 0;
     rdata.calledBack = false;
 
     RegExpStatics *res = cx->regExpStatics();
     RegExpShared &re = rdata.g.regExp();
 
-    /* Optimize removal. */
-    if (rdata.repstr && rdata.repstr->length() == 0 && !rdata.dollar) {
-        JS_ASSERT(!rdata.lambda && !rdata.elembase);
-        return str_replace_regexp_remove(cx, args, rdata.str, re);
-    }
-
     Value tmp;
     if (!DoMatch(cx, res, rdata.str, re, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
         return false;
 
     if (!rdata.calledBack) {
         /* Didn't match, so the string is unmodified. */
         args.rval().setString(rdata.str);
         return true;
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -117,18 +117,18 @@ MatchPairs::initArray(size_t pairCount)
     JS_ASSERT(pairCount > 0);
 
     /* Guarantee adequate space in buffer. */
     if (!allocOrExpandArray(pairCount))
         return false;
 
     /* Initialize all MatchPair objects to invalid locations. */
     for (size_t i = 0; i < pairCount; i++) {
-        pairs_[i].start = -1;
-        pairs_[i].limit = -1;
+        pairs_[i].start = size_t(-1);
+        pairs_[i].limit = size_t(-1);
     }
 
     return true;
 }
 
 bool
 MatchPairs::initArrayFrom(MatchPairs &copyFrom)
 {
@@ -640,28 +640,16 @@ RegExpShared::executeMatchOnly(JSContext
 
 RegExpCompartment::RegExpCompartment(JSRuntime *rt)
   : map_(rt), inUse_(rt)
 {}
 
 RegExpCompartment::~RegExpCompartment()
 {
     JS_ASSERT(map_.empty());
-
-    /*
-     * RegExpStatics may have prevented a single RegExpShared from
-     * being collected during RegExpCompartment::sweep().
-     */
-    if (!inUse_.empty()) {
-        PendingSet::Enum e(inUse_);
-        RegExpShared *shared = e.front();
-        JS_ASSERT(shared->activeUseCount == 0);
-        js_delete(shared);
-        e.removeFront();
-    }
     JS_ASSERT(inUse_.empty());
 }
 
 bool
 RegExpCompartment::init(JSContext *cx)
 {
     if (!map_.init() || !inUse_.init()) {
         if (cx)
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -193,58 +193,46 @@ class RegExpShared
 
 /*
  * 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
 {
     RegExpShared *re_;
-
-  private:
     RegExpGuard(const RegExpGuard &) MOZ_DELETE;
     void operator=(const RegExpGuard &) MOZ_DELETE;
-
   public:
     RegExpGuard() : re_(NULL) {}
     RegExpGuard(RegExpShared &re) : re_(&re) {
         re_->incRef();
     }
-    ~RegExpGuard() { release(); }
-
-  public:
     void init(RegExpShared &re) {
-        JS_ASSERT(!initialized());
+        JS_ASSERT(!re_);
         re_ = &re;
         re_->incRef();
     }
-    void release() {
-        if (re_) {
+    ~RegExpGuard() {
+        if (re_)
             re_->decRef();
-            re_ = NULL;
-        }
     }
-
     bool initialized() const { return !!re_; }
     RegExpShared *re() const { JS_ASSERT(initialized()); return re_; }
     RegExpShared *operator->() { return re(); }
     RegExpShared &operator*() { return *re(); }
 };
 
 class RegExpCompartment
 {
     struct Key {
         JSAtom *atom;
         uint16_t flag;
-
         Key() {}
         Key(JSAtom *atom, RegExpFlag flag)
-          : atom(atom), flag(flag)
-        { }
-
+          : atom(atom), flag(flag) {}
         typedef Key Lookup;
         static HashNumber hash(const Lookup &l) {
             return DefaultHasher<JSAtom *>::hash(l.atom) ^ (l.flag << 1);
         }
         static bool match(Key l, Key r) {
             return l.atom == r.atom && l.flag == r.flag;
         }
     };
--- a/js/src/vm/RegExpStatics-inl.h
+++ b/js/src/vm/RegExpStatics-inl.h
@@ -22,16 +22,25 @@ js::GlobalObject::getRegExpStatics() con
 }
 
 inline size_t
 SizeOfRegExpStaticsData(const JSObject *obj, JSMallocSizeOfFun mallocSizeOf)
 {
     return mallocSizeOf(obj->getPrivate());
 }
 
+inline
+RegExpStatics::RegExpStatics()
+  : pendingLazyEvaluation(false),
+    bufferLink(NULL),
+    copied(false)
+{
+    clear();
+}
+
 inline bool
 RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, Value *out)
 {
     /* Private function: caller must perform lazy evaluation. */
     JS_ASSERT(!pendingLazyEvaluation);
 
     JS_ASSERT(start <= end);
     JS_ASSERT(end <= matchesInput->length());
@@ -217,28 +226,25 @@ RegExpStatics::getRightContext(JSSubStri
 
 inline void
 RegExpStatics::copyTo(RegExpStatics &dst)
 {
     /* Destination buffer has already been reserved by save(). */
     if (!pendingLazyEvaluation)
         dst.matches.initArrayFrom(matches);
 
-    if (regexpGuard.initialized())
-        dst.regexpGuard.init(*regexpGuard);
-
     dst.matchesInput = matchesInput;
+    dst.regexp = regexp;
     dst.lastIndex = lastIndex;
     dst.pendingInput = pendingInput;
     dst.flags = flags;
     dst.pendingLazyEvaluation = pendingLazyEvaluation;
 
-    JS_ASSERT_IF(pendingLazyEvaluation, regexpGuard.initialized());
+    JS_ASSERT_IF(pendingLazyEvaluation, regexp);
     JS_ASSERT_IF(pendingLazyEvaluation, matchesInput);
-    JS_ASSERT(regexpGuard.initialized() == dst.regexpGuard.initialized());
 }
 
 inline void
 RegExpStatics::aboutToWrite()
 {
     if (bufferLink && !bufferLink->copied) {
         copyTo(*bufferLink);
         bufferLink->copied = true;
@@ -250,41 +256,38 @@ RegExpStatics::restore()
 {
     if (bufferLink->copied)
         bufferLink->copyTo(*this);
     bufferLink = bufferLink->bufferLink;
 }
 
 inline void
 RegExpStatics::updateLazily(JSContext *cx, JSLinearString *input,
-                            RegExpShared *shared, size_t lastIndex)
+                            RegExpObject *regexp, size_t lastIndex)
 {
-    JS_ASSERT(input && shared);
+    JS_ASSERT(input && regexp);
     aboutToWrite();
 
     BarrieredSetPair<JSString, JSLinearString>(cx->compartment,
                                                pendingInput, input,
                                                matchesInput, input);
-    if (regexpGuard.initialized())
-        regexpGuard.release();
-    regexpGuard.init(*shared);
-
+    pendingLazyEvaluation = true;
+    this->regexp = regexp;
     this->lastIndex = lastIndex;
-    pendingLazyEvaluation = true;
 }
 
 inline bool
 RegExpStatics::updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs &newPairs)
 {
     JS_ASSERT(input);
     aboutToWrite();
 
     /* Unset all lazy state. */
     pendingLazyEvaluation = false;
-    this->regexpGuard.release();
+    this->regexp = NULL;
     this->lastIndex = size_t(-1);
 
     BarrieredSetPair<JSString, JSLinearString>(cx->compartment,
                                                pendingInput, input,
                                                matchesInput, input);
 
     if (!matches.initArrayFrom(newPairs)) {
         js_ReportOutOfMemory(cx);
@@ -293,24 +296,21 @@ RegExpStatics::updateFromMatchPairs(JSCo
 
     return true;
 }
 
 inline void
 RegExpStatics::clear()
 {
     aboutToWrite();
-
-    matches.forgetArray();
+    flags = RegExpFlag(0);
+    pendingInput = NULL;
+    pendingLazyEvaluation = false;
     matchesInput = NULL;
-    regexpGuard.release();
-    lastIndex = size_t(-1);
-    pendingInput = NULL;
-    flags = RegExpFlag(0);
-    pendingLazyEvaluation = false;
+    matches.forgetArray();
 }
 
 inline void
 RegExpStatics::setPendingInput(JSString *newInput)
 {
     aboutToWrite();
     pendingInput = newInput;
 }
@@ -358,19 +358,18 @@ RegExpStatics::reset(JSContext *cx, JSSt
     checkInvariants();
 }
 
 inline void
 RegExpStatics::checkInvariants()
 {
 #ifdef DEBUG
     if (pendingLazyEvaluation) {
-        JS_ASSERT(regexpGuard.initialized());
+        JS_ASSERT(regexp);
         JS_ASSERT(pendingInput);
-        JS_ASSERT(lastIndex != size_t(-1));
         return;
     }
 
     if (matches.empty()) {
         JS_ASSERT(!matchesInput);
         return;
     }
 
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -69,32 +69,35 @@ RegExpStatics::create(JSContext *cx, Glo
 }
 
 bool
 RegExpStatics::executeLazy(JSContext *cx)
 {
     if (!pendingLazyEvaluation)
         return true;
 
-    JS_ASSERT(regexpGuard.initialized());
+    JS_ASSERT(regexp);
     JS_ASSERT(matchesInput);
     JS_ASSERT(lastIndex != size_t(-1));
 
     /*
      * It is not necessary to call aboutToWrite(): evaluation of
      * implicit copies is safe.
      */
 
     size_t length = matchesInput->length();
     StableCharPtr chars(matchesInput->chars(), length);
 
     /* Execute the full regular expression. */
-    RegExpRunStatus status = regexpGuard->execute(cx, chars, length, &this->lastIndex, this->matches);
+    RegExpGuard shared;
+    if (!regexp->getShared(cx, &shared))
+        return false;
+
+    RegExpRunStatus status = shared->execute(cx, chars, length, &this->lastIndex, this->matches);
     if (status == RegExpRunStatus_Error)
         return false;
 
     /* Unset lazy state and remove rooted values that now have no use. */
     pendingLazyEvaluation = false;
-    regexpGuard.release();
-    lastIndex = size_t(-1);
+    regexp = NULL;
 
     return true;
 }
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -10,48 +10,43 @@
 
 #include "jscntxt.h"
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "js/Vector.h"
 
 #include "vm/MatchPairs.h"
-#include "vm/RegExpObject.h"
 
 namespace js {
 
 class RegExpStatics
 {
     /* The latest RegExp output, set after execution. */
     VectorMatchPairs        matches;
     HeapPtr<JSLinearString> matchesInput;
 
     /* The previous RegExp input, used to resolve lazy state. */
-    RegExpGuard             regexpGuard;  /* Strong reference to RegExpShared. */
+    HeapPtr<RegExpObject>   regexp;
     size_t                  lastIndex;
 
     /* The latest RegExp input, set before execution. */
     HeapPtr<JSString>       pendingInput;
     RegExpFlag              flags;
 
     /*
-     * If true, |matchesInput|, |regexpGuard|, and |lastIndex| may be used
+     * If true, |matchesInput|, |regexp|, and |lastIndex| may be used
      * to replay the last executed RegExp, and |matches| is invalid.
      */
     bool                    pendingLazyEvaluation;
 
     /* Linkage for preserving RegExpStatics during nested RegExp execution. */
     RegExpStatics           *bufferLink;
     bool                    copied;
 
-  public:
-    RegExpStatics() : bufferLink(NULL), copied(false) { clear(); }
-    static JSObject *create(JSContext *cx, GlobalObject *parent);
-
   private:
     bool executeLazy(JSContext *cx);
 
     inline void aboutToWrite();
     inline void copyTo(RegExpStatics &dst);
 
     inline void restore();
     bool save(JSContext *cx, RegExpStatics *buffer) {
@@ -78,19 +73,24 @@ class RegExpStatics
     void markFlagsSet(JSContext *cx);
 
     struct InitBuffer {};
     explicit RegExpStatics(InitBuffer) : bufferLink(NULL), copied(false) {}
 
     friend class PreserveRegExpStatics;
 
   public:
+    inline RegExpStatics();
+
+    static JSObject *create(JSContext *cx, GlobalObject *parent);
+
     /* Mutators. */
+
     inline void updateLazily(JSContext *cx, JSLinearString *input,
-                             RegExpShared *shared, size_t lastIndex);
+                             RegExpObject *regexp, size_t lastIndex);
     inline bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs &newPairs);
     inline void setMultiline(JSContext *cx, bool enabled);
 
     inline void clear();
 
     /* Corresponds to JSAPI functionality to set the pending RegExp input. */
     inline void reset(JSContext *cx, JSString *newInput, bool newMultiline);
 
@@ -113,16 +113,18 @@ class RegExpStatics
     bool matched() const {
         /* Safe: only used by String methods, which do not set lazy mode. */
         JS_ASSERT(!pendingLazyEvaluation);
         JS_ASSERT(matches.pairCount() > 0);
         return matches[0].limit - matches[0].start > 0;
     }
 
     void mark(JSTracer *trc) {
+        if (regexp)
+            gc::MarkObject(trc, &regexp, "res->regexp");
         if (pendingInput)
             MarkString(trc, &pendingInput, "res->pendingInput");
         if (matchesInput)
             MarkString(trc, &matchesInput, "res->matchesInput");
     }
 
     /* Value creators. */