Bug 1053683 - Add overrecursion checks to FillInBMInfo. r=jandem, a=abillings
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 14 Aug 2014 10:37:00 -0400
changeset 208317 c6e134b4ed52
parent 208316 9e94aa2f0ae7
child 208318 6790f9333fec
push id3822
push userryanvm@gmail.com
push date2014-08-18 13:40 +0000
treeherdermozilla-beta@c6e134b4ed52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, abillings
bugs1053683
milestone32.0
Bug 1053683 - Add overrecursion checks to FillInBMInfo. r=jandem, a=abillings
js/src/irregexp/RegExpEngine.cpp
js/src/irregexp/RegExpEngine.h
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -488,16 +488,31 @@ class VisitMarker
     }
     ~VisitMarker() {
         info_->visited = false;
     }
   private:
     NodeInfo* info_;
 };
 
+bool
+SeqRegExpNode::FillInBMInfo(int offset,
+                            int budget,
+                            BoyerMooreLookahead* bm,
+                            bool not_at_start)
+{
+    if (!bm->CheckOverRecursed())
+        return false;
+    if (!on_success_->FillInBMInfo(offset, budget - 1, bm, not_at_start))
+        return false;
+    if (offset == 0)
+        set_bm_info(not_at_start, bm);
+    return true;
+}
+
 RegExpNode *
 SeqRegExpNode::FilterASCII(int depth, bool ignore_case)
 {
     if (info()->replacement_calculated)
         return replacement();
 
     if (depth < 0)
         return this;
@@ -528,27 +543,34 @@ ActionNode::EatsAtLeast(int still_to_fin
         return 0;
     if (action_type_ == POSITIVE_SUBMATCH_SUCCESS)
         return 0;  // Rewinds input!
     return on_success()->EatsAtLeast(still_to_find,
                                      budget - 1,
                                      not_at_start);
 }
 
-void
+bool
 ActionNode::FillInBMInfo(int offset,
                          int budget,
                          BoyerMooreLookahead* bm,
                          bool not_at_start)
 {
-    if (action_type_ == BEGIN_SUBMATCH)
+    if (!bm->CheckOverRecursed())
+        return false;
+
+    if (action_type_ == BEGIN_SUBMATCH) {
         bm->SetRest(offset);
-    else if (action_type_ != POSITIVE_SUBMATCH_SUCCESS)
-        on_success()->FillInBMInfo(offset, budget - 1, bm, not_at_start);
+    } else if (action_type_ != POSITIVE_SUBMATCH_SUCCESS) {
+        if (!on_success()->FillInBMInfo(offset, budget - 1, bm, not_at_start))
+            return false;
+    }
     SaveBMInfo(bm, not_at_start, offset);
+
+    return true;
 }
 
 /* static */ ActionNode *
 ActionNode::SetRegister(int reg,
                         int val,
                         RegExpNode *on_success)
 {
     ActionNode *result = on_success->alloc()->newInfallible<ActionNode>(SET_REGISTER, on_success);
@@ -768,45 +790,51 @@ AssertionNode::EatsAtLeast(int still_to_
     // that won't prevent us from preloading a lot of characters for the other
     // branches in the node graph.
     if (assertion_type() == AT_START && not_at_start)
         return still_to_find;
 
     return on_success()->EatsAtLeast(still_to_find, budget - 1, not_at_start);
 }
 
-void
+bool
 AssertionNode::FillInBMInfo(int offset, int budget, BoyerMooreLookahead* bm, bool not_at_start)
 {
+    if (!bm->CheckOverRecursed())
+        return false;
+
     // Match the behaviour of EatsAtLeast on this node.
     if (assertion_type() == AT_START && not_at_start)
-        return;
-
-    on_success()->FillInBMInfo(offset, budget - 1, bm, not_at_start);
+        return true;
+
+    if (!on_success()->FillInBMInfo(offset, budget - 1, bm, not_at_start))
+        return false;
     SaveBMInfo(bm, not_at_start, offset);
+    return true;
 }
 
 // -------------------------------------------------------------------
 // BackReferenceNode
 
 int
 BackReferenceNode::EatsAtLeast(int still_to_find, int budget, bool not_at_start)
 {
     if (budget <= 0)
         return 0;
     return on_success()->EatsAtLeast(still_to_find, budget - 1, not_at_start);
 }
 
-void
+bool
 BackReferenceNode::FillInBMInfo(int offset, int budget, BoyerMooreLookahead* bm, bool not_at_start)
 {
     // Working out the set of characters that a backreference can match is too
     // hard, so we just say that any character can match.
     bm->SetRest(offset);
     SaveBMInfo(bm, not_at_start, offset);
+    return true;
 }
 
 // -------------------------------------------------------------------
 // ChoiceNode
 
 int
 ChoiceNode::EatsAtLeastHelper(int still_to_find,
                               int budget,
@@ -860,34 +888,39 @@ ChoiceNode::GetQuickCheckDetails(QuickCh
         node->GetQuickCheckDetails(&new_details, compiler,
                                    characters_filled_in,
                                    not_at_start);
         // Here we merge the quick match details of the two branches.
         details->Merge(&new_details, characters_filled_in);
     }
 }
 
-void
+bool
 ChoiceNode::FillInBMInfo(int offset,
                          int budget,
                          BoyerMooreLookahead* bm,
                          bool not_at_start)
 {
+    if (!bm->CheckOverRecursed())
+        return false;
+
     const GuardedAlternativeVector &alts = alternatives();
     budget = (budget - 1) / alts.length();
     for (size_t i = 0; i < alts.length(); i++) {
         const GuardedAlternative& alt = alts[i];
         if (alt.guards() != nullptr && alt.guards()->length() != 0) {
             bm->SetRest(offset);  // Give up trying to fill in info.
             SaveBMInfo(bm, not_at_start, offset);
-            return;
+            return true;
         }
-        alt.node()->FillInBMInfo(offset, budget, bm, not_at_start);
+        if (!alt.node()->FillInBMInfo(offset, budget, bm, not_at_start))
+            return false;
     }
     SaveBMInfo(bm, not_at_start, offset);
+    return true;
 }
 
 RegExpNode*
 ChoiceNode::FilterASCII(int depth, bool ignore_case)
 {
     if (info()->replacement_calculated)
         return replacement();
     if (depth < 0)
@@ -940,16 +973,32 @@ ChoiceNode::FilterASCII(int depth, bool 
 
     alternatives_.appendAll(new_alternatives);
     return this;
 }
 
 // -------------------------------------------------------------------
 // NegativeLookaheadChoiceNode
 
+bool
+NegativeLookaheadChoiceNode::FillInBMInfo(int offset,
+                                          int budget,
+                                          BoyerMooreLookahead* bm,
+                                          bool not_at_start)
+{
+    if (!bm->CheckOverRecursed())
+        return false;
+
+    if (!alternatives()[1].node()->FillInBMInfo(offset, budget - 1, bm, not_at_start))
+        return false;
+    if (offset == 0)
+        set_bm_info(not_at_start, bm);
+    return true;
+}
+
 int
 NegativeLookaheadChoiceNode::EatsAtLeast(int still_to_find, int budget, bool not_at_start)
 {
     if (budget <= 0)
         return 0;
 
     // Alternative 0 is the negative lookahead, alternative 1 is what comes
     // afterwards.
@@ -1049,29 +1098,31 @@ LoopChoiceNode::GetQuickCheckDetails(Qui
         return;
     VisitMarker marker(info());
     return ChoiceNode::GetQuickCheckDetails(details,
                                             compiler,
                                             characters_filled_in,
                                             not_at_start);
 }
 
-void
+bool
 LoopChoiceNode::FillInBMInfo(int offset,
                              int budget,
                              BoyerMooreLookahead* bm,
                              bool not_at_start)
 {
     if (body_can_be_zero_length_ || budget <= 0) {
         bm->SetRest(offset);
         SaveBMInfo(bm, not_at_start, offset);
-        return;
+        return true;
     }
-    ChoiceNode::FillInBMInfo(offset, budget - 1, bm, not_at_start);
+    if (!ChoiceNode::FillInBMInfo(offset, budget - 1, bm, not_at_start))
+        return false;
     SaveBMInfo(bm, not_at_start, offset);
+    return true;
 }
 
 RegExpNode *
 LoopChoiceNode::FilterASCII(int depth, bool ignore_case)
 {
     if (info()->replacement_calculated)
         return replacement();
     if (depth < 0)
@@ -1407,17 +1458,17 @@ class FrequencyCollator
   private:
     CharacterFrequency frequencies_[RegExpMacroAssembler::kTableSize];
     int total_samples_;
 };
 
 class irregexp::RegExpCompiler
 {
   public:
-    RegExpCompiler(LifoAlloc *alloc, int capture_count, bool ignore_case, bool is_ascii);
+    RegExpCompiler(JSContext *cx, LifoAlloc *alloc, int capture_count, bool ignore_case, bool is_ascii);
 
     int AllocateRegister() {
         if (next_register_ >= RegExpMacroAssembler::kMaxRegister) {
             reg_exp_too_big_ = true;
             return next_register_;
         }
         return next_register_++;
     }
@@ -1450,31 +1501,33 @@ class irregexp::RegExpCompiler
     inline bool ascii() { return ascii_; }
     FrequencyCollator* frequency_collator() { return &frequency_collator_; }
 
     int current_expansion_factor() { return current_expansion_factor_; }
     void set_current_expansion_factor(int value) {
         current_expansion_factor_ = value;
     }
 
+    JSContext *cx() const { return cx_; }
     LifoAlloc *alloc() const { return alloc_; }
 
     static const int kNoRegister = -1;
 
   private:
     EndNode* accept_;
     int next_register_;
     js::Vector<RegExpNode *, 4, SystemAllocPolicy> work_list_;
     int recursion_depth_;
     RegExpMacroAssembler* macro_assembler_;
     bool ignore_case_;
     bool ascii_;
     bool reg_exp_too_big_;
     int current_expansion_factor_;
     FrequencyCollator frequency_collator_;
+    JSContext *cx_;
     LifoAlloc *alloc_;
 };
 
 class RecursionCheck
 {
   public:
     explicit RecursionCheck(RegExpCompiler* compiler) : compiler_(compiler) {
         compiler->IncrementRecursionDepth();
@@ -1482,24 +1535,25 @@ class RecursionCheck
     ~RecursionCheck() { compiler_->DecrementRecursionDepth(); }
 
   private:
     RegExpCompiler* compiler_;
 };
 
 // Attempts to compile the regexp using an Irregexp code generator.  Returns
 // a fixed array or a null handle depending on whether it succeeded.
-RegExpCompiler::RegExpCompiler(LifoAlloc *alloc, int capture_count, bool ignore_case, bool ascii)
+RegExpCompiler::RegExpCompiler(JSContext *cx, LifoAlloc *alloc, int capture_count, bool ignore_case, bool ascii)
   : next_register_(2 * (capture_count + 1)),
     recursion_depth_(0),
     ignore_case_(ignore_case),
     ascii_(ascii),
     reg_exp_too_big_(false),
     current_expansion_factor_(1),
     frequency_collator_(),
+    cx_(cx),
     alloc_(alloc)
 {
     accept_ = alloc->newInfallible<EndNode>(alloc, EndNode::ACCEPT);
     JS_ASSERT(next_register_ - 1 <= RegExpMacroAssembler::kMaxRegister);
 }
 
 RegExpCode
 RegExpCompiler::Assemble(JSContext *cx,
@@ -1539,17 +1593,17 @@ irregexp::CompilePattern(JSContext *cx, 
                          bool is_global, bool ignore_case, bool is_ascii)
 {
     if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) {
         JS_ReportError(cx, "regexp too big");
         return RegExpCode();
     }
 
     LifoAlloc &alloc = cx->tempLifoAlloc();
-    RegExpCompiler compiler(&alloc, data->capture_count, ignore_case, is_ascii);
+    RegExpCompiler compiler(cx, &alloc, data->capture_count, ignore_case, is_ascii);
 
     // Sample some characters from the middle of the string.
     static const int kSampleSize = 128;
 
     int chars_sampled = 0;
     int half_way = (sampleLength - kSampleSize) / 2;
     for (size_t i = Max(0, half_way);
          i < sampleLength && chars_sampled < kSampleSize;
@@ -2292,16 +2346,23 @@ BoyerMooreLookahead::EmitSkipInstruction
     masm->CheckBitInTable(boolean_skip_table, &cont);
     masm->AdvanceCurrentPosition(skip_distance);
     masm->JumpOrBacktrack(&again);
     masm->Bind(&cont);
 
     return true;
 }
 
+bool
+BoyerMooreLookahead::CheckOverRecursed()
+{
+    JS_CHECK_RECURSION(compiler()->cx(), compiler()->SetRegExpTooBig(); return false);
+    return true;
+}
+
 // -------------------------------------------------------------------
 // Trace
 
 bool Trace::DeferredAction::Mentions(int that)
 {
     if (action_type() == ActionNode::CLEAR_CAPTURES) {
         Interval range = static_cast<DeferredClearCaptures*>(this)->range();
         return range.Contains(that);
@@ -4441,41 +4502,44 @@ RegExpNode::EmitQuickCheck(RegExpCompile
             assembler->CheckNotCharacterAfterAnd(value, mask, trace->backtrack());
         } else {
             assembler->CheckNotCharacter(value, trace->backtrack());
         }
     }
     return true;
 }
 
-void
+bool
 TextNode::FillInBMInfo(int initial_offset,
                        int budget,
                        BoyerMooreLookahead* bm,
                        bool not_at_start)
 {
+    if (!bm->CheckOverRecursed())
+        return false;
+
     if (initial_offset >= bm->length())
-        return;
+        return true;
 
     int offset = initial_offset;
     int max_char = bm->max_char();
     for (size_t i = 0; i < elements().length(); i++) {
         if (offset >= bm->length()) {
             if (initial_offset == 0)
                 set_bm_info(not_at_start, bm);
-            return;
+            return true;
         }
         TextElement text = elements()[i];
         if (text.text_type() == TextElement::ATOM) {
             RegExpAtom* atom = text.atom();
             for (int j = 0; j < atom->length(); j++, offset++) {
                 if (offset >= bm->length()) {
                     if (initial_offset == 0)
                         set_bm_info(not_at_start, bm);
-                    return;
+                    return true;
                 }
                 jschar character = atom->data()[j];
                 if (bm->compiler()->ignore_case()) {
                     jschar chars[kEcma262UnCanonicalizeMaxWidth];
                     int length = GetCaseIndependentLetters(character,
                                                            bm->max_char() == kMaxOneByteCharCode,
                                                            chars);
                     for (int j = 0; j < length; j++)
@@ -4499,24 +4563,26 @@ TextNode::FillInBMInfo(int initial_offse
                     bm->SetInterval(offset, Interval(range.from(), to));
                 }
             }
             offset++;
         }
     }
     if (offset >= bm->length()) {
         if (initial_offset == 0) set_bm_info(not_at_start, bm);
-        return;
+        return true;
     }
-    on_success()->FillInBMInfo(offset,
-                               budget - 1,
-                               bm,
-                               true);  // Not at start after a text node.
+    if (!on_success()->FillInBMInfo(offset,
+                                    budget - 1,
+                                    bm,
+                                    true))   // Not at start after a text node.
+        return false;
     if (initial_offset == 0)
         set_bm_info(not_at_start, bm);
+    return true;
 }
 
 // -------------------------------------------------------------------
 // QuickCheckDetails
 
 // Takes the left-most 1-bit and smears it out, setting all bits to its right.
 static inline uint32_t
 SmearBitsRight(uint32_t v)
--- a/js/src/irregexp/RegExpEngine.h
+++ b/js/src/irregexp/RegExpEngine.h
@@ -515,17 +515,17 @@ class RegExpNode
 
     static const int kRecursionBudget = 200;
 
     // Collects information on the possible code units (mod 128) that can match if
     // we look forward.  This is used for a Boyer-Moore-like string searching
     // implementation.  TODO(erikcorry):  This should share more code with
     // EatsAtLeast, GetQuickCheckDetails.  The budget argument is used to limit
     // the number of nodes we are willing to look at in order to create this data.
-    virtual void FillInBMInfo(int offset,
+    virtual bool FillInBMInfo(int offset,
                               int budget,
                               BoyerMooreLookahead* bm,
                               bool not_at_start) {
         MOZ_ASSUME_UNREACHABLE("Bad call");
     }
 
     // If we know that the input is ASCII then there are some nodes that can
     // never match.  This method returns a node that can be substituted for
@@ -633,23 +633,20 @@ class SeqRegExpNode : public RegExpNode
   public:
     explicit SeqRegExpNode(RegExpNode* on_success)
       : RegExpNode(on_success->alloc()), on_success_(on_success)
     {}
 
     RegExpNode* on_success() { return on_success_; }
     void set_on_success(RegExpNode* node) { on_success_ = node; }
     virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
-    virtual void FillInBMInfo(int offset,
+    virtual bool FillInBMInfo(int offset,
                               int budget,
                               BoyerMooreLookahead* bm,
-                              bool not_at_start) {
-        on_success_->FillInBMInfo(offset, budget - 1, bm, not_at_start);
-        if (offset == 0) set_bm_info(not_at_start, bm);
-    }
+                              bool not_at_start);
 
   protected:
     RegExpNode* FilterSuccessor(int depth, bool ignore_case);
 
   private:
     RegExpNode* on_success_;
 };
 
@@ -694,17 +691,17 @@ class ActionNode : public SeqRegExpNode
     virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start);
     virtual void GetQuickCheckDetails(QuickCheckDetails* details,
                                       RegExpCompiler* compiler,
                                       int filled_in,
                                       bool not_at_start) {
         return on_success()->GetQuickCheckDetails(
                                                   details, compiler, filled_in, not_at_start);
     }
-    virtual void FillInBMInfo(int offset,
+    virtual bool FillInBMInfo(int offset,
                               int budget,
                               BoyerMooreLookahead* bm,
                               bool not_at_start);
     ActionType action_type() { return action_type_; }
     // TODO(erikcorry): We should allow some action nodes in greedy loops.
     virtual int GreedyLoopTextLength() { return kNodeIsTooComplexForGreedyLoops; }
 
   private:
@@ -764,17 +761,17 @@ class TextNode : public SeqRegExpNode
                                       RegExpCompiler* compiler,
                                       int characters_filled_in,
                                       bool not_at_start);
     TextElementVector &elements() { return *elements_; }
     void MakeCaseIndependent(bool is_ascii);
     virtual int GreedyLoopTextLength();
     virtual RegExpNode* GetSuccessorOfOmnivorousTextNode(
                                                          RegExpCompiler* compiler);
-    virtual void FillInBMInfo(int offset,
+    virtual bool FillInBMInfo(int offset,
                               int budget,
                               BoyerMooreLookahead* bm,
                               bool not_at_start);
     void CalculateOffsets();
     virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
 
   private:
     enum TextEmitPassType {
@@ -828,17 +825,17 @@ class AssertionNode : public SeqRegExpNo
     }
     virtual void Accept(NodeVisitor* visitor);
     virtual void Emit(RegExpCompiler* compiler, Trace* trace);
     virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start);
     virtual void GetQuickCheckDetails(QuickCheckDetails* details,
                                       RegExpCompiler* compiler,
                                       int filled_in,
                                       bool not_at_start);
-    virtual void FillInBMInfo(int offset,
+    virtual bool FillInBMInfo(int offset,
                               int budget,
                               BoyerMooreLookahead* bm,
                               bool not_at_start);
     AssertionType assertion_type() { return assertion_type_; }
 
   private:
     void EmitBoundaryCheck(RegExpCompiler* compiler, Trace* trace);
     enum IfPrevious { kIsNonWord, kIsWord };
@@ -867,17 +864,17 @@ class BackReferenceNode : public SeqRegE
                             int recursion_depth,
                             bool not_at_start);
     virtual void GetQuickCheckDetails(QuickCheckDetails* details,
                                       RegExpCompiler* compiler,
                                       int characters_filled_in,
                                       bool not_at_start) {
         return;
     }
-    virtual void FillInBMInfo(int offset,
+    virtual bool FillInBMInfo(int offset,
                               int budget,
                               BoyerMooreLookahead* bm,
                               bool not_at_start);
 
   private:
     int start_reg_;
     int end_reg_;
 };
@@ -899,17 +896,17 @@ class EndNode : public RegExpNode
     virtual void GetQuickCheckDetails(QuickCheckDetails* details,
                                       RegExpCompiler* compiler,
                                       int characters_filled_in,
                                       bool not_at_start)
     {
         // Returning 0 from EatsAtLeast should ensure we never get here.
         MOZ_ASSUME_UNREACHABLE("Bad call");
     }
-    virtual void FillInBMInfo(int offset,
+    virtual bool FillInBMInfo(int offset,
                               int budget,
                               BoyerMooreLookahead* bm,
                               bool not_at_start) {
         // Returning 0 from EatsAtLeast should ensure we never get here.
         MOZ_ASSUME_UNREACHABLE("Bad call");
     }
 
   private:
@@ -1008,17 +1005,17 @@ class ChoiceNode : public RegExpNode
     int EatsAtLeastHelper(int still_to_find,
                           int budget,
                           RegExpNode* ignore_this_node,
                           bool not_at_start);
     virtual void GetQuickCheckDetails(QuickCheckDetails* details,
                                       RegExpCompiler* compiler,
                                       int characters_filled_in,
                                       bool not_at_start);
-    virtual void FillInBMInfo(int offset,
+    virtual bool FillInBMInfo(int offset,
                               int budget,
                               BoyerMooreLookahead* bm,
                               bool not_at_start);
 
     bool being_calculated() { return being_calculated_; }
     bool not_at_start() { return not_at_start_; }
     void set_not_at_start() { not_at_start_ = true; }
     void set_being_calculated(bool b) { being_calculated_ = b; }
@@ -1060,25 +1057,20 @@ class NegativeLookaheadChoiceNode : publ
         AddAlternative(this_must_fail);
         AddAlternative(then_do_this);
     }
     virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start);
     virtual void GetQuickCheckDetails(QuickCheckDetails* details,
                                       RegExpCompiler* compiler,
                                       int characters_filled_in,
                                       bool not_at_start);
-    virtual void FillInBMInfo(int offset,
+    virtual bool FillInBMInfo(int offset,
                               int budget,
                               BoyerMooreLookahead* bm,
-                              bool not_at_start)
-    {
-        alternatives()[1].node()->FillInBMInfo(offset, budget - 1, bm, not_at_start);
-        if (offset == 0)
-            set_bm_info(not_at_start, bm);
-    }
+                              bool not_at_start);
 
     // For a negative lookahead we don't emit the quick check for the
     // alternative that is expected to fail.  This is because quick check code
     // starts by loading enough characters for the alternative that takes fewest
     // characters, but on a negative lookahead the negative branch did not take
     // part in that calculation (EatsAtLeast) so the assumptions don't hold.
     virtual bool try_to_emit_quick_check_for_alternative(int i) { return i != 0; }
     virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
@@ -1097,17 +1089,17 @@ class LoopChoiceNode : public ChoiceNode
     void AddLoopAlternative(GuardedAlternative alt);
     void AddContinueAlternative(GuardedAlternative alt);
     virtual void Emit(RegExpCompiler* compiler, Trace* trace);
     virtual int EatsAtLeast(int still_to_find,  int budget, bool not_at_start);
     virtual void GetQuickCheckDetails(QuickCheckDetails* details,
                                       RegExpCompiler* compiler,
                                       int characters_filled_in,
                                       bool not_at_start);
-    virtual void FillInBMInfo(int offset,
+    virtual bool FillInBMInfo(int offset,
                               int budget,
                               BoyerMooreLookahead* bm,
                               bool not_at_start);
     RegExpNode* loop_node() { return loop_node_; }
     RegExpNode* continue_node() { return continue_node_; }
     bool body_can_be_zero_length() { return body_can_be_zero_length_; }
     virtual void Accept(NodeVisitor* visitor);
     virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
@@ -1243,16 +1235,18 @@ class BoyerMooreLookahead
         bitmaps_[map_number]->SetAll();
     }
 
     void SetRest(int from_map) {
         for (int i = from_map; i < length_; i++) SetAll(i);
     }
     bool EmitSkipInstructions(RegExpMacroAssembler* masm);
 
+    bool CheckOverRecursed();
+
   private:
     // This is the value obtained by EatsAtLeast.  If we do not have at least this
     // many characters left in the sample string then the match is bound to fail.
     // Therefore it is OK to read a character this far ahead of the current match
     // point.
     int length_;
     RegExpCompiler* compiler_;