Added clearing of captures before entering the body of a loop. This
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
This commit is contained in:
parent
cd1afeaaa2
commit
d6e6508bd7
34
src/ast.cc
34
src/ast.cc
@ -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
|
||||
|
30
src/ast.h
30
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.
|
||||
|
108
src/jsregexp.cc
108
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,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);
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
|
@ -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:
|
||||
|
@ -1548,5 +1548,5 @@ TEST(CharClassDifference) {
|
||||
|
||||
TEST(Graph) {
|
||||
V8::Initialize(NULL);
|
||||
Execute("(?:a|)*", false, true, true);
|
||||
Execute("(?:(?:x(.))?\1)+$", false, true, true);
|
||||
}
|
||||
|
30
test/mjsunit/bugs/bug-187.js
Normal file
30
test/mjsunit/bugs/bug-187.js
Normal file
@ -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|)./));
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
29
test/mjsunit/regexp-loop-capture.js
Normal file
29
test/mjsunit/regexp-loop-capture.js
Normal file
@ -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"));
|
Loading…
Reference in New Issue
Block a user