Added clearing of captures before entering the body of a loop. This
authorchristian.plesner.hansen@gmail.com <christian.plesner.hansen@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 14 Jan 2009 11:32:23 +0000 (11:32 +0000)
committerchristian.plesner.hansen@gmail.com <christian.plesner.hansen@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 14 Jan 2009 11:32:23 +0000 (11:32 +0000)
also revealed a bug or two that had to be fixed.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1070 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

18 files changed:
src/ast.cc
src/ast.h
src/jsregexp.cc
src/jsregexp.h
src/regexp-macro-assembler-ia32.cc
src/regexp-macro-assembler-ia32.h
src/regexp-macro-assembler-irregexp.cc
src/regexp-macro-assembler-irregexp.h
src/regexp-macro-assembler-tracer.cc
src/regexp-macro-assembler-tracer.h
src/regexp-macro-assembler.h
test/cctest/test-regexp.cc
test/mjsunit/bugs/bug-176.js [deleted file]
test/mjsunit/bugs/bug-187.js [new file with mode: 0644]
test/mjsunit/mjsunit.js
test/mjsunit/mjsunit.status
test/mjsunit/regexp-loop-capture.js [new file with mode: 0644]
test/mjsunit/regress/regress-176.js [new file with mode: 0644]

index 1433fe7ad07bbc5d153d4208d319b98a7e3d38dd..5034151efd607de55ccc62dfdd3d888178548b84 100644 (file)
@@ -210,6 +210,40 @@ FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE)
 RegExpEmpty RegExpEmpty::kInstance;
 
 
+static Interval ListCaptureRegisters(ZoneList<RegExpTree*>* children) {
+  Interval result = Interval::Empty();
+  for (int i = 0; i < children->length(); i++)
+    result = result.Union(children->at(i)->CaptureRegisters());
+  return result;
+}
+
+
+Interval RegExpAlternative::CaptureRegisters() {
+  return ListCaptureRegisters(nodes());
+}
+
+
+Interval RegExpDisjunction::CaptureRegisters() {
+  return ListCaptureRegisters(alternatives());
+}
+
+
+Interval RegExpLookahead::CaptureRegisters() {
+  return body()->CaptureRegisters();
+}
+
+
+Interval RegExpCapture::CaptureRegisters() {
+  Interval self(StartRegister(index()), EndRegister(index()));
+  return self.Union(body()->CaptureRegisters());
+}
+
+
+Interval RegExpQuantifier::CaptureRegisters() {
+  return body()->CaptureRegisters();
+}
+
+
 // Convert regular expression trees to a simple sexp representation.
 // This representation should be different from the input grammar
 // in as many cases as possible, to make it more difficult for incorrect
