Commit f80da64d authored by lrn@chromium.org's avatar lrn@chromium.org

Use finite-length end-anchored regexps to reduce part of regexp that is searched.

Review URL: http://codereview.chromium.org/3850005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5657 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent d4ae5265
...@@ -142,7 +142,6 @@ int RegExpMacroAssemblerARM::stack_limit_slack() { ...@@ -142,7 +142,6 @@ int RegExpMacroAssemblerARM::stack_limit_slack() {
void RegExpMacroAssemblerARM::AdvanceCurrentPosition(int by) { void RegExpMacroAssemblerARM::AdvanceCurrentPosition(int by) {
if (by != 0) { if (by != 0) {
Label inside_string;
__ add(current_input_offset(), __ add(current_input_offset(),
current_input_offset(), Operand(by * char_size())); current_input_offset(), Operand(by * char_size()));
} }
...@@ -927,6 +926,19 @@ void RegExpMacroAssemblerARM::ReadStackPointerFromRegister(int reg) { ...@@ -927,6 +926,19 @@ void RegExpMacroAssemblerARM::ReadStackPointerFromRegister(int reg) {
} }
void RegExpMacroAssemblerARM::SetCurrentPositionFromEnd(int by) {
NearLabel after_position;
__ cmp(current_input_offset(), Operand(by * char_size()));
__ b(hs, &after_position);
__ mov(current_input_offset(), Operand(by * char_size()));
// On RegExp code entry (where this operation is used), the character before
// the current position is expected to be already loaded.
// We have advenced the position, so it's safe to read backwards.
LoadCurrentCharacterUnchecked(-1, 1);
__ bind(&after_position);
}
void RegExpMacroAssemblerARM::SetRegister(int register_index, int to) { void RegExpMacroAssemblerARM::SetRegister(int register_index, int to) {
ASSERT(register_index >= num_saved_registers_); // Reserved for positions! ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
__ mov(r0, Operand(to)); __ mov(r0, Operand(to));
......
...@@ -100,6 +100,7 @@ class RegExpMacroAssemblerARM: public NativeRegExpMacroAssembler { ...@@ -100,6 +100,7 @@ class RegExpMacroAssemblerARM: public NativeRegExpMacroAssembler {
StackCheckFlag check_stack_limit); StackCheckFlag check_stack_limit);
virtual void ReadCurrentPositionFromRegister(int reg); virtual void ReadCurrentPositionFromRegister(int reg);
virtual void ReadStackPointerFromRegister(int reg); virtual void ReadStackPointerFromRegister(int reg);
virtual void SetCurrentPositionFromEnd(int by);
virtual void SetRegister(int register_index, int to); virtual void SetRegister(int register_index, int to);
virtual void Succeed(); virtual void Succeed();
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
......
...@@ -398,39 +398,70 @@ Interval RegExpQuantifier::CaptureRegisters() { ...@@ -398,39 +398,70 @@ Interval RegExpQuantifier::CaptureRegisters() {
} }
bool RegExpAssertion::IsAnchored() { bool RegExpAssertion::IsAnchoredAtStart() {
return type() == RegExpAssertion::START_OF_INPUT; return type() == RegExpAssertion::START_OF_INPUT;
} }
bool RegExpAlternative::IsAnchored() { bool RegExpAssertion::IsAnchoredAtEnd() {
return type() == RegExpAssertion::END_OF_INPUT;
}
bool RegExpAlternative::IsAnchoredAtStart() {
ZoneList<RegExpTree*>* nodes = this->nodes(); ZoneList<RegExpTree*>* nodes = this->nodes();
for (int i = 0; i < nodes->length(); i++) { for (int i = 0; i < nodes->length(); i++) {
RegExpTree* node = nodes->at(i); RegExpTree* node = nodes->at(i);
if (node->IsAnchored()) { return true; } if (node->IsAnchoredAtStart()) { return true; }
if (node->max_match() > 0) { return false; }
}
return false;
}
bool RegExpAlternative::IsAnchoredAtEnd() {
ZoneList<RegExpTree*>* nodes = this->nodes();
for (int i = nodes->length() - 1; i >= 0; i--) {
RegExpTree* node = nodes->at(i);
if (node->IsAnchoredAtEnd()) { return true; }
if (node->max_match() > 0) { return false; } if (node->max_match() > 0) { return false; }
} }
return false; return false;
} }
bool RegExpDisjunction::IsAnchored() { bool RegExpDisjunction::IsAnchoredAtStart() {
ZoneList<RegExpTree*>* alternatives = this->alternatives(); ZoneList<RegExpTree*>* alternatives = this->alternatives();
for (int i = 0; i < alternatives->length(); i++) { for (int i = 0; i < alternatives->length(); i++) {
if (!alternatives->at(i)->IsAnchored()) if (!alternatives->at(i)->IsAnchoredAtStart())
return false; return false;
} }
return true; return true;
} }
bool RegExpLookahead::IsAnchored() { bool RegExpDisjunction::IsAnchoredAtEnd() {
return is_positive() && body()->IsAnchored(); ZoneList<RegExpTree*>* alternatives = this->alternatives();
for (int i = 0; i < alternatives->length(); i++) {
if (!alternatives->at(i)->IsAnchoredAtEnd())
return false;
}
return true;
}
bool RegExpLookahead::IsAnchoredAtStart() {
return is_positive() && body()->IsAnchoredAtStart();
}
bool RegExpCapture::IsAnchoredAtStart() {
return body()->IsAnchoredAtStart();
} }
bool RegExpCapture::IsAnchored() { bool RegExpCapture::IsAnchoredAtEnd() {
return body()->IsAnchored(); return body()->IsAnchoredAtEnd();
} }
......
...@@ -1523,7 +1523,8 @@ class RegExpTree: public ZoneObject { ...@@ -1523,7 +1523,8 @@ class RegExpTree: public ZoneObject {
virtual RegExpNode* ToNode(RegExpCompiler* compiler, virtual RegExpNode* ToNode(RegExpCompiler* compiler,
RegExpNode* on_success) = 0; RegExpNode* on_success) = 0;
virtual bool IsTextElement() { return false; } virtual bool IsTextElement() { return false; }
virtual bool IsAnchored() { return false; } virtual bool IsAnchoredAtStart() { return false; }
virtual bool IsAnchoredAtEnd() { return false; }
virtual int min_match() = 0; virtual int min_match() = 0;
virtual int max_match() = 0; virtual int max_match() = 0;
// Returns the interval of registers used for captures within this // Returns the interval of registers used for captures within this
...@@ -1548,7 +1549,8 @@ class RegExpDisjunction: public RegExpTree { ...@@ -1548,7 +1549,8 @@ class RegExpDisjunction: public RegExpTree {
virtual RegExpDisjunction* AsDisjunction(); virtual RegExpDisjunction* AsDisjunction();
virtual Interval CaptureRegisters(); virtual Interval CaptureRegisters();
virtual bool IsDisjunction(); virtual bool IsDisjunction();
virtual bool IsAnchored(); virtual bool IsAnchoredAtStart();
virtual bool IsAnchoredAtEnd();
virtual int min_match() { return min_match_; } virtual int min_match() { return min_match_; }
virtual int max_match() { return max_match_; } virtual int max_match() { return max_match_; }
ZoneList<RegExpTree*>* alternatives() { return alternatives_; } ZoneList<RegExpTree*>* alternatives() { return alternatives_; }
...@@ -1568,7 +1570,8 @@ class RegExpAlternative: public RegExpTree { ...@@ -1568,7 +1570,8 @@ class RegExpAlternative: public RegExpTree {
virtual RegExpAlternative* AsAlternative(); virtual RegExpAlternative* AsAlternative();
virtual Interval CaptureRegisters(); virtual Interval CaptureRegisters();
virtual bool IsAlternative(); virtual bool IsAlternative();
virtual bool IsAnchored(); virtual bool IsAnchoredAtStart();
virtual bool IsAnchoredAtEnd();
virtual int min_match() { return min_match_; } virtual int min_match() { return min_match_; }
virtual int max_match() { return max_match_; } virtual int max_match() { return max_match_; }
ZoneList<RegExpTree*>* nodes() { return nodes_; } ZoneList<RegExpTree*>* nodes() { return nodes_; }
...@@ -1595,7 +1598,8 @@ class RegExpAssertion: public RegExpTree { ...@@ -1595,7 +1598,8 @@ class RegExpAssertion: public RegExpTree {
RegExpNode* on_success); RegExpNode* on_success);
virtual RegExpAssertion* AsAssertion(); virtual RegExpAssertion* AsAssertion();
virtual bool IsAssertion(); virtual bool IsAssertion();
virtual bool IsAnchored(); virtual bool IsAnchoredAtStart();
virtual bool IsAnchoredAtEnd();
virtual int min_match() { return 0; } virtual int min_match() { return 0; }
virtual int max_match() { return 0; } virtual int max_match() { return 0; }
Type type() { return type_; } Type type() { return type_; }
...@@ -1768,7 +1772,8 @@ class RegExpCapture: public RegExpTree { ...@@ -1768,7 +1772,8 @@ class RegExpCapture: public RegExpTree {
RegExpCompiler* compiler, RegExpCompiler* compiler,
RegExpNode* on_success); RegExpNode* on_success);
virtual RegExpCapture* AsCapture(); virtual RegExpCapture* AsCapture();
virtual bool IsAnchored(); virtual bool IsAnchoredAtStart();
virtual bool IsAnchoredAtEnd();
virtual Interval CaptureRegisters(); virtual Interval CaptureRegisters();
virtual bool IsCapture(); virtual bool IsCapture();
virtual int min_match() { return body_->min_match(); } virtual int min_match() { return body_->min_match(); }
...@@ -1800,7 +1805,7 @@ class RegExpLookahead: public RegExpTree { ...@@ -1800,7 +1805,7 @@ class RegExpLookahead: public RegExpTree {
virtual RegExpLookahead* AsLookahead(); virtual RegExpLookahead* AsLookahead();
virtual Interval CaptureRegisters(); virtual Interval CaptureRegisters();
virtual bool IsLookahead(); virtual bool IsLookahead();
virtual bool IsAnchored(); virtual bool IsAnchoredAtStart();
virtual int min_match() { return 0; } virtual int min_match() { return 0; }
virtual int max_match() { return 0; } virtual int max_match() { return 0; }
RegExpTree* body() { return body_; } RegExpTree* body() { return body_; }
......
...@@ -88,7 +88,8 @@ V(CHECK_REGISTER_EQ_POS, 43, 8) /* bc8 reg_idx24 addr32 */ \ ...@@ -88,7 +88,8 @@ V(CHECK_REGISTER_EQ_POS, 43, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_AT_START, 44, 8) /* bc8 pad24 addr32 */ \ V(CHECK_AT_START, 44, 8) /* bc8 pad24 addr32 */ \
V(CHECK_NOT_AT_START, 45, 8) /* bc8 pad24 addr32 */ \ V(CHECK_NOT_AT_START, 45, 8) /* bc8 pad24 addr32 */ \
V(CHECK_GREEDY, 46, 8) /* bc8 pad24 addr32 */ \ V(CHECK_GREEDY, 46, 8) /* bc8 pad24 addr32 */ \
V(ADVANCE_CP_AND_GOTO, 47, 8) /* bc8 offset24 addr32 */ V(ADVANCE_CP_AND_GOTO, 47, 8) /* bc8 offset24 addr32 */ \
V(SET_CURRENT_POSITION_FROM_END, 48, 4) /* bc8 idx24 */
#define DECLARE_BYTECODES(name, code, length) \ #define DECLARE_BYTECODES(name, code, length) \
static const int BC_##name = code; static const int BC_##name = code;
......
...@@ -133,7 +133,6 @@ int RegExpMacroAssemblerIA32::stack_limit_slack() { ...@@ -133,7 +133,6 @@ int RegExpMacroAssemblerIA32::stack_limit_slack() {
void RegExpMacroAssemblerIA32::AdvanceCurrentPosition(int by) { void RegExpMacroAssemblerIA32::AdvanceCurrentPosition(int by) {
if (by != 0) { if (by != 0) {
Label inside_string;
__ add(Operand(edi), Immediate(by * char_size())); __ add(Operand(edi), Immediate(by * char_size()));
} }
} }
...@@ -964,6 +963,17 @@ void RegExpMacroAssemblerIA32::ReadStackPointerFromRegister(int reg) { ...@@ -964,6 +963,17 @@ void RegExpMacroAssemblerIA32::ReadStackPointerFromRegister(int reg) {
__ add(backtrack_stackpointer(), Operand(ebp, kStackHighEnd)); __ add(backtrack_stackpointer(), Operand(ebp, kStackHighEnd));
} }
void RegExpMacroAssemblerIA32::SetCurrentPositionFromEnd(int by) {
NearLabel after_position;
__ cmp(edi, -by * char_size());
__ j(above_equal, &after_position);
__ mov(edi, -by * char_size());
// On RegExp code entry (where this operation is used), the character before
// the current position is expected to be already loaded.
// We have advenced the position, so it's safe to read backwards.
LoadCurrentCharacterUnchecked(-1, 1);
__ bind(&after_position);
}
void RegExpMacroAssemblerIA32::SetRegister(int register_index, int to) { void RegExpMacroAssemblerIA32::SetRegister(int register_index, int to) {
ASSERT(register_index >= num_saved_registers_); // Reserved for positions! ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
......
...@@ -98,6 +98,7 @@ class RegExpMacroAssemblerIA32: public NativeRegExpMacroAssembler { ...@@ -98,6 +98,7 @@ class RegExpMacroAssemblerIA32: public NativeRegExpMacroAssembler {
StackCheckFlag check_stack_limit); StackCheckFlag check_stack_limit);
virtual void ReadCurrentPositionFromRegister(int reg); virtual void ReadCurrentPositionFromRegister(int reg);
virtual void ReadStackPointerFromRegister(int reg); virtual void ReadStackPointerFromRegister(int reg);
virtual void SetCurrentPositionFromEnd(int by);
virtual void SetRegister(int register_index, int to); virtual void SetRegister(int register_index, int to);
virtual void Succeed(); virtual void Succeed();
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
......
...@@ -607,6 +607,15 @@ static bool RawMatch(const byte* code_base, ...@@ -607,6 +607,15 @@ static bool RawMatch(const byte* code_base,
pc = code_base + Load32Aligned(pc + 4); pc = code_base + Load32Aligned(pc + 4);
} }
break; break;
BYTECODE(SET_CURRENT_POSITION_FROM_END) {
int by = static_cast<uint32_t>(insn) >> BYTECODE_SHIFT;
if (subject.length() - current > by) {
current = subject.length() - by;
current_char = subject[current - 1];
}
pc += BC_SET_CURRENT_POSITION_FROM_END_LENGTH;
break;
}
default: default:
UNREACHABLE(); UNREACHABLE();
break; break;
......
...@@ -5180,7 +5180,10 @@ RegExpEngine::CompilationResult RegExpEngine::Compile(RegExpCompileData* data, ...@@ -5180,7 +5180,10 @@ RegExpEngine::CompilationResult RegExpEngine::Compile(RegExpCompileData* data,
&compiler, &compiler,
compiler.accept()); compiler.accept());
RegExpNode* node = captured_body; RegExpNode* node = captured_body;
if (!data->tree->IsAnchored()) { bool is_end_anchored = data->tree->IsAnchoredAtEnd();
bool is_start_anchored = data->tree->IsAnchoredAtStart();
int max_length = data->tree->max_match();
if (!is_start_anchored) {
// Add a .*? at the beginning, outside the body capture, unless // Add a .*? at the beginning, outside the body capture, unless
// this expression is anchored at the beginning. // this expression is anchored at the beginning.
RegExpNode* loop_node = RegExpNode* loop_node =
...@@ -5236,6 +5239,15 @@ RegExpEngine::CompilationResult RegExpEngine::Compile(RegExpCompileData* data, ...@@ -5236,6 +5239,15 @@ RegExpEngine::CompilationResult RegExpEngine::Compile(RegExpCompileData* data,
RegExpMacroAssemblerIrregexp macro_assembler(codes); RegExpMacroAssemblerIrregexp macro_assembler(codes);
#endif // V8_INTERPRETED_REGEXP #endif // V8_INTERPRETED_REGEXP
// Inserted here, instead of in Assembler, because it depends on information
// in the AST that isn't replicated in the Node structure.
static const int kMaxBacksearchLimit = 1024;
if (is_end_anchored &&
!is_start_anchored &&
max_length < kMaxBacksearchLimit) {
macro_assembler.SetCurrentPositionFromEnd(max_length);
}
return compiler.Assemble(&macro_assembler, return compiler.Assemble(&macro_assembler,
node, node,
data->capture_count, data->capture_count,
......
...@@ -145,6 +145,12 @@ void RegExpMacroAssemblerIrregexp::ReadStackPointerFromRegister( ...@@ -145,6 +145,12 @@ void RegExpMacroAssemblerIrregexp::ReadStackPointerFromRegister(
} }
void RegExpMacroAssemblerIrregexp::SetCurrentPositionFromEnd(int by) {
ASSERT(is_uint24(by));
Emit(BC_SET_CURRENT_POSITION_FROM_END, by);
}
void RegExpMacroAssemblerIrregexp::SetRegister(int register_index, int to) { void RegExpMacroAssemblerIrregexp::SetRegister(int register_index, int to) {
ASSERT(register_index >= 0); ASSERT(register_index >= 0);
ASSERT(register_index <= kMaxRegister); ASSERT(register_index <= kMaxRegister);
......
...@@ -65,6 +65,7 @@ class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler { ...@@ -65,6 +65,7 @@ class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler {
virtual void PushRegister(int register_index, virtual void PushRegister(int register_index,
StackCheckFlag check_stack_limit); StackCheckFlag check_stack_limit);
virtual void AdvanceRegister(int reg, int by); // r[reg] += by. virtual void AdvanceRegister(int reg, int by); // r[reg] += by.
virtual void SetCurrentPositionFromEnd(int by);
virtual void SetRegister(int register_index, int to); virtual void SetRegister(int register_index, int to);
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
virtual void ClearRegisters(int reg_from, int reg_to); virtual void ClearRegisters(int reg_from, int reg_to);
......
...@@ -136,6 +136,12 @@ void RegExpMacroAssemblerTracer::AdvanceRegister(int reg, int by) { ...@@ -136,6 +136,12 @@ void RegExpMacroAssemblerTracer::AdvanceRegister(int reg, int by) {
} }
void RegExpMacroAssemblerTracer::SetCurrentPositionFromEnd(int by) {
PrintF(" SetCurrentPositionFromEnd(by=%d);\n", by);
assembler_->SetCurrentPositionFromEnd(by);
}
void RegExpMacroAssemblerTracer::SetRegister(int register_index, int to) { void RegExpMacroAssemblerTracer::SetRegister(int register_index, int to) {
PrintF(" SetRegister(register=%d, to=%d);\n", register_index, to); PrintF(" SetRegister(register=%d, to=%d);\n", register_index, to);
assembler_->SetRegister(register_index, to); assembler_->SetRegister(register_index, to);
......
...@@ -89,6 +89,7 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler { ...@@ -89,6 +89,7 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler {
StackCheckFlag check_stack_limit); StackCheckFlag check_stack_limit);
virtual void ReadCurrentPositionFromRegister(int reg); virtual void ReadCurrentPositionFromRegister(int reg);
virtual void ReadStackPointerFromRegister(int reg); virtual void ReadStackPointerFromRegister(int reg);
virtual void SetCurrentPositionFromEnd(int by);
virtual void SetRegister(int register_index, int to); virtual void SetRegister(int register_index, int to);
virtual void Succeed(); virtual void Succeed();
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
......
...@@ -155,6 +155,7 @@ class RegExpMacroAssembler { ...@@ -155,6 +155,7 @@ class RegExpMacroAssembler {
StackCheckFlag check_stack_limit) = 0; StackCheckFlag check_stack_limit) = 0;
virtual void ReadCurrentPositionFromRegister(int reg) = 0; virtual void ReadCurrentPositionFromRegister(int reg) = 0;
virtual void ReadStackPointerFromRegister(int reg) = 0; virtual void ReadStackPointerFromRegister(int reg) = 0;
virtual void SetCurrentPositionFromEnd(int by) = 0;
virtual void SetRegister(int register_index, int to) = 0; virtual void SetRegister(int register_index, int to) = 0;
virtual void Succeed() = 0; virtual void Succeed() = 0;
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset) = 0; virtual void WriteCurrentPositionToRegister(int reg, int cp_offset) = 0;
......
...@@ -145,7 +145,6 @@ int RegExpMacroAssemblerX64::stack_limit_slack() { ...@@ -145,7 +145,6 @@ int RegExpMacroAssemblerX64::stack_limit_slack() {
void RegExpMacroAssemblerX64::AdvanceCurrentPosition(int by) { void RegExpMacroAssemblerX64::AdvanceCurrentPosition(int by) {
if (by != 0) { if (by != 0) {
Label inside_string;
__ addq(rdi, Immediate(by * char_size())); __ addq(rdi, Immediate(by * char_size()));
} }
} }
...@@ -1053,6 +1052,19 @@ void RegExpMacroAssemblerX64::ReadStackPointerFromRegister(int reg) { ...@@ -1053,6 +1052,19 @@ void RegExpMacroAssemblerX64::ReadStackPointerFromRegister(int reg) {
} }
void RegExpMacroAssemblerX64::SetCurrentPositionFromEnd(int by) {
NearLabel after_position;
__ cmpq(rdi, Immediate(-by * char_size()));
__ j(above_equal, &after_position);
__ movq(rdi, Immediate(-by * char_size()));
// On RegExp code entry (where this operation is used), the character before
// the current position is expected to be already loaded.
// We have advenced the position, so it's safe to read backwards.
LoadCurrentCharacterUnchecked(-1, 1);
__ bind(&after_position);
}
void RegExpMacroAssemblerX64::SetRegister(int register_index, int to) { void RegExpMacroAssemblerX64::SetRegister(int register_index, int to) {
ASSERT(register_index >= num_saved_registers_); // Reserved for positions! ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
__ movq(register_location(register_index), Immediate(to)); __ movq(register_location(register_index), Immediate(to));
......
...@@ -93,6 +93,7 @@ class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler { ...@@ -93,6 +93,7 @@ class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler {
StackCheckFlag check_stack_limit); StackCheckFlag check_stack_limit);
virtual void ReadCurrentPositionFromRegister(int reg); virtual void ReadCurrentPositionFromRegister(int reg);
virtual void ReadStackPointerFromRegister(int reg); virtual void ReadStackPointerFromRegister(int reg);
virtual void SetCurrentPositionFromEnd(int by);
virtual void SetRegister(int register_index, int to); virtual void SetRegister(int register_index, int to);
virtual void Succeed(); virtual void Succeed();
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
......
...@@ -589,3 +589,59 @@ assertEquals(0, desc.value); ...@@ -589,3 +589,59 @@ assertEquals(0, desc.value);
assertEquals(false, desc.configurable); assertEquals(false, desc.configurable);
assertEquals(false, desc.enumerable); assertEquals(false, desc.enumerable);
assertEquals(true, desc.writable); assertEquals(true, desc.writable);
// Check that end-anchored regexps are optimized correctly.
var re = /(?:a|bc)g$/;
assertTrue(re.test("ag"));
assertTrue(re.test("bcg"));
assertTrue(re.test("abcg"));
assertTrue(re.test("zimbag"));
assertTrue(re.test("zimbcg"));
assertFalse(re.test("g"));
assertFalse(re.test(""));
// Global regexp (non-zero start).
var re = /(?:a|bc)g$/g;
assertTrue(re.test("ag"));
re.lastIndex = 1; // Near start of string.
assertTrue(re.test("zimbag"));
re.lastIndex = 5; // Near end of string.
assertFalse(re.test("zimbag"));
re.lastIndex = 4;
assertTrue(re.test("zimbag"));
// Anchored at both ends.
var re = /^(?:a|bc)g$/g;
assertTrue(re.test("ag"));
re.lastIndex = 1;
assertFalse(re.test("ag"));
re.lastIndex = 1;
assertFalse(re.test("zag"));
// Long max_length of RegExp.
var re = /VeryLongRegExp!{1,1000}$/;
assertTrue(re.test("BahoolaVeryLongRegExp!!!!!!"));
assertFalse(re.test("VeryLongRegExp"));
assertFalse(re.test("!"));
// End anchor inside disjunction.
var re = /(?:a$|bc$)/;
assertTrue(re.test("a"));
assertTrue(re.test("bc"));
assertTrue(re.test("abc"));
assertTrue(re.test("zimzamzumba"));
assertTrue(re.test("zimzamzumbc"));
assertFalse(re.test("c"));
assertFalse(re.test(""));
// Only partially anchored.
var re = /(?:a|bc$)/;
assertTrue(re.test("a"));
assertTrue(re.test("bc"));
assertEquals(["a"], re.exec("abc"));
assertEquals(4, re.exec("zimzamzumba").index);
assertEquals(["bc"], re.exec("zimzomzumbc"));
assertFalse(re.test("c"));
assertFalse(re.test(""));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment