Chromium Code Reviews| Index: src/jsregexp.cc |
| diff --git a/src/jsregexp.cc b/src/jsregexp.cc |
| index 37ba35f7343f02e09285c45c66c8abb255be6422..0653dfe00a774005d9d834ff8f20fc1cc0f81c89 100644 |
| --- a/src/jsregexp.cc |
| +++ b/src/jsregexp.cc |
| @@ -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,6 +1423,7 @@ void GenerationVariant::PerformDeferredActions(RegExpMacroAssembler* assembler, |
| value++; |
| } |
| ASSERT_EQ(store_position, -1); |
| + ASSERT(!clear); |
| break; |
| case ActionNode::STORE_POSITION: { |
|
Erik Corry
2009/01/14 09:24:31
We should be ignoring a store position that we fin
Christian Plesner Hansen
2009/01/14 11:31:35
Fixed
|
| GenerationVariant::DeferredCapture* pc = |
| @@ -1415,6 +1435,12 @@ void GenerationVariant::PerformDeferredActions(RegExpMacroAssembler* assembler, |
| ASSERT_EQ(value, 0); |
| break; |
| } |
| + case ActionNode::CLEAR_CAPTURES: { |
|
Erik Corry
2009/01/14 09:24:31
...and we should be ignoring a clear that we find
Christian Plesner Hansen
2009/01/14 11:31:35
Fixed
|
| + clear = true; |
| + ASSERT(!absolute); |
| + ASSERT_EQ(value, 0); |
| + break; |
| + } |
| default: |
| UNREACHABLE(); |
| break; |
| @@ -1423,14 +1449,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 +1610,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 +2300,25 @@ void QuickCheckDetails::Merge(QuickCheckDetails* other, int from_index) { |
| } |
| +class VisitMarker { |
| + public: |
| + 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 +2891,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 +3050,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 +3421,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 +3654,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) { |
|
Erik Corry
2009/01/14 09:24:31
Surely it's OK to unroll and no need to clear capt
|
| + // 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 +3718,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); |