index 150506b4240bf2e43e96da5e86dc7d639141a32e..427fe7a1f946fa987a85f2c26945178274f2cc99 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1214,6 +1214,16 @@ class ThisFunction: public Expression {
 // Regular expressions
 
 
+class RegExpVisitor BASE_EMBEDDED {
+ public:
+  virtual ~RegExpVisitor() { }
+#define MAKE_CASE(Name)                                              \
+  virtual void* Visit##Name(RegExp##Name*, void* data) = 0;
+  FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
+#undef MAKE_CASE
+};
+
+
 class RegExpTree: public ZoneObject {
  public:
   static const int kInfinity = kMaxInt;
@@ -1224,6 +1234,9 @@ class RegExpTree: public ZoneObject {
   virtual bool IsTextElement() { return false; }
   virtual int min_match() = 0;
   virtual int max_match() = 0;
+  // Returns the interval of registers used for captures within this
+  // expression.
+  virtual Interval CaptureRegisters() { return Interval::Empty(); }
   virtual void AppendToText(RegExpText* text);
   SmartPointer<const char> ToString();
 #define MAKE_ASTYPE(Name)                                                  \
@@ -1241,6 +1254,7 @@ class RegExpDisjunction: public RegExpTree {
   virtual RegExpNode* ToNode(RegExpCompiler* compiler,
                              RegExpNode* on_success);
   virtual RegExpDisjunction* AsDisjunction();
+  virtual Interval CaptureRegisters();
   virtual bool IsDisjunction();
   virtual int min_match() { return min_match_; }
   virtual int max_match() { return max_match_; }
@@ -1259,6 +1273,7 @@ class RegExpAlternative: public RegExpTree {
   virtual RegExpNode* ToNode(RegExpCompiler* compiler,
                              RegExpNode* on_success);
   virtual RegExpAlternative* AsAlternative();
+  virtual Interval CaptureRegisters();
   virtual bool IsAlternative();
   virtual int min_match() { return min_match_; }
   virtual int max_match() { return max_match_; }
@@ -1423,6 +1438,7 @@ class RegExpQuantifier: public RegExpTree {
                             RegExpCompiler* compiler,
                             RegExpNode* on_success);
   virtual RegExpQuantifier* AsQuantifier();
+  virtual Interval CaptureRegisters();
   virtual bool IsQuantifier();
   virtual int min_match() { return min_match_; }
   virtual int max_match() { return max_match_; }
@@ -1458,6 +1474,7 @@ class RegExpCapture: public RegExpTree {
                             RegExpCompiler* compiler,
                             RegExpNode* on_success);
   virtual RegExpCapture* AsCapture();
+  virtual Interval CaptureRegisters();
   virtual bool IsCapture();
   virtual int min_match() { return body_->min_match(); }
   virtual int max_match() { return body_->max_match(); }
@@ -1485,6 +1502,7 @@ class RegExpLookahead: public RegExpTree {
   virtual RegExpNode* ToNode(RegExpCompiler* compiler,
                              RegExpNode* on_success);
   virtual RegExpLookahead* AsLookahead();
+  virtual Interval CaptureRegisters();
   virtual bool IsLookahead();
   virtual int min_match() { return 0; }
   virtual int max_match() { return 0; }
@@ -1505,7 +1523,7 @@ class RegExpBackReference: public RegExpTree {
                              RegExpNode* on_success);
   virtual RegExpBackReference* AsBackReference();
   virtual bool IsBackReference();
-  virtual int min_match() { return capture_->min_match(); }
+  virtual int min_match() { return 0; }
   virtual int max_match() { return capture_->max_match(); }
   int index() { return capture_->index(); }
   RegExpCapture* capture() { return capture_; }
@@ -1530,16 +1548,6 @@ class RegExpEmpty: public RegExpTree {
 };
 
 
-class RegExpVisitor BASE_EMBEDDED {
- public:
-  virtual ~RegExpVisitor() { }
-#define MAKE_CASE(Name)                                              \
-  virtual void* Visit##Name(RegExp##Name*, void* data) = 0;
-  FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
-#undef MAKE_CASE
-};
-
-
 // ----------------------------------------------------------------------------
 // Basic visitor
 // - leaf node visitors are abstract.
index 37ba35f7343f02e09285c45c66c8abb255be6422..d0c3b21cfbf297cbc00bd4ce820b161795ef2e2c 100644 (file)
@@ -1304,12 +1304,22 @@ Handle<FixedArray> RegExpCompiler::Assemble(
   return array;
 }
 
+bool GenerationVariant::DeferredAction::Mentions(int that) {
+  if (type() == ActionNode::CLEAR_CAPTURES) {
+    Interval range = static_cast<DeferredClearCaptures*>(this)->range();
+    return range.Contains(that);
+  } else {
+    return reg() == that;
+  }
+}
+
 
 bool GenerationVariant::mentions_reg(int reg) {
   for (DeferredAction* action = actions_;
        action != NULL;
        action = action->next()) {
-    if (reg == action->reg()) return true;
+    if (action->Mentions(reg))
+      return true;
   }
   return false;
 }
@@ -1320,7 +1330,7 @@ bool GenerationVariant::GetStoredPosition(int reg, int* cp_offset) {
   for (DeferredAction* action = actions_;
        action != NULL;
        action = action->next()) {
-    if (reg == action->reg()) {
+    if (action->Mentions(reg)) {
       if (action->type() == ActionNode::STORE_POSITION) {
         *cp_offset = static_cast<DeferredCapture*>(action)->cp_offset();
         return true;
@@ -1338,8 +1348,15 @@ int GenerationVariant::FindAffectedRegisters(OutSet* affected_registers) {
   for (DeferredAction* action = actions_;
        action != NULL;
        action = action->next()) {
-    affected_registers->Set(action->reg());
-    if (action->reg() > max_register) max_register = action->reg();
+    if (action->type() == ActionNode::CLEAR_CAPTURES) {
+      Interval range = static_cast<DeferredClearCaptures*>(action)->range();
+      for (int i = range.from(); i <= range.to(); i++)
+        affected_registers->Set(i);
+      if (range.to() > max_register) max_register = range.to();
+    } else {
+      affected_registers->Set(action->reg());
+      if (action->reg() > max_register) max_register = action->reg();
+    }
   }
   return max_register;
 }
@@ -1383,13 +1400,14 @@ void GenerationVariant::PerformDeferredActions(RegExpMacroAssembler* assembler,
     }
     int value = 0;
     bool absolute = false;
+    bool clear = false;
     int store_position = -1;
     // This is a little tricky because we are scanning the actions in reverse
     // historical order (newest first).
     for (DeferredAction* action = actions_;
          action != NULL;
          action = action->next()) {
-      if (action->reg() == reg) {
+      if (action->Mentions(reg)) {
         switch (action->type()) {
           case ActionNode::SET_REGISTER: {
             GenerationVariant::DeferredSetRegister* psr =
@@ -1397,6 +1415,7 @@ void GenerationVariant::PerformDeferredActions(RegExpMacroAssembler* assembler,
             value += psr->value();
             absolute = true;
             ASSERT_EQ(store_position, -1);
+            ASSERT(!clear);
             break;
           }
           case ActionNode::INCREMENT_REGISTER:
@@ -1404,17 +1423,28 @@ void GenerationVariant::PerformDeferredActions(RegExpMacroAssembler* assembler,
               value++;
             }
             ASSERT_EQ(store_position, -1);
+            ASSERT(!clear);
             break;
           case ActionNode::STORE_POSITION: {
             GenerationVariant::DeferredCapture* pc =
                 static_cast<GenerationVariant::DeferredCapture*>(action);
-            if (store_position == -1) {
+            if (!clear && store_position == -1) {
               store_position = pc->cp_offset();
             }
             ASSERT(!absolute);
             ASSERT_EQ(value, 0);
             break;
           }
+          case ActionNode::CLEAR_CAPTURES: {
+            // Since we're scanning in reverse order, if we've already
+            // set the position we have to ignore historically earlier
+            // clearing operations.
+            if (store_position == -1)
+              clear = true;
+            ASSERT(!absolute);
+            ASSERT_EQ(value, 0);
+            break;
+          }
           default:
             UNREACHABLE();
             break;
@@ -1423,14 +1453,12 @@ void GenerationVariant::PerformDeferredActions(RegExpMacroAssembler* assembler,
     }
     if (store_position != -1) {
       assembler->WriteCurrentPositionToRegister(reg, store_position);
-    } else {
-      if (absolute) {
-        assembler->SetRegister(reg, value);
-      } else {
-        if (value != 0) {
-          assembler->AdvanceRegister(reg, value);
-        }
-      }
+    } else if (clear) {
+      assembler->ClearRegister(reg);
+    } else if (absolute) {
+      assembler->SetRegister(reg, value);
+    } else if (value != 0) {
+      assembler->AdvanceRegister(reg, value);
     }
   }
 }
@@ -1586,6 +1614,15 @@ ActionNode* ActionNode::StorePosition(int reg, RegExpNode* on_success) {
 }
 
 
+ActionNode* ActionNode::ClearCaptures(Interval range,
+                                      RegExpNode* on_success) {
+  ActionNode* result = new ActionNode(CLEAR_CAPTURES, on_success);
+  result->data_.u_clear_captures.range_from = range.from();
+  result->data_.u_clear_captures.range_to = range.to();
+  return result;
+}
+
+
 ActionNode* ActionNode::BeginSubmatch(int stack_reg,
                                       int position_reg,
                                       RegExpNode* on_success) {
@@ -2267,10 +2304,25 @@ void QuickCheckDetails::Merge(QuickCheckDetails* other, int from_index) {
 }
 
 
+class VisitMarker {
+ public:
+  explicit VisitMarker(NodeInfo* info) : info_(info) {
+    ASSERT(!info->visited);
+    info->visited = true;
+  }
+  ~VisitMarker() {
+    info_->visited = false;
+  }
+ private:
+  NodeInfo* info_;
+};
+
+
 void LoopChoiceNode::GetQuickCheckDetails(QuickCheckDetails* details,
                                           RegExpCompiler* compiler,
                                           int characters_filled_in) {
-  if (body_can_be_zero_length_) return;
+  if (body_can_be_zero_length_ || info()->visited) return;
+  VisitMarker marker(info());
   return ChoiceNode::GetQuickCheckDetails(details,
                                           compiler,
                                           characters_filled_in);
@@ -2843,7 +2895,7 @@ bool ChoiceNode::Emit(RegExpCompiler* compiler, GenerationVariant* variant) {
   // is to use the Dispatch table to try only the relevant ones.
   for (int i = first_normal_choice; i < choice_count; i++) {
     GuardedAlternative alternative = alternatives_->at(i);
-    AlternativeGeneration* alt_gen(alt_gens.at(i));
+    AlternativeGeneration* alt_gen = alt_gens.at(i);
     alt_gen->quick_check_details.set_characters(preload_characters);
     ZoneList<Guard*>* guards = alternative.guards();
     int guard_count = (guards == NULL) ? 0 : guards->length();
@@ -3002,6 +3054,14 @@ bool ActionNode::Emit(RegExpCompiler* compiler, GenerationVariant* variant) {
       new_variant.add_action(&new_set);
       return on_success()->Emit(compiler, &new_variant);
     }
+    case CLEAR_CAPTURES: {
+      GenerationVariant::DeferredClearCaptures
+        new_capture(Interval(data_.u_clear_captures.range_from,
+                             data_.u_clear_captures.range_to));
+      GenerationVariant new_variant = *variant;
+      new_variant.add_action(&new_capture);
+      return on_success()->Emit(compiler, &new_variant);
+    }
     case BEGIN_SUBMATCH:
       if (!variant->is_trivial()) return variant->Flush(compiler, this);
       assembler->WriteCurrentPositionToRegister(
@@ -3365,6 +3425,12 @@ void DotPrinter::VisitAction(ActionNode* that) {
                     that->data_.u_empty_match_check.repetition_register,
                     that->data_.u_empty_match_check.repetition_limit);
       break;
+    case ActionNode::CLEAR_CAPTURES: {
+      stream()->Add("label=\"clear $%i to $%i\", shape=septagon",
+                    that->data_.u_clear_captures.range_from,
+                    that->data_.u_clear_captures.range_to);
+      break;
+    }
   }
   stream()->Add("];\n");
   PrintAttributes(that);
@@ -3592,9 +3658,13 @@ RegExpNode* RegExpQuantifier::ToNode(int min,
   if (max == 0) return on_success;  // This can happen due to recursion.
   bool body_can_be_empty = (body->min_match() == 0);
   int body_start_reg = RegExpCompiler::kNoRegister;
+  Interval capture_registers = body->CaptureRegisters();
+  bool needs_capture_clearing = !capture_registers.is_empty();
   if (body_can_be_empty) {
     body_start_reg = compiler->AllocateRegister();
-  } else {
+  } else if (!needs_capture_clearing) {
+    // Only unroll if there are no captures and the body can't be
+    // empty.
     if (min > 0 && min <= kMaxUnrolledMinMatches) {
       int new_max = (max == kInfinity) ? max : max - min;
       // Recurse once to get the loop or optional matches after the fixed ones.
@@ -3652,6 +3722,10 @@ RegExpNode* RegExpQuantifier::ToNode(int min,
     // so we can bail out if it was empty.
     body_node = ActionNode::StorePosition(body_start_reg, body_node);
   }
+  if (needs_capture_clearing) {
+    // Before entering the body of this loop we need to clear captures.
+    body_node = ActionNode::ClearCaptures(capture_registers, body_node);
+  }
   GuardedAlternative body_alt(body_node);
   if (has_max) {
     Guard* body_guard = new Guard(reg_ctr, Guard::LT, max);
index de99dc71ad506eefdc1937151a22ee988065799b..cadf7f2cacf8823d479db5f256aef3f728999811 100644 (file)
@@ -664,6 +664,33 @@ class RegExpNode: public ZoneObject {
 };
 
 
+// A simple closed interval.
+class Interval {
+ public:
+  Interval() : from_(kNone), to_(kNone) { }
+  Interval(int from, int to) : from_(from), to_(to) { }
+  Interval Union(Interval that) {
+    if (that.from_ == kNone)
+      return *this;
+    else if (from_ == kNone)
+      return that;
+    else
+      return Interval(Min(from_, that.from_), Max(to_, that.to_));
+  }
+  bool Contains(int value) {
+    return (from_ <= value) && (value <= to_);
+  }
+  bool is_empty() { return from_ == kNone; }
+  int from() { return from_; }
+  int to() { return to_; }
+  static Interval Empty() { return Interval(); }
+  static const int kNone = -1;
+ private:
+  int from_;
+  int to_;
+};
+
+
 class SeqRegExpNode: public RegExpNode {
  public:
   explicit SeqRegExpNode(RegExpNode* on_success)
@@ -683,24 +710,23 @@ class ActionNode: public SeqRegExpNode {
     STORE_POSITION,
     BEGIN_SUBMATCH,
     POSITIVE_SUBMATCH_SUCCESS,
-    EMPTY_MATCH_CHECK
+    EMPTY_MATCH_CHECK,
+    CLEAR_CAPTURES
   };
   static ActionNode* SetRegister(int reg, int val, RegExpNode* on_success);
   static ActionNode* IncrementRegister(int reg, RegExpNode* on_success);
   static ActionNode* StorePosition(int reg, RegExpNode* on_success);
-  static ActionNode* BeginSubmatch(
-      int stack_pointer_reg,
-      int position_reg,
-      RegExpNode* on_success);
-  static ActionNode* PositiveSubmatchSuccess(
-      int stack_pointer_reg,
-      int restore_reg,
-      RegExpNode* on_success);
-  static ActionNode* EmptyMatchCheck(
-      int start_register,
-      int repetition_register,
-      int repetition_limit,
-      RegExpNode* on_success);
+  static ActionNode* ClearCaptures(Interval range, RegExpNode* on_success);
+  static ActionNode* BeginSubmatch(int stack_pointer_reg,
+                                   int position_reg,
+                                   RegExpNode* on_success);
+  static ActionNode* PositiveSubmatchSuccess(int stack_pointer_reg,
+                                             int restore_reg,
+                                             RegExpNode* on_success);
+  static ActionNode* EmptyMatchCheck(int start_register,
+                                     int repetition_register,
+                                     int repetition_limit,
+                                     RegExpNode* on_success);
   virtual void Accept(NodeVisitor* visitor);
   virtual bool Emit(RegExpCompiler* compiler, GenerationVariant* variant);
   virtual int EatsAtLeast(int recursion_depth);
@@ -736,6 +762,10 @@ class ActionNode: public SeqRegExpNode {
       int repetition_register;
       int repetition_limit;
     } u_empty_match_check;
+    struct {
+      int range_from;
+      int range_to;
+    } u_clear_captures;
   } data_;
   ActionNode(Type type, RegExpNode* on_success)
       : SeqRegExpNode(on_success),
@@ -980,6 +1010,7 @@ class GenerationVariant {
     DeferredAction(ActionNode::Type type, int reg)
         : type_(type), reg_(reg), next_(NULL) { }
     DeferredAction* next() { return next_; }
+    bool Mentions(int reg);
     int reg() { return reg_; }
     ActionNode::Type type() { return type_; }
    private:
@@ -1010,6 +1041,16 @@ class GenerationVariant {
     int value_;
   };
 
+  class DeferredClearCaptures : public DeferredAction {
+   public:
+    explicit DeferredClearCaptures(Interval range)
+        : DeferredAction(ActionNode::CLEAR_CAPTURES, -1),
+          range_(range) { }
+    Interval range() { return range_; }
+   private:
+    Interval range_;
+  };
+
   class DeferredIncrementRegister: public DeferredAction {
    public:
     explicit DeferredIncrementRegister(int reg)
index 7afc46d7d03489574df4575fcf0b9da071af6702..35a6e529059524182785ca456405d821f2e2111c 100644 (file)
@@ -608,6 +608,7 @@ Handle<Object> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
   __ push(esi);
   __ push(edi);
   __ push(ebx);  // Callee-save on MacOS.
+  __ push(Immediate(0));  // Make room for input start minus one
 
   // Check if we have space on the stack for registers.
   Label retry_stack_check;
@@ -669,6 +670,9 @@ Handle<Object> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
     // Set eax to address of char before start of input
     // (effectively string position -1).
     __ lea(eax, Operand(edi, -char_size()));
+    // Store this value in a local variable, for use when clearing
+    // position registers.
+    __ mov(Operand(ebp, kInputStartMinusOne), eax);
     Label init_loop;
     __ bind(&init_loop);
     __ mov(Operand(ebp, ecx, times_1, +0), eax);
@@ -928,6 +932,12 @@ void RegExpMacroAssemblerIA32::WriteCurrentPositionToRegister(int reg,
 }
 
 
+void RegExpMacroAssemblerIA32::ClearRegister(int reg) {
+  __ mov(eax, Operand(ebp, kInputStartMinusOne));
+  __ mov(register_location(reg), eax);
+}
+
+
 void RegExpMacroAssemblerIA32::WriteStackPointerToRegister(int reg) {
   __ mov(register_location(reg), backtrack_stackpointer());
 }
index 341668c92790280c733ff0bf39aa3c754a8f121e..8d28bebf3f5690f589462635914d3ccd414f9139 100644 (file)
@@ -106,6 +106,7 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler {
   virtual void SetRegister(int register_index, int to);
   virtual void Succeed();
   virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
+  virtual void ClearRegister(int reg);
   virtual void WriteStackPointerToRegister(int reg);
 
   static Result Execute(Code* code,
@@ -127,11 +128,14 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler {
   static const int kAtStart = kRegisterOutput + kPointerSize;
   static const int kStackHighEnd = kAtStart + kPointerSize;
   // Below the frame pointer - local stack variables.
+  // When adding local variables remember to push space for them in
+  // the frame in GetCode.
   static const int kBackup_esi = kFramePointer - kPointerSize;
   static const int kBackup_edi = kBackup_esi - kPointerSize;
   static const int kBackup_ebx = kBackup_edi - kPointerSize;
+  static const int kInputStartMinusOne = kBackup_ebx - kPointerSize;
   // First register address. Following registers are below it on the stack.
-  static const int kRegisterZero = kBackup_ebx - kPointerSize;
+  static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
 
   // Initial size of code buffer.
   static const size_t kRegExpCodeSize = 1024;
index d962a739b268855c631e1c24be9b31d4ffc7ea36..a36f0a766dcfca61a5ffb06e0b4b54c2ed133fe8 100644 (file)
@@ -107,6 +107,11 @@ void RegExpMacroAssemblerIrregexp::WriteCurrentPositionToRegister(
 }
 
 
+void RegExpMacroAssemblerIrregexp::ClearRegister(int reg) {
+  SetRegister(reg, -1);
+}
+
+
 void RegExpMacroAssemblerIrregexp::ReadCurrentPositionFromRegister(
     int register_index) {
   ASSERT(register_index >= 0);
index b2fba37cf6643f2c0e2d0b55ca531447dc056d03..95b903cc471a9801c69299893a09c4e9957ed546 100644 (file)
@@ -66,6 +66,7 @@ class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler {
   virtual void AdvanceRegister(int reg, int by);  // r[reg] += by.
   virtual void SetRegister(int register_index, int to);
   virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
+  virtual void ClearRegister(int reg);
   virtual void ReadCurrentPositionFromRegister(int reg);
   virtual void WriteStackPointerToRegister(int reg);
   virtual void ReadStackPointerFromRegister(int reg);
index c733a2fcc432c40e78603c7e518b31807e797650..5caf032897bd7b64bb1cc9eace902ad52c9f6b52 100644 (file)
@@ -150,6 +150,12 @@ void RegExpMacroAssemblerTracer::WriteCurrentPositionToRegister(int reg,
 }
 
 
+void RegExpMacroAssemblerTracer::ClearRegister(int reg) {
+  PrintF(" ClearRegister(register=%d);\n", reg);
+  assembler_->ClearRegister(reg);
+}
+
+
 void RegExpMacroAssemblerTracer::ReadCurrentPositionFromRegister(int reg) {
   PrintF(" ReadCurrentPositionFromRegister(register=%d);\n", reg);
   assembler_->ReadCurrentPositionFromRegister(reg);
index 7a7a4017a2f5aa58e08e9576f8130a2dcde12f29..c8088a90ecaaa8c5511c25431a2b4dd783e537c9 100644 (file)
@@ -106,6 +106,7 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler {
   virtual void SetRegister(int register_index, int to);
   virtual void Succeed();
   virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
+  virtual void ClearRegister(int reg);
   virtual void WriteStackPointerToRegister(int reg);
  private:
   RegExpMacroAssembler* assembler_;
index 4b76cab2d28a0014fb2ac48a3b9bfb635862288c..e9f7731abe2b7358ffcab5ee242802369462db50 100644 (file)
@@ -167,6 +167,7 @@ class RegExpMacroAssembler {
   virtual void SetRegister(int register_index, int to) = 0;
   virtual void Succeed() = 0;
   virtual void WriteCurrentPositionToRegister(int reg, int cp_offset) = 0;
+  virtual void ClearRegister(int reg) = 0;
   virtual void WriteStackPointerToRegister(int reg) = 0;
 
  private:
index 986785ec789749a0ce34d5343d4e34e250483702..6e61514396f8285156ba3e14dc858b31344f0cc1 100644 (file)
@@ -1548,5 +1548,5 @@ TEST(CharClassDifference) {
 
 TEST(Graph) {
   V8::Initialize(NULL);
-  Execute("(?:a|)*", false, true, true);
+  Execute("(?:(?:x(.))?\1)+$", false, true, true);
 }
diff --git a/test/mjsunit/bugs/bug-176.js b/test/mjsunit/bugs/bug-176.js
deleted file mode 100644 (file)
index b204812..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-//       notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-//       copyright notice, this list of conditions and the following
-//       disclaimer in the documentation and/or other materials provided
-//       with the distribution.
-//     * Neither the name of Google Inc. nor the names of its
-//       contributors may be used to endorse or promote products derived
-//       from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// See http://code.google.com/p/v8/issues/detail?id=176
-
-assertEquals("f,",
-             "foo".match(/(?:(?=(f)o))?f/).toString(),
-             "zero length match in (?:) with capture in lookahead");
-assertEquals("f,",
-             "foo".match(/(?=(f)o)?f/).toString(),
-             "zero length match in (?=) with capture in lookahead");
-assertEquals("fo,f",
-             "foo".match(/(?:(?=(f)o)f)?o/),
-             "non-zero length match with capture in lookahead");
-assertEquals("fo,f",
-             "foo".match(/(?:(?=(f)o)f?)?o/),
-             "non-zero length match with greedy ? in (?:)");
-assertEquals("fo,f",
-             "foo".match(/(?:(?=(f)o)f??)?o/),
-             "non-zero length match with non-greedy ? in (?:), o forces backtrack");
-assertEquals("fo,f",
-             "foo".match(/(?:(?=(f)o)f??)?./),
-             "non-zero length match with non-greedy ? in (?:), zero length match causes backtrack");
-assertEquals("f,",
-             "foo".match(/(?:(?=(f)o)fx)?./),
-             "x causes backtrack inside (?:)");
diff --git a/test/mjsunit/bugs/bug-187.js b/test/mjsunit/bugs/bug-187.js
new file mode 100644 (file)
index 0000000..44d8d7a
--- /dev/null
@@ -0,0 +1,30 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// See http://code.google.com/p/v8/issues/detail?id=187
+
+assertEquals("f,", "foo".match(/(?:(?=(f)o)fx|)./));
index 1d1e260b456a80ca44c7797d578f810cf567a503..3570d689ce550443072fca93a2bdbc68f6043ef6 100644 (file)
@@ -51,8 +51,32 @@ function fail(expected, found, name_opt) {
 }
 
 
+function deepEquals(a, b) {
+  if (a == b) return true;
+  if ((typeof a) !== 'object' || (typeof b) !== 'object' ||
+      (a === null) || (b === null))
+    return false;
+  if (a.constructor === Array) {
+    if (b.constructor !== Array)
+      return false;
+    if (a.length != b.length)
+      return false;
+    for (var i = 0; i < a.length; i++) {
+      if (i in a) {
+        if (!(i in b) || !(deepEquals(a[i], b[i])))
+          return false;
+      } else if (i in b) {
+        return false;
+      }
+    }
+    return true;
+  }
+  return false;
+}
+
+
 function assertEquals(expected, found, name_opt) {
-  if (expected != found) {
+  if (!deepEquals(found, expected)) {
     fail(expected, found, name_opt);
   }
 }
index e030878f80dade58c8a2a22750722b3dc25293d3..c35430045bc33cc94d0bfd921ca80c6c745a7f25 100644 (file)
@@ -38,6 +38,10 @@ fuzz-natives: PASS, SKIP if ($mode == release || $arch == arm)
 # no longer using JSCRE.
 regexp-UC16: PASS || FAIL
 
+# These tests pass with irregexp but fail with jscre
+regress/regress-176: PASS || FAIL
+regexp-loop-capture: PASS || FAIL
+
 [ $arch == arm ]
 
 # Slow tests which times out in debug mode.
diff --git a/test/mjsunit/regexp-loop-capture.js b/test/mjsunit/regexp-loop-capture.js
new file mode 100644 (file)
index 0000000..9a0c99c
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+assertEquals(["abc",undefined,undefined,"c"], /(?:(a)|(b)|(c))+/.exec("abc"));
+assertEquals(["ab",undefined], /(?:(a)|b)*/.exec("ab"));
diff --git a/test/mjsunit/regress/regress-176.js b/test/mjsunit/regress/regress-176.js
new file mode 100644 (file)
index 0000000..b204812
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// See http://code.google.com/p/v8/issues/detail?id=176
+
+assertEquals("f,",
+             "foo".match(/(?:(?=(f)o))?f/).toString(),
+             "zero length match in (?:) with capture in lookahead");
+assertEquals("f,",
+             "foo".match(/(?=(f)o)?f/).toString(),
+             "zero length match in (?=) with capture in lookahead");
+assertEquals("fo,f",
+             "foo".match(/(?:(?=(f)o)f)?o/),
+             "non-zero length match with capture in lookahead");
+assertEquals("fo,f",
+             "foo".match(/(?:(?=(f)o)f?)?o/),
+             "non-zero length match with greedy ? in (?:)");
+assertEquals("fo,f",
+             "foo".match(/(?:(?=(f)o)f??)?o/),
+             "non-zero length match with non-greedy ? in (?:), o forces backtrack");
+assertEquals("fo,f",
+             "foo".match(/(?:(?=(f)o)f??)?./),
+             "non-zero length match with non-greedy ? in (?:), zero length match causes backtrack");
+assertEquals("f,",
+             "foo".match(/(?:(?=(f)o)fx)?./),
+             "x causes backtrack inside (?:)");