From d6e6508bd703b8d75e47b4f4f9098e7b2e076b41 Mon Sep 17 00:00:00 2001 From: "christian.plesner.hansen@gmail.com" Date: Wed, 14 Jan 2009 11:32:23 +0000 Subject: [PATCH] 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 --- src/ast.cc | 34 ++++++ src/ast.h | 30 +++-- src/jsregexp.cc | 108 +++++++++++++++--- src/jsregexp.h | 69 ++++++++--- src/regexp-macro-assembler-ia32.cc | 10 ++ src/regexp-macro-assembler-ia32.h | 6 +- src/regexp-macro-assembler-irregexp.cc | 5 + src/regexp-macro-assembler-irregexp.h | 1 + src/regexp-macro-assembler-tracer.cc | 6 + src/regexp-macro-assembler-tracer.h | 1 + src/regexp-macro-assembler.h | 1 + test/cctest/test-regexp.cc | 2 +- test/mjsunit/bugs/bug-187.js | 30 +++++ test/mjsunit/mjsunit.js | 26 ++++- test/mjsunit/mjsunit.status | 4 + test/mjsunit/regexp-loop-capture.js | 29 +++++ .../bug-176.js => regress/regress-176.js} | 0 17 files changed, 317 insertions(+), 45 deletions(-) create mode 100644 test/mjsunit/bugs/bug-187.js create mode 100644 test/mjsunit/regexp-loop-capture.js rename test/mjsunit/{bugs/bug-176.js => regress/regress-176.js} (100%) diff --git a/src/ast.cc b/src/ast.cc index 1433fe7ad..5034151ef 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -210,6 +210,40 @@ FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE) RegExpEmpty RegExpEmpty::kInstance; +static Interval ListCaptureRegisters(ZoneList* 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 diff --git a/src/ast.h b/src/ast.h index 150506b42..427fe7a1f 100644 --- 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 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. diff --git a/src/jsregexp.cc b/src/jsregexp.cc index 37ba35f73..d0c3b21cf 100644 --- a/src/jsregexp.cc +++ b/src/jsregexp.cc @@ -1304,12 +1304,22 @@ Handle RegExpCompiler::Assemble( return array; } +bool GenerationVariant::DeferredAction::Mentions(int that) { + if (type() == ActionNode::CLEAR_CAPTURES) { + Interval range = static_cast(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(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(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(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* 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); diff --git a/src/jsregexp.h b/src/jsregexp.h index de99dc71a..cadf7f2ca 100644 --- a/src/jsregexp.h +++ b/src/jsregexp.h @@ -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) diff --git a/src/regexp-macro-assembler-ia32.cc b/src/regexp-macro-assembler-ia32.cc index 7afc46d7d..35a6e5290 100644 --- a/src/regexp-macro-assembler-ia32.cc +++ b/src/regexp-macro-assembler-ia32.cc @@ -608,6 +608,7 @@ Handle RegExpMacroAssemblerIA32::GetCode(Handle 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 RegExpMacroAssemblerIA32::GetCode(Handle 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()); } diff --git a/src/regexp-macro-assembler-ia32.h b/src/regexp-macro-assembler-ia32.h index 341668c92..8d28bebf3 100644 --- a/src/regexp-macro-assembler-ia32.h +++ b/src/regexp-macro-assembler-ia32.h @@ -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; diff --git a/src/regexp-macro-assembler-irregexp.cc b/src/regexp-macro-assembler-irregexp.cc index d962a739b..a36f0a766 100644 --- a/src/regexp-macro-assembler-irregexp.cc +++ b/src/regexp-macro-assembler-irregexp.cc @@ -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); diff --git a/src/regexp-macro-assembler-irregexp.h b/src/regexp-macro-assembler-irregexp.h index b2fba37cf..95b903cc4 100644 --- a/src/regexp-macro-assembler-irregexp.h +++ b/src/regexp-macro-assembler-irregexp.h @@ -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); diff --git a/src/regexp-macro-assembler-tracer.cc b/src/regexp-macro-assembler-tracer.cc index c733a2fcc..5caf03289 100644 --- a/src/regexp-macro-assembler-tracer.cc +++ b/src/regexp-macro-assembler-tracer.cc @@ -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); diff --git a/src/regexp-macro-assembler-tracer.h b/src/regexp-macro-assembler-tracer.h index 7a7a4017a..c8088a90e 100644 --- a/src/regexp-macro-assembler-tracer.h +++ b/src/regexp-macro-assembler-tracer.h @@ -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_; diff --git a/src/regexp-macro-assembler.h b/src/regexp-macro-assembler.h index 4b76cab2d..e9f7731ab 100644 --- a/src/regexp-macro-assembler.h +++ b/src/regexp-macro-assembler.h @@ -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: diff --git a/test/cctest/test-regexp.cc b/test/cctest/test-regexp.cc index 986785ec7..6e6151439 100644 --- a/test/cctest/test-regexp.cc +++ b/test/cctest/test-regexp.cc @@ -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-187.js b/test/mjsunit/bugs/bug-187.js new file mode 100644 index 000000000..44d8d7a52 --- /dev/null +++ b/test/mjsunit/bugs/bug-187.js @@ -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|)./)); diff --git a/test/mjsunit/mjsunit.js b/test/mjsunit/mjsunit.js index 1d1e260b4..3570d689c 100644 --- a/test/mjsunit/mjsunit.js +++ b/test/mjsunit/mjsunit.js @@ -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); } } diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status index e030878f8..c35430045 100644 --- a/test/mjsunit/mjsunit.status +++ b/test/mjsunit/mjsunit.status @@ -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 index 000000000..9a0c99cee --- /dev/null +++ b/test/mjsunit/regexp-loop-capture.js @@ -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/bugs/bug-176.js b/test/mjsunit/regress/regress-176.js similarity index 100% rename from test/mjsunit/bugs/bug-176.js rename to test/mjsunit/regress/regress-176.js -- 2.34.1