Revert of Experimental support for RegExp lookbehind. (patchset #18 id:340001 of https://codereview.chromium.org/1418963009/ )
Reason for revert: gc stress breaks due to string_start_minus_one not being set correctly. Original issue's description: > Experimental support for RegExp lookbehind. > > R=erikcorry@chromium.org, littledan@chromium.org > BUG=v8:4545 > LOG=N > > Committed: https://crrev.com/37632606bbce1418238b13fd90cb6ef6705871cd > Cr-Commit-Position: refs/heads/master@{#32029} TBR=littledan@chromium.org,erikcorry@chromium.org,erikcorry@google.com NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=v8:4545 Review URL: https://codereview.chromium.org/1451373003 Cr-Commit-Position: refs/heads/master@{#32032}
This commit is contained in:
parent
2f7d6b46d0
commit
5b2ae9d908
12
src/ast.cc
12
src/ast.cc
@ -850,7 +850,7 @@ Interval RegExpDisjunction::CaptureRegisters() {
|
||||
}
|
||||
|
||||
|
||||
Interval RegExpLookaround::CaptureRegisters() {
|
||||
Interval RegExpLookahead::CaptureRegisters() {
|
||||
return body()->CaptureRegisters();
|
||||
}
|
||||
|
||||
@ -918,8 +918,8 @@ bool RegExpDisjunction::IsAnchoredAtEnd() {
|
||||
}
|
||||
|
||||
|
||||
bool RegExpLookaround::IsAnchoredAtStart() {
|
||||
return is_positive() && type() == LOOKAHEAD && body()->IsAnchoredAtStart();
|
||||
bool RegExpLookahead::IsAnchoredAtStart() {
|
||||
return is_positive() && body()->IsAnchoredAtStart();
|
||||
}
|
||||
|
||||
|
||||
@ -1068,10 +1068,8 @@ void* RegExpUnparser::VisitCapture(RegExpCapture* that, void* data) {
|
||||
}
|
||||
|
||||
|
||||
void* RegExpUnparser::VisitLookaround(RegExpLookaround* that, void* data) {
|
||||
os_ << "(";
|
||||
os_ << (that->type() == RegExpLookaround::LOOKAHEAD ? "->" : "<-");
|
||||
os_ << (that->is_positive() ? " + " : " - ");
|
||||
void* RegExpUnparser::VisitLookahead(RegExpLookahead* that, void* data) {
|
||||
os_ << "(-> " << (that->is_positive() ? "+ " : "- ");
|
||||
that->body()->Accept(this, data);
|
||||
os_ << ")";
|
||||
return NULL;
|
||||
|
34
src/ast.h
34
src/ast.h
@ -119,7 +119,7 @@ class RegExpCharacterClass;
|
||||
class RegExpCompiler;
|
||||
class RegExpDisjunction;
|
||||
class RegExpEmpty;
|
||||
class RegExpLookaround;
|
||||
class RegExpLookahead;
|
||||
class RegExpQuantifier;
|
||||
class RegExpText;
|
||||
|
||||
@ -3075,7 +3075,8 @@ class RegExpQuantifier final : public RegExpTree {
|
||||
|
||||
class RegExpCapture final : public RegExpTree {
|
||||
public:
|
||||
explicit RegExpCapture(int index) : body_(NULL), index_(index) {}
|
||||
explicit RegExpCapture(RegExpTree* body, int index)
|
||||
: body_(body), index_(index) { }
|
||||
void* Accept(RegExpVisitor* visitor, void* data) override;
|
||||
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
|
||||
static RegExpNode* ToNode(RegExpTree* body,
|
||||
@ -3090,7 +3091,6 @@ class RegExpCapture final : public RegExpTree {
|
||||
int min_match() override { return body_->min_match(); }
|
||||
int max_match() override { return body_->max_match(); }
|
||||
RegExpTree* body() { return body_; }
|
||||
void set_body(RegExpTree* body) { body_ = body; }
|
||||
int index() { return index_; }
|
||||
static int StartRegister(int index) { return index * 2; }
|
||||
static int EndRegister(int index) { return index * 2 + 1; }
|
||||
@ -3101,23 +3101,22 @@ class RegExpCapture final : public RegExpTree {
|
||||
};
|
||||
|
||||
|
||||
class RegExpLookaround final : public RegExpTree {
|
||||
class RegExpLookahead final : public RegExpTree {
|
||||
public:
|
||||
enum Type { LOOKAHEAD, LOOKBEHIND };
|
||||
|
||||
RegExpLookaround(RegExpTree* body, bool is_positive, int capture_count,
|
||||
int capture_from, Type type)
|
||||
RegExpLookahead(RegExpTree* body,
|
||||
bool is_positive,
|
||||
int capture_count,
|
||||
int capture_from)
|
||||
: body_(body),
|
||||
is_positive_(is_positive),
|
||||
capture_count_(capture_count),
|
||||
capture_from_(capture_from),
|
||||
type_(type) {}
|
||||
capture_from_(capture_from) { }
|
||||
|
||||
void* Accept(RegExpVisitor* visitor, void* data) override;
|
||||
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
|
||||
RegExpLookaround* AsLookaround() override;
|
||||
RegExpLookahead* AsLookahead() override;
|
||||
Interval CaptureRegisters() override;
|
||||
bool IsLookaround() override;
|
||||
bool IsLookahead() override;
|
||||
bool IsAnchoredAtStart() override;
|
||||
int min_match() override { return 0; }
|
||||
int max_match() override { return 0; }
|
||||
@ -3125,14 +3124,12 @@ class RegExpLookaround final : public RegExpTree {
|
||||
bool is_positive() { return is_positive_; }
|
||||
int capture_count() { return capture_count_; }
|
||||
int capture_from() { return capture_from_; }
|
||||
Type type() { return type_; }
|
||||
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
bool is_positive_;
|
||||
int capture_count_;
|
||||
int capture_from_;
|
||||
Type type_;
|
||||
};
|
||||
|
||||
|
||||
@ -3145,14 +3142,7 @@ class RegExpBackReference final : public RegExpTree {
|
||||
RegExpBackReference* AsBackReference() override;
|
||||
bool IsBackReference() override;
|
||||
int min_match() override { return 0; }
|
||||
// The capture may not be completely parsed yet, if the reference occurs
|
||||
// before the capture. In the ordinary case, nothing has been captured yet,
|
||||
// so the back reference must have the length 0. If the back reference is
|
||||
// inside a lookbehind, effectively making it a forward reference, we return
|
||||
// 0 since lookbehinds have a length of 0.
|
||||
int max_match() override {
|
||||
return capture_->body() ? capture_->max_match() : 0;
|
||||
}
|
||||
int max_match() override { return capture_->max_match(); }
|
||||
int index() { return capture_->index(); }
|
||||
RegExpCapture* capture() { return capture_; }
|
||||
private:
|
||||
|
@ -2049,7 +2049,6 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode_regexps)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_completion)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tolength)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_do_expressions)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_lookbehind)
|
||||
|
||||
|
||||
static void SimpleInstallFunction(Handle<JSObject>& base, const char* name,
|
||||
@ -2562,7 +2561,6 @@ bool Genesis::InstallExperimentalNatives() {
|
||||
static const char* harmony_completion_natives[] = {nullptr};
|
||||
static const char* harmony_do_expressions_natives[] = {nullptr};
|
||||
static const char* harmony_regexp_subclass_natives[] = {nullptr};
|
||||
static const char* harmony_regexp_lookbehind_natives[] = {nullptr};
|
||||
|
||||
for (int i = ExperimentalNatives::GetDebuggerCount();
|
||||
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
|
||||
|
@ -202,8 +202,7 @@ DEFINE_IMPLICATION(es_staging, harmony_destructuring)
|
||||
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
|
||||
V(harmony_simd, "harmony simd") \
|
||||
V(harmony_do_expressions, "harmony do-expressions") \
|
||||
V(harmony_regexp_subclass, "harmony regexp subclassing") \
|
||||
V(harmony_regexp_lookbehind, "harmony regexp lookbehind")
|
||||
V(harmony_regexp_subclass, "harmony regexp subclassing")
|
||||
|
||||
// Features that are complete (but still behind --harmony/es-staging flag).
|
||||
#define HARMONY_STAGED(V) \
|
||||
|
124
src/parser.cc
124
src/parser.cc
@ -5182,7 +5182,6 @@ RegExpParser::RegExpParser(FlatStringReader* in, Handle<String>* error,
|
||||
in_(in),
|
||||
current_(kEndMarker),
|
||||
next_pos_(0),
|
||||
captures_started_(0),
|
||||
capture_count_(0),
|
||||
has_more_(true),
|
||||
multiline_(multiline),
|
||||
@ -5286,26 +5285,25 @@ RegExpTree* RegExpParser::ParsePattern() {
|
||||
// Atom Quantifier
|
||||
RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
// Used to store current state while parsing subexpressions.
|
||||
RegExpParserState initial_state(NULL, INITIAL, RegExpLookaround::LOOKAHEAD, 0,
|
||||
zone());
|
||||
RegExpParserState* state = &initial_state;
|
||||
RegExpParserState initial_state(NULL, INITIAL, 0, zone());
|
||||
RegExpParserState* stored_state = &initial_state;
|
||||
// Cache the builder in a local variable for quick access.
|
||||
RegExpBuilder* builder = initial_state.builder();
|
||||
while (true) {
|
||||
switch (current()) {
|
||||
case kEndMarker:
|
||||
if (state->IsSubexpression()) {
|
||||
if (stored_state->IsSubexpression()) {
|
||||
// Inside a parenthesized group when hitting end of input.
|
||||
ReportError(CStrVector("Unterminated group") CHECK_FAILED);
|
||||
}
|
||||
DCHECK_EQ(INITIAL, state->group_type());
|
||||
DCHECK_EQ(INITIAL, stored_state->group_type());
|
||||
// Parsing completed successfully.
|
||||
return builder->ToRegExp();
|
||||
case ')': {
|
||||
if (!state->IsSubexpression()) {
|
||||
if (!stored_state->IsSubexpression()) {
|
||||
ReportError(CStrVector("Unmatched ')'") CHECK_FAILED);
|
||||
}
|
||||
DCHECK_NE(INITIAL, state->group_type());
|
||||
DCHECK_NE(INITIAL, stored_state->group_type());
|
||||
|
||||
Advance();
|
||||
// End disjunction parsing and convert builder content to new single
|
||||
@ -5314,27 +5312,27 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
|
||||
int end_capture_index = captures_started();
|
||||
|
||||
int capture_index = state->capture_index();
|
||||
SubexpressionType group_type = state->group_type();
|
||||
int capture_index = stored_state->capture_index();
|
||||
SubexpressionType group_type = stored_state->group_type();
|
||||
|
||||
// Restore previous state.
|
||||
stored_state = stored_state->previous_state();
|
||||
builder = stored_state->builder();
|
||||
|
||||
// Build result of subexpression.
|
||||
if (group_type == CAPTURE) {
|
||||
RegExpCapture* capture = GetCapture(capture_index);
|
||||
capture->set_body(body);
|
||||
RegExpCapture* capture = new(zone()) RegExpCapture(body, capture_index);
|
||||
captures_->at(capture_index - 1) = capture;
|
||||
body = capture;
|
||||
} else if (group_type != GROUPING) {
|
||||
DCHECK(group_type == POSITIVE_LOOKAROUND ||
|
||||
group_type == NEGATIVE_LOOKAROUND);
|
||||
bool is_positive = (group_type == POSITIVE_LOOKAROUND);
|
||||
body = new (zone()) RegExpLookaround(
|
||||
body, is_positive, end_capture_index - capture_index, capture_index,
|
||||
state->lookaround_type());
|
||||
DCHECK(group_type == POSITIVE_LOOKAHEAD ||
|
||||
group_type == NEGATIVE_LOOKAHEAD);
|
||||
bool is_positive = (group_type == POSITIVE_LOOKAHEAD);
|
||||
body = new(zone()) RegExpLookahead(body,
|
||||
is_positive,
|
||||
end_capture_index - capture_index,
|
||||
capture_index);
|
||||
}
|
||||
|
||||
// Restore previous state.
|
||||
state = state->previous_state();
|
||||
builder = state->builder();
|
||||
|
||||
builder->AddAtom(body);
|
||||
// For compatability with JSC and ES3, we allow quantifiers after
|
||||
// lookaheads, and break in all cases.
|
||||
@ -5381,7 +5379,6 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
}
|
||||
case '(': {
|
||||
SubexpressionType subexpr_type = CAPTURE;
|
||||
RegExpLookaround::Type lookaround_type = state->lookaround_type();
|
||||
Advance();
|
||||
if (current() == '?') {
|
||||
switch (Next()) {
|
||||
@ -5389,41 +5386,29 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
subexpr_type = GROUPING;
|
||||
break;
|
||||
case '=':
|
||||
lookaround_type = RegExpLookaround::LOOKAHEAD;
|
||||
subexpr_type = POSITIVE_LOOKAROUND;
|
||||
subexpr_type = POSITIVE_LOOKAHEAD;
|
||||
break;
|
||||
case '!':
|
||||
lookaround_type = RegExpLookaround::LOOKAHEAD;
|
||||
subexpr_type = NEGATIVE_LOOKAROUND;
|
||||
subexpr_type = NEGATIVE_LOOKAHEAD;
|
||||
break;
|
||||
case '<':
|
||||
if (FLAG_harmony_regexp_lookbehind) {
|
||||
Advance();
|
||||
lookaround_type = RegExpLookaround::LOOKBEHIND;
|
||||
if (Next() == '=') {
|
||||
subexpr_type = POSITIVE_LOOKAROUND;
|
||||
break;
|
||||
} else if (Next() == '!') {
|
||||
subexpr_type = NEGATIVE_LOOKAROUND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fall through.
|
||||
default:
|
||||
ReportError(CStrVector("Invalid group") CHECK_FAILED);
|
||||
break;
|
||||
}
|
||||
Advance(2);
|
||||
} else {
|
||||
if (captures_started_ >= kMaxCaptures) {
|
||||
if (captures_ == NULL) {
|
||||
captures_ = new(zone()) ZoneList<RegExpCapture*>(2, zone());
|
||||
}
|
||||
if (captures_started() >= kMaxCaptures) {
|
||||
ReportError(CStrVector("Too many captures") CHECK_FAILED);
|
||||
}
|
||||
captures_started_++;
|
||||
captures_->Add(NULL, zone());
|
||||
}
|
||||
// Store current state and begin new disjunction parsing.
|
||||
state = new (zone()) RegExpParserState(
|
||||
state, subexpr_type, lookaround_type, captures_started_, zone());
|
||||
builder = state->builder();
|
||||
stored_state = new(zone()) RegExpParserState(stored_state, subexpr_type,
|
||||
captures_started(), zone());
|
||||
builder = stored_state->builder();
|
||||
continue;
|
||||
}
|
||||
case '[': {
|
||||
@ -5466,15 +5451,16 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
case '7': case '8': case '9': {
|
||||
int index = 0;
|
||||
if (ParseBackReferenceIndex(&index)) {
|
||||
if (state->IsInsideCaptureGroup(index)) {
|
||||
// The backreference is inside the capture group it refers to.
|
||||
// Nothing can possibly have been captured yet.
|
||||
builder->AddEmpty();
|
||||
} else {
|
||||
RegExpCapture* capture = GetCapture(index);
|
||||
RegExpTree* atom = new (zone()) RegExpBackReference(capture);
|
||||
builder->AddAtom(atom);
|
||||
RegExpCapture* capture = NULL;
|
||||
if (captures_ != NULL && index <= captures_->length()) {
|
||||
capture = captures_->at(index - 1);
|
||||
}
|
||||
if (capture == NULL) {
|
||||
builder->AddEmpty();
|
||||
break;
|
||||
}
|
||||
RegExpTree* atom = new(zone()) RegExpBackReference(capture);
|
||||
builder->AddAtom(atom);
|
||||
break;
|
||||
}
|
||||
uc32 first_digit = Next();
|
||||
@ -5735,34 +5721,6 @@ bool RegExpParser::ParseBackReferenceIndex(int* index_out) {
|
||||
}
|
||||
|
||||
|
||||
RegExpCapture* RegExpParser::GetCapture(int index) {
|
||||
// The index for the capture groups are one-based. Its index in the list is
|
||||
// zero-based.
|
||||
int know_captures =
|
||||
is_scanned_for_captures_ ? capture_count_ : captures_started_;
|
||||
DCHECK(index <= know_captures);
|
||||
if (captures_ == NULL) {
|
||||
captures_ = new (zone()) ZoneList<RegExpCapture*>(know_captures, zone());
|
||||
}
|
||||
while (captures_->length() < know_captures) {
|
||||
captures_->Add(new (zone()) RegExpCapture(captures_->length() + 1), zone());
|
||||
}
|
||||
return captures_->at(index - 1);
|
||||
}
|
||||
|
||||
|
||||
bool RegExpParser::RegExpParserState::IsInsideCaptureGroup(int index) {
|
||||
for (RegExpParserState* s = this; s != NULL; s = s->previous_state()) {
|
||||
if (s->group_type() != CAPTURE) continue;
|
||||
// Return true if we found the matching capture index.
|
||||
if (index == s->capture_index()) return true;
|
||||
// Abort if index is larger than what has been parsed up till this state.
|
||||
if (index > s->capture_index()) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// QuantifierPrefix ::
|
||||
// { DecimalDigits }
|
||||
// { DecimalDigits , }
|
||||
@ -6094,7 +6052,7 @@ RegExpTree* RegExpParser::ParseCharacterClass() {
|
||||
ranges->Add(CharacterRange::Everything(), zone());
|
||||
is_negated = !is_negated;
|
||||
}
|
||||
return new (zone()) RegExpCharacterClass(ranges, is_negated);
|
||||
return new(zone()) RegExpCharacterClass(ranges, is_negated);
|
||||
}
|
||||
|
||||
|
||||
|
24
src/parser.h
24
src/parser.h
@ -450,7 +450,7 @@ class RegExpParser BASE_EMBEDDED {
|
||||
bool simple();
|
||||
bool contains_anchor() { return contains_anchor_; }
|
||||
void set_contains_anchor() { contains_anchor_ = true; }
|
||||
int captures_started() { return captures_started_; }
|
||||
int captures_started() { return captures_ == NULL ? 0 : captures_->length(); }
|
||||
int position() { return next_pos_ - 1; }
|
||||
bool failed() { return failed_; }
|
||||
|
||||
@ -463,8 +463,8 @@ class RegExpParser BASE_EMBEDDED {
|
||||
enum SubexpressionType {
|
||||
INITIAL,
|
||||
CAPTURE, // All positive values represent captures.
|
||||
POSITIVE_LOOKAROUND,
|
||||
NEGATIVE_LOOKAROUND,
|
||||
POSITIVE_LOOKAHEAD,
|
||||
NEGATIVE_LOOKAHEAD,
|
||||
GROUPING
|
||||
};
|
||||
|
||||
@ -472,12 +472,11 @@ class RegExpParser BASE_EMBEDDED {
|
||||
public:
|
||||
RegExpParserState(RegExpParserState* previous_state,
|
||||
SubexpressionType group_type,
|
||||
RegExpLookaround::Type lookaround_type,
|
||||
int disjunction_capture_index, Zone* zone)
|
||||
int disjunction_capture_index,
|
||||
Zone* zone)
|
||||
: previous_state_(previous_state),
|
||||
builder_(new (zone) RegExpBuilder(zone)),
|
||||
builder_(new(zone) RegExpBuilder(zone)),
|
||||
group_type_(group_type),
|
||||
lookaround_type_(lookaround_type),
|
||||
disjunction_capture_index_(disjunction_capture_index) {}
|
||||
// Parser state of containing expression, if any.
|
||||
RegExpParserState* previous_state() { return previous_state_; }
|
||||
@ -486,16 +485,11 @@ class RegExpParser BASE_EMBEDDED {
|
||||
RegExpBuilder* builder() { return builder_; }
|
||||
// Type of regexp being parsed (parenthesized group or entire regexp).
|
||||
SubexpressionType group_type() { return group_type_; }
|
||||
// Lookahead or Lookbehind.
|
||||
RegExpLookaround::Type lookaround_type() { return lookaround_type_; }
|
||||
// Index in captures array of first capture in this sub-expression, if any.
|
||||
// Also the capture index of this sub-expression itself, if group_type
|
||||
// is CAPTURE.
|
||||
int capture_index() { return disjunction_capture_index_; }
|
||||
|
||||
// Check whether the parser is inside a capture group with the given index.
|
||||
bool IsInsideCaptureGroup(int index);
|
||||
|
||||
private:
|
||||
// Linked list implementation of stack of states.
|
||||
RegExpParserState* previous_state_;
|
||||
@ -503,15 +497,10 @@ class RegExpParser BASE_EMBEDDED {
|
||||
RegExpBuilder* builder_;
|
||||
// Stored disjunction type (capture, look-ahead or grouping), if any.
|
||||
SubexpressionType group_type_;
|
||||
// Stored read direction.
|
||||
RegExpLookaround::Type lookaround_type_;
|
||||
// Stored disjunction's capture index (if any).
|
||||
int disjunction_capture_index_;
|
||||
};
|
||||
|
||||
// Return the 1-indexed RegExpCapture object, allocate if necessary.
|
||||
RegExpCapture* GetCapture(int index);
|
||||
|
||||
Isolate* isolate() { return isolate_; }
|
||||
Zone* zone() const { return zone_; }
|
||||
|
||||
@ -529,7 +518,6 @@ class RegExpParser BASE_EMBEDDED {
|
||||
FlatStringReader* in_;
|
||||
uc32 current_;
|
||||
int next_pos_;
|
||||
int captures_started_;
|
||||
// The capture count is only valid after we have scanned for captures.
|
||||
int capture_count_;
|
||||
bool has_more_;
|
||||
|
@ -58,7 +58,7 @@ namespace internal {
|
||||
* - fp[-16] void* input_string (location of a handle containing the string).
|
||||
* - fp[-20] success counter (only for global regexps to count matches).
|
||||
* - fp[-24] Offset of location before start of input (effectively character
|
||||
* string start - 1). Used to initialize capture registers to a
|
||||
* position -1). Used to initialize capture registers to a
|
||||
* non-position.
|
||||
* - fp[-28] At start (if 1, we are starting at the start of the
|
||||
* string, otherwise 0)
|
||||
@ -176,18 +176,29 @@ void RegExpMacroAssemblerARM::CheckCharacterGT(uc16 limit, Label* on_greater) {
|
||||
|
||||
|
||||
void RegExpMacroAssemblerARM::CheckAtStart(Label* on_at_start) {
|
||||
__ ldr(r1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ add(r0, current_input_offset(), Operand(-char_size()));
|
||||
Label not_at_start;
|
||||
// Did we start the match at the start of the string at all?
|
||||
__ ldr(r0, MemOperand(frame_pointer(), kStartIndex));
|
||||
__ cmp(r0, Operand::Zero());
|
||||
BranchOrBacktrack(ne, ¬_at_start);
|
||||
|
||||
// If we did, are we still at the start of the input?
|
||||
__ ldr(r1, MemOperand(frame_pointer(), kInputStart));
|
||||
__ add(r0, end_of_input_address(), Operand(current_input_offset()));
|
||||
__ cmp(r0, r1);
|
||||
BranchOrBacktrack(eq, on_at_start);
|
||||
__ bind(¬_at_start);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerARM::CheckNotAtStart(int cp_offset,
|
||||
Label* on_not_at_start) {
|
||||
__ ldr(r1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ add(r0, current_input_offset(),
|
||||
Operand(-char_size() + cp_offset * char_size()));
|
||||
void RegExpMacroAssemblerARM::CheckNotAtStart(Label* on_not_at_start) {
|
||||
// Did we start the match at the start of the string at all?
|
||||
__ ldr(r0, MemOperand(frame_pointer(), kStartIndex));
|
||||
__ cmp(r0, Operand::Zero());
|
||||
BranchOrBacktrack(ne, on_not_at_start);
|
||||
// If we did, are we still at the start of the input?
|
||||
__ ldr(r1, MemOperand(frame_pointer(), kInputStart));
|
||||
__ add(r0, end_of_input_address(), Operand(current_input_offset()));
|
||||
__ cmp(r0, r1);
|
||||
BranchOrBacktrack(ne, on_not_at_start);
|
||||
}
|
||||
@ -209,27 +220,20 @@ void RegExpMacroAssemblerARM::CheckGreedyLoop(Label* on_equal) {
|
||||
|
||||
|
||||
void RegExpMacroAssemblerARM::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, Label* on_no_match) {
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
__ ldr(r0, register_location(start_reg)); // Index of start of capture
|
||||
__ ldr(r1, register_location(start_reg + 1)); // Index of end of capture
|
||||
__ sub(r1, r1, r0, SetCC); // Length of capture.
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
// If length is zero, either the capture is empty or it is not participating.
|
||||
// In either case succeed immediately.
|
||||
__ b(eq, &fallthrough);
|
||||
|
||||
// Check that there are enough characters left in the input.
|
||||
if (read_backward) {
|
||||
__ ldr(r3, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ add(r3, r3, r1);
|
||||
__ cmp(current_input_offset(), r3);
|
||||
BranchOrBacktrack(le, on_no_match);
|
||||
} else {
|
||||
__ cmn(r1, Operand(current_input_offset()));
|
||||
BranchOrBacktrack(gt, on_no_match);
|
||||
}
|
||||
__ cmn(r1, Operand(current_input_offset()));
|
||||
BranchOrBacktrack(gt, on_no_match);
|
||||
|
||||
if (mode_ == LATIN1) {
|
||||
Label success;
|
||||
@ -238,12 +242,9 @@ void RegExpMacroAssemblerARM::CheckNotBackReferenceIgnoreCase(
|
||||
|
||||
// r0 - offset of start of capture
|
||||
// r1 - length of capture
|
||||
__ add(r0, r0, end_of_input_address());
|
||||
__ add(r2, end_of_input_address(), current_input_offset());
|
||||
if (read_backward) {
|
||||
__ sub(r2, r2, r1); // Offset by length when matching backwards.
|
||||
}
|
||||
__ add(r1, r0, r1);
|
||||
__ add(r0, r0, Operand(end_of_input_address()));
|
||||
__ add(r2, end_of_input_address(), Operand(current_input_offset()));
|
||||
__ add(r1, r0, Operand(r1));
|
||||
|
||||
// r0 - Address of start of capture.
|
||||
// r1 - Address of end of capture
|
||||
@ -282,12 +283,6 @@ void RegExpMacroAssemblerARM::CheckNotBackReferenceIgnoreCase(
|
||||
__ bind(&success);
|
||||
// Compute new value of character position after the matched part.
|
||||
__ sub(current_input_offset(), r2, end_of_input_address());
|
||||
if (read_backward) {
|
||||
__ ldr(r0, register_location(start_reg)); // Index of start of capture
|
||||
__ ldr(r1, register_location(start_reg + 1)); // Index of end of capture
|
||||
__ add(current_input_offset(), current_input_offset(), r0);
|
||||
__ sub(current_input_offset(), current_input_offset(), r1);
|
||||
}
|
||||
} else {
|
||||
DCHECK(mode_ == UC16);
|
||||
int argument_count = 4;
|
||||
@ -310,10 +305,7 @@ void RegExpMacroAssemblerARM::CheckNotBackReferenceIgnoreCase(
|
||||
// Save length in callee-save register for use on return.
|
||||
__ mov(r4, Operand(r1));
|
||||
// Address of current input position.
|
||||
__ add(r1, current_input_offset(), end_of_input_address());
|
||||
if (read_backward) {
|
||||
__ sub(r1, r1, r4);
|
||||
}
|
||||
__ add(r1, current_input_offset(), Operand(end_of_input_address()));
|
||||
// Isolate.
|
||||
__ mov(r3, Operand(ExternalReference::isolate_address(isolate())));
|
||||
|
||||
@ -327,22 +319,17 @@ void RegExpMacroAssemblerARM::CheckNotBackReferenceIgnoreCase(
|
||||
// Check if function returned non-zero for success or zero for failure.
|
||||
__ cmp(r0, Operand::Zero());
|
||||
BranchOrBacktrack(eq, on_no_match);
|
||||
|
||||
// On success, advance position by length of capture.
|
||||
if (read_backward) {
|
||||
__ sub(current_input_offset(), current_input_offset(), r4);
|
||||
} else {
|
||||
__ add(current_input_offset(), current_input_offset(), r4);
|
||||
}
|
||||
// On success, increment position by length of capture.
|
||||
__ add(current_input_offset(), current_input_offset(), Operand(r4));
|
||||
}
|
||||
|
||||
__ bind(&fallthrough);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerARM::CheckNotBackReference(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match) {
|
||||
void RegExpMacroAssemblerARM::CheckNotBackReference(
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
Label success;
|
||||
|
||||
@ -350,31 +337,17 @@ void RegExpMacroAssemblerARM::CheckNotBackReference(int start_reg,
|
||||
__ ldr(r0, register_location(start_reg));
|
||||
__ ldr(r1, register_location(start_reg + 1));
|
||||
__ sub(r1, r1, r0, SetCC); // Length to check.
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
// Succeed on empty capture (including no capture).
|
||||
__ b(eq, &fallthrough);
|
||||
|
||||
// Check that there are enough characters left in the input.
|
||||
if (read_backward) {
|
||||
__ ldr(r3, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ add(r3, r3, r1);
|
||||
__ cmp(current_input_offset(), r3);
|
||||
BranchOrBacktrack(lt, on_no_match);
|
||||
} else {
|
||||
__ cmn(r1, Operand(current_input_offset()));
|
||||
BranchOrBacktrack(gt, on_no_match);
|
||||
}
|
||||
__ cmn(r1, Operand(current_input_offset()));
|
||||
BranchOrBacktrack(gt, on_no_match);
|
||||
|
||||
// r0 - offset of start of capture
|
||||
// r1 - length of capture
|
||||
__ add(r0, r0, end_of_input_address());
|
||||
__ add(r2, end_of_input_address(), current_input_offset());
|
||||
if (read_backward) {
|
||||
__ sub(r2, r2, r1); // Offset by length when matching backwards.
|
||||
}
|
||||
__ add(r1, r0, r1);
|
||||
// Compute pointers to match string and capture string
|
||||
__ add(r0, r0, Operand(end_of_input_address()));
|
||||
__ add(r2, end_of_input_address(), Operand(current_input_offset()));
|
||||
__ add(r1, r1, Operand(r0));
|
||||
|
||||
Label loop;
|
||||
__ bind(&loop);
|
||||
@ -393,13 +366,6 @@ void RegExpMacroAssemblerARM::CheckNotBackReference(int start_reg,
|
||||
|
||||
// Move current character position to position after match.
|
||||
__ sub(current_input_offset(), r2, end_of_input_address());
|
||||
if (read_backward) {
|
||||
__ ldr(r0, register_location(start_reg)); // Index of start of capture
|
||||
__ ldr(r1, register_location(start_reg + 1)); // Index of end of capture
|
||||
__ add(current_input_offset(), current_input_offset(), r0);
|
||||
__ sub(current_input_offset(), current_input_offset(), r1);
|
||||
}
|
||||
|
||||
__ bind(&fallthrough);
|
||||
}
|
||||
|
||||
@ -637,7 +603,7 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
|
||||
__ add(frame_pointer(), sp, Operand(4 * kPointerSize));
|
||||
__ mov(r0, Operand::Zero());
|
||||
__ push(r0); // Make room for success counter and initialize it to 0.
|
||||
__ push(r0); // Make room for "string start - 1" constant.
|
||||
__ push(r0); // Make room for "position - 1" constant (value is irrelevant).
|
||||
// Check if we have space on the stack for registers.
|
||||
Label stack_limit_hit;
|
||||
Label stack_ok;
|
||||
@ -681,7 +647,7 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
|
||||
__ sub(r0, r0, Operand(r1, LSL, (mode_ == UC16) ? 1 : 0));
|
||||
// Store this value in a local variable, for use when clearing
|
||||
// position registers.
|
||||
__ str(r0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ str(r0, MemOperand(frame_pointer(), kInputStartMinusOne));
|
||||
|
||||
// Initialize code pointer register
|
||||
__ mov(code_pointer(), Operand(masm_->CodeObject()));
|
||||
@ -785,7 +751,7 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
|
||||
__ str(r2, MemOperand(frame_pointer(), kRegisterOutput));
|
||||
|
||||
// Prepare r0 to initialize registers with its value in the next run.
|
||||
__ ldr(r0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ ldr(r0, MemOperand(frame_pointer(), kInputStartMinusOne));
|
||||
|
||||
if (global_with_zero_length_check()) {
|
||||
// Special case for zero-length matches.
|
||||
@ -926,13 +892,10 @@ void RegExpMacroAssemblerARM::LoadCurrentCharacter(int cp_offset,
|
||||
Label* on_end_of_input,
|
||||
bool check_bounds,
|
||||
int characters) {
|
||||
DCHECK(cp_offset >= -1); // ^ and \b can look behind one character.
|
||||
DCHECK(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
|
||||
if (check_bounds) {
|
||||
if (cp_offset >= 0) {
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
} else {
|
||||
CheckPosition(cp_offset, on_end_of_input);
|
||||
}
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
}
|
||||
LoadCurrentCharacterUnchecked(cp_offset, characters);
|
||||
}
|
||||
@ -1020,7 +983,7 @@ void RegExpMacroAssemblerARM::WriteCurrentPositionToRegister(int reg,
|
||||
|
||||
void RegExpMacroAssemblerARM::ClearRegisters(int reg_from, int reg_to) {
|
||||
DCHECK(reg_from <= reg_to);
|
||||
__ ldr(r0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ ldr(r0, MemOperand(frame_pointer(), kInputStartMinusOne));
|
||||
for (int reg = reg_from; reg <= reg_to; reg++) {
|
||||
__ str(r0, register_location(reg));
|
||||
}
|
||||
@ -1106,15 +1069,8 @@ MemOperand RegExpMacroAssemblerARM::register_location(int register_index) {
|
||||
|
||||
void RegExpMacroAssemblerARM::CheckPosition(int cp_offset,
|
||||
Label* on_outside_input) {
|
||||
if (cp_offset >= 0) {
|
||||
__ cmp(current_input_offset(), Operand(-cp_offset * char_size()));
|
||||
BranchOrBacktrack(ge, on_outside_input);
|
||||
} else {
|
||||
__ ldr(r1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ add(r0, current_input_offset(), Operand(cp_offset * char_size()));
|
||||
__ cmp(r0, r1);
|
||||
BranchOrBacktrack(le, on_outside_input);
|
||||
}
|
||||
__ cmp(current_input_offset(), Operand(-cp_offset * char_size()));
|
||||
BranchOrBacktrack(ge, on_outside_input);
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,11 +34,9 @@ class RegExpMacroAssemblerARM: public NativeRegExpMacroAssembler {
|
||||
// A "greedy loop" is a loop that is both greedy and with a simple
|
||||
// body. It has a particularly simple implementation.
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotAtStart(Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
|
||||
virtual void CheckNotCharacterAfterAnd(unsigned c,
|
||||
@ -121,9 +119,9 @@ class RegExpMacroAssemblerARM: public NativeRegExpMacroAssembler {
|
||||
// When adding local variables remember to push space for them in
|
||||
// the frame in GetCode.
|
||||
static const int kSuccessfulCaptures = kInputString - kPointerSize;
|
||||
static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
// First register address. Following registers are below it on the stack.
|
||||
static const int kRegisterZero = kStringStartMinusOne - kPointerSize;
|
||||
static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
|
||||
|
||||
// Initial size of code buffer.
|
||||
static const size_t kRegExpCodeSize = 1024;
|
||||
|
@ -210,17 +210,23 @@ void RegExpMacroAssemblerARM64::CheckCharacterGT(uc16 limit,
|
||||
|
||||
|
||||
void RegExpMacroAssemblerARM64::CheckAtStart(Label* on_at_start) {
|
||||
__ Add(w10, current_input_offset(), Operand(-char_size()));
|
||||
__ Cmp(w10, string_start_minus_one());
|
||||
Label not_at_start;
|
||||
// Did we start the match at the start of the input string?
|
||||
CompareAndBranchOrBacktrack(start_offset(), 0, ne, ¬_at_start);
|
||||
// If we did, are we still at the start of the input string?
|
||||
__ Add(x10, input_end(), Operand(current_input_offset(), SXTW));
|
||||
__ Cmp(x10, input_start());
|
||||
BranchOrBacktrack(eq, on_at_start);
|
||||
__ Bind(¬_at_start);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerARM64::CheckNotAtStart(int cp_offset,
|
||||
Label* on_not_at_start) {
|
||||
__ Add(w10, current_input_offset(),
|
||||
Operand(-char_size() + cp_offset * char_size()));
|
||||
__ Cmp(w10, string_start_minus_one());
|
||||
void RegExpMacroAssemblerARM64::CheckNotAtStart(Label* on_not_at_start) {
|
||||
// Did we start the match at the start of the input string?
|
||||
CompareAndBranchOrBacktrack(start_offset(), 0, ne, on_not_at_start);
|
||||
// If we did, are we still at the start of the input string?
|
||||
__ Add(x10, input_end(), Operand(current_input_offset(), SXTW));
|
||||
__ Cmp(x10, input_start());
|
||||
BranchOrBacktrack(ne, on_not_at_start);
|
||||
}
|
||||
|
||||
@ -271,9 +277,9 @@ void RegExpMacroAssemblerARM64::CheckGreedyLoop(Label* on_equal) {
|
||||
BranchOrBacktrack(eq, on_equal);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerARM64::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, Label* on_no_match) {
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
|
||||
Register capture_start_offset = w10;
|
||||
@ -291,21 +297,12 @@ void RegExpMacroAssemblerARM64::CheckNotBackReferenceIgnoreCase(
|
||||
__ Ldp(w11, capture_start_offset, capture_location(start_reg, x10));
|
||||
}
|
||||
__ Sub(capture_length, w11, capture_start_offset); // Length to check.
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
__ CompareAndBranch(capture_length, Operand(0), eq, &fallthrough);
|
||||
// Succeed on empty capture (including no capture).
|
||||
__ Cbz(capture_length, &fallthrough);
|
||||
|
||||
// Check that there are enough characters left in the input.
|
||||
if (read_backward) {
|
||||
__ Add(w12, string_start_minus_one(), capture_length);
|
||||
__ Cmp(current_input_offset(), w12);
|
||||
BranchOrBacktrack(le, on_no_match);
|
||||
} else {
|
||||
__ Cmn(capture_length, current_input_offset());
|
||||
BranchOrBacktrack(gt, on_no_match);
|
||||
}
|
||||
__ Cmn(capture_length, current_input_offset());
|
||||
BranchOrBacktrack(gt, on_no_match);
|
||||
|
||||
if (mode_ == LATIN1) {
|
||||
Label success;
|
||||
@ -325,11 +322,6 @@ void RegExpMacroAssemblerARM64::CheckNotBackReferenceIgnoreCase(
|
||||
__ Add(current_position_address,
|
||||
input_end(),
|
||||
Operand(current_input_offset(), SXTW));
|
||||
if (read_backward) {
|
||||
// Offset by length when matching backwards.
|
||||
__ Sub(current_position_address, current_position_address,
|
||||
Operand(capture_length, SXTW));
|
||||
}
|
||||
|
||||
Label loop;
|
||||
__ Bind(&loop);
|
||||
@ -369,10 +361,6 @@ void RegExpMacroAssemblerARM64::CheckNotBackReferenceIgnoreCase(
|
||||
// The current input offset should be <= 0, and fit in a W register.
|
||||
__ Check(le, kOffsetOutOfRange);
|
||||
}
|
||||
if (read_backward) {
|
||||
__ Sub(current_input_offset(), current_input_offset(),
|
||||
Operand(capture_length, SXTW));
|
||||
}
|
||||
} else {
|
||||
DCHECK(mode_ == UC16);
|
||||
int argument_count = 4;
|
||||
@ -395,9 +383,6 @@ void RegExpMacroAssemblerARM64::CheckNotBackReferenceIgnoreCase(
|
||||
__ Mov(w2, capture_length);
|
||||
// Address of current input position.
|
||||
__ Add(x1, input_end(), Operand(current_input_offset(), SXTW));
|
||||
if (read_backward) {
|
||||
__ Sub(x1, x1, Operand(capture_length, SXTW));
|
||||
}
|
||||
// Isolate.
|
||||
__ Mov(x3, ExternalReference::isolate_address(isolate()));
|
||||
|
||||
@ -415,20 +400,16 @@ void RegExpMacroAssemblerARM64::CheckNotBackReferenceIgnoreCase(
|
||||
__ PopCPURegList(cached_registers);
|
||||
BranchOrBacktrack(eq, on_no_match);
|
||||
|
||||
// On success, advance position by length of capture.
|
||||
if (read_backward) {
|
||||
__ Sub(current_input_offset(), current_input_offset(), capture_length);
|
||||
} else {
|
||||
__ Add(current_input_offset(), current_input_offset(), capture_length);
|
||||
}
|
||||
// On success, increment position by length of capture.
|
||||
__ Add(current_input_offset(), current_input_offset(), capture_length);
|
||||
}
|
||||
|
||||
__ Bind(&fallthrough);
|
||||
}
|
||||
|
||||
void RegExpMacroAssemblerARM64::CheckNotBackReference(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match) {
|
||||
void RegExpMacroAssemblerARM64::CheckNotBackReference(
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
|
||||
Register capture_start_address = x12;
|
||||
@ -445,21 +426,12 @@ void RegExpMacroAssemblerARM64::CheckNotBackReference(int start_reg,
|
||||
__ Ldp(w11, w10, capture_location(start_reg, x10));
|
||||
}
|
||||
__ Sub(capture_length, w11, w10); // Length to check.
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
__ CompareAndBranch(capture_length, Operand(0), eq, &fallthrough);
|
||||
// Succeed on empty capture (including no capture).
|
||||
__ Cbz(capture_length, &fallthrough);
|
||||
|
||||
// Check that there are enough characters left in the input.
|
||||
if (read_backward) {
|
||||
__ Add(w12, string_start_minus_one(), capture_length);
|
||||
__ Cmp(current_input_offset(), w12);
|
||||
BranchOrBacktrack(le, on_no_match);
|
||||
} else {
|
||||
__ Cmn(capture_length, current_input_offset());
|
||||
BranchOrBacktrack(gt, on_no_match);
|
||||
}
|
||||
__ Cmn(capture_length, current_input_offset());
|
||||
BranchOrBacktrack(gt, on_no_match);
|
||||
|
||||
// Compute pointers to match string and capture string
|
||||
__ Add(capture_start_address, input_end(), Operand(w10, SXTW));
|
||||
@ -469,11 +441,6 @@ void RegExpMacroAssemblerARM64::CheckNotBackReference(int start_reg,
|
||||
__ Add(current_position_address,
|
||||
input_end(),
|
||||
Operand(current_input_offset(), SXTW));
|
||||
if (read_backward) {
|
||||
// Offset by length when matching backwards.
|
||||
__ Sub(current_position_address, current_position_address,
|
||||
Operand(capture_length, SXTW));
|
||||
}
|
||||
|
||||
Label loop;
|
||||
__ Bind(&loop);
|
||||
@ -492,11 +459,6 @@ void RegExpMacroAssemblerARM64::CheckNotBackReference(int start_reg,
|
||||
|
||||
// Move current character position to position after match.
|
||||
__ Sub(current_input_offset().X(), current_position_address, input_end());
|
||||
if (read_backward) {
|
||||
__ Sub(current_input_offset(), current_input_offset(),
|
||||
Operand(capture_length, SXTW));
|
||||
}
|
||||
|
||||
if (masm_->emit_debug_code()) {
|
||||
__ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW));
|
||||
__ Ccmp(current_input_offset(), 0, NoFlag, eq);
|
||||
@ -796,13 +758,14 @@ Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
|
||||
// The non-position value is used as a clearing value for the
|
||||
// capture registers, it corresponds to the position of the first character
|
||||
// minus one.
|
||||
__ Sub(string_start_minus_one(), current_input_offset(), char_size());
|
||||
__ Sub(string_start_minus_one(), string_start_minus_one(),
|
||||
__ Sub(non_position_value(), current_input_offset(), char_size());
|
||||
__ Sub(non_position_value(), non_position_value(),
|
||||
Operand(start_offset(), LSL, (mode_ == UC16) ? 1 : 0));
|
||||
// We can store this value twice in an X register for initializing
|
||||
// on-stack registers later.
|
||||
__ Orr(twice_non_position_value(), string_start_minus_one().X(),
|
||||
Operand(string_start_minus_one().X(), LSL, kWRegSizeInBits));
|
||||
__ Orr(twice_non_position_value(),
|
||||
non_position_value().X(),
|
||||
Operand(non_position_value().X(), LSL, kWRegSizeInBits));
|
||||
|
||||
// Initialize code pointer register.
|
||||
__ Mov(code_pointer(), Operand(masm_->CodeObject()));
|
||||
@ -1118,14 +1081,11 @@ void RegExpMacroAssemblerARM64::LoadCurrentCharacter(int cp_offset,
|
||||
int characters) {
|
||||
// TODO(pielan): Make sure long strings are caught before this, and not
|
||||
// just asserted in debug mode.
|
||||
DCHECK(cp_offset >= -1); // ^ and \b can look behind one character.
|
||||
// Be sane! (And ensure that an int32_t can be used to index the string)
|
||||
DCHECK(cp_offset < (1<<30));
|
||||
if (check_bounds) {
|
||||
if (cp_offset >= 0) {
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
} else {
|
||||
CheckPosition(cp_offset, on_end_of_input);
|
||||
}
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
}
|
||||
LoadCurrentCharacterUnchecked(cp_offset, characters);
|
||||
}
|
||||
@ -1250,7 +1210,7 @@ void RegExpMacroAssemblerARM64::ClearRegisters(int reg_from, int reg_to) {
|
||||
// If the first capture register is cached in a hardware register but not
|
||||
// aligned on a 64-bit one, we need to clear the first one specifically.
|
||||
if ((reg_from < kNumCachedRegisters) && ((reg_from % 2) != 0)) {
|
||||
StoreRegister(reg_from, string_start_minus_one());
|
||||
StoreRegister(reg_from, non_position_value());
|
||||
num_registers--;
|
||||
reg_from++;
|
||||
}
|
||||
@ -1264,7 +1224,7 @@ void RegExpMacroAssemblerARM64::ClearRegisters(int reg_from, int reg_to) {
|
||||
}
|
||||
|
||||
if ((num_registers % 2) == 1) {
|
||||
StoreRegister(reg_from, string_start_minus_one());
|
||||
StoreRegister(reg_from, non_position_value());
|
||||
num_registers--;
|
||||
reg_from++;
|
||||
}
|
||||
@ -1341,14 +1301,10 @@ int RegExpMacroAssemblerARM64::CheckStackGuardState(
|
||||
|
||||
void RegExpMacroAssemblerARM64::CheckPosition(int cp_offset,
|
||||
Label* on_outside_input) {
|
||||
if (cp_offset >= 0) {
|
||||
CompareAndBranchOrBacktrack(current_input_offset(),
|
||||
-cp_offset * char_size(), ge, on_outside_input);
|
||||
} else {
|
||||
__ Add(w12, current_input_offset(), Operand(cp_offset * char_size()));
|
||||
__ Cmp(w12, string_start_minus_one());
|
||||
BranchOrBacktrack(le, on_outside_input);
|
||||
}
|
||||
CompareAndBranchOrBacktrack(current_input_offset(),
|
||||
-cp_offset * char_size(),
|
||||
ge,
|
||||
on_outside_input);
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,11 +39,9 @@ class RegExpMacroAssemblerARM64: public NativeRegExpMacroAssembler {
|
||||
// A "greedy loop" is a loop that is both greedy and with a simple
|
||||
// body. It has a particularly simple implementation.
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotAtStart(Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
|
||||
virtual void CheckNotCharacterAfterAnd(unsigned c,
|
||||
@ -192,7 +190,7 @@ class RegExpMacroAssemblerARM64: public NativeRegExpMacroAssembler {
|
||||
Register code_pointer() { return x20; }
|
||||
|
||||
// Register holding the value used for clearing capture registers.
|
||||
Register string_start_minus_one() { return w24; }
|
||||
Register non_position_value() { return w24; }
|
||||
// The top 32 bit of this register is used to store this value
|
||||
// twice. This is used for clearing more than one register at a time.
|
||||
Register twice_non_position_value() { return x24; }
|
||||
|
@ -57,17 +57,15 @@ V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \
|
||||
V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_BACKWARD, 39, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD, 40, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_REGS_EQUAL, 41, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
|
||||
V(CHECK_REGISTER_LT, 42, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_GE, 43, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_EQ_POS, 44, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_AT_START, 45, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_NOT_AT_START, 46, 8) /* bc8 offset24 addr32 */ \
|
||||
V(CHECK_GREEDY, 47, 8) /* bc8 pad24 addr32 */ \
|
||||
V(ADVANCE_CP_AND_GOTO, 48, 8) /* bc8 offset24 addr32 */ \
|
||||
V(SET_CURRENT_POSITION_FROM_END, 49, 4) /* bc8 idx24 */
|
||||
V(CHECK_NOT_REGS_EQUAL, 39, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
|
||||
V(CHECK_REGISTER_LT, 40, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_GE, 41, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_EQ_POS, 42, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_AT_START, 43, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_NOT_AT_START, 44, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_GREEDY, 45, 8) /* bc8 pad24 addr32 */ \
|
||||
V(ADVANCE_CP_AND_GOTO, 46, 8) /* bc8 offset24 addr32 */ \
|
||||
V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */
|
||||
|
||||
#define DECLARE_BYTECODES(name, code, length) \
|
||||
static const int BC_##name = code;
|
||||
|
@ -53,8 +53,7 @@ namespace internal {
|
||||
* - backup of caller ebx
|
||||
* - success counter (only for global regexps to count matches).
|
||||
* - Offset of location before start of input (effectively character
|
||||
* string start - 1). Used to initialize capture registers to a
|
||||
* non-position.
|
||||
* position -1). Used to initialize capture registers to a non-position.
|
||||
* - register 0 ebp[-4] (only positions must be stored in the first
|
||||
* - register 1 ebp[-8] num_saved_registers_ registers)
|
||||
* - ...
|
||||
@ -157,16 +156,25 @@ void RegExpMacroAssemblerIA32::CheckCharacterGT(uc16 limit, Label* on_greater) {
|
||||
|
||||
|
||||
void RegExpMacroAssemblerIA32::CheckAtStart(Label* on_at_start) {
|
||||
__ lea(eax, Operand(edi, -char_size()));
|
||||
__ cmp(eax, Operand(ebp, kStringStartMinusOne));
|
||||
Label not_at_start;
|
||||
// Did we start the match at the start of the string at all?
|
||||
__ cmp(Operand(ebp, kStartIndex), Immediate(0));
|
||||
BranchOrBacktrack(not_equal, ¬_at_start);
|
||||
// If we did, are we still at the start of the input?
|
||||
__ lea(eax, Operand(esi, edi, times_1, 0));
|
||||
__ cmp(eax, Operand(ebp, kInputStart));
|
||||
BranchOrBacktrack(equal, on_at_start);
|
||||
__ bind(¬_at_start);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerIA32::CheckNotAtStart(int cp_offset,
|
||||
Label* on_not_at_start) {
|
||||
__ lea(eax, Operand(edi, -char_size() + cp_offset * char_size()));
|
||||
__ cmp(eax, Operand(ebp, kStringStartMinusOne));
|
||||
void RegExpMacroAssemblerIA32::CheckNotAtStart(Label* on_not_at_start) {
|
||||
// Did we start the match at the start of the string at all?
|
||||
__ cmp(Operand(ebp, kStartIndex), Immediate(0));
|
||||
BranchOrBacktrack(not_equal, on_not_at_start);
|
||||
// If we did, are we still at the start of the input?
|
||||
__ lea(eax, Operand(esi, edi, times_1, 0));
|
||||
__ cmp(eax, Operand(ebp, kInputStart));
|
||||
BranchOrBacktrack(not_equal, on_not_at_start);
|
||||
}
|
||||
|
||||
@ -188,28 +196,26 @@ void RegExpMacroAssemblerIA32::CheckGreedyLoop(Label* on_equal) {
|
||||
|
||||
|
||||
void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, Label* on_no_match) {
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
__ mov(edx, register_location(start_reg)); // Index of start of capture
|
||||
__ mov(ebx, register_location(start_reg + 1)); // Index of end of capture
|
||||
__ sub(ebx, edx); // Length of capture.
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
// The length of a capture should not be negative. This can only happen
|
||||
// if the end of the capture is unrecorded, or at a point earlier than
|
||||
// the start of the capture.
|
||||
BranchOrBacktrack(less, on_no_match);
|
||||
|
||||
// If length is zero, either the capture is empty or it is completely
|
||||
// uncaptured. In either case succeed immediately.
|
||||
__ j(equal, &fallthrough);
|
||||
|
||||
// Check that there are sufficient characters left in the input.
|
||||
if (read_backward) {
|
||||
__ mov(eax, Operand(ebp, kStringStartMinusOne));
|
||||
__ add(eax, ebx);
|
||||
__ cmp(edi, eax);
|
||||
BranchOrBacktrack(less_equal, on_no_match);
|
||||
} else {
|
||||
__ mov(eax, edi);
|
||||
__ add(eax, ebx);
|
||||
BranchOrBacktrack(greater, on_no_match);
|
||||
}
|
||||
__ mov(eax, edi);
|
||||
__ add(eax, ebx);
|
||||
BranchOrBacktrack(greater, on_no_match);
|
||||
|
||||
if (mode_ == LATIN1) {
|
||||
Label success;
|
||||
@ -222,9 +228,6 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
|
||||
|
||||
__ add(edx, esi); // Start of capture
|
||||
__ add(edi, esi); // Start of text to match against capture.
|
||||
if (read_backward) {
|
||||
__ sub(edi, ebx); // Offset by length when matching backwards.
|
||||
}
|
||||
__ add(ebx, edi); // End of text to match against capture.
|
||||
|
||||
Label loop;
|
||||
@ -275,11 +278,6 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
|
||||
__ add(esp, Immediate(kPointerSize));
|
||||
// Compute new value of character position after the matched part.
|
||||
__ sub(edi, esi);
|
||||
if (read_backward) {
|
||||
// Subtract match length if we matched backward.
|
||||
__ add(edi, register_location(start_reg));
|
||||
__ sub(edi, register_location(start_reg + 1));
|
||||
}
|
||||
} else {
|
||||
DCHECK(mode_ == UC16);
|
||||
// Save registers before calling C function.
|
||||
@ -306,9 +304,6 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
|
||||
// Found by adding negative string-end offset of current position (edi)
|
||||
// to end of string.
|
||||
__ add(edi, esi);
|
||||
if (read_backward) {
|
||||
__ sub(edi, ebx); // Offset by length when matching backwards.
|
||||
}
|
||||
__ mov(Operand(esp, 1 * kPointerSize), edi);
|
||||
// Set byte_offset1.
|
||||
// Start of capture, where edx already holds string-end negative offset.
|
||||
@ -330,20 +325,16 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
|
||||
// Check if function returned non-zero for success or zero for failure.
|
||||
__ or_(eax, eax);
|
||||
BranchOrBacktrack(zero, on_no_match);
|
||||
// On success, advance position by length of capture.
|
||||
if (read_backward) {
|
||||
__ sub(edi, ebx);
|
||||
} else {
|
||||
__ add(edi, ebx);
|
||||
}
|
||||
// On success, increment position by length of capture.
|
||||
__ add(edi, ebx);
|
||||
}
|
||||
__ bind(&fallthrough);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerIA32::CheckNotBackReference(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match) {
|
||||
void RegExpMacroAssemblerIA32::CheckNotBackReference(
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
Label success;
|
||||
Label fail;
|
||||
@ -352,33 +343,22 @@ void RegExpMacroAssemblerIA32::CheckNotBackReference(int start_reg,
|
||||
__ mov(edx, register_location(start_reg));
|
||||
__ mov(eax, register_location(start_reg + 1));
|
||||
__ sub(eax, edx); // Length to check.
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
// Fail on partial or illegal capture (start of capture after end of capture).
|
||||
BranchOrBacktrack(less, on_no_match);
|
||||
// Succeed on empty capture (including no capture)
|
||||
__ j(equal, &fallthrough);
|
||||
|
||||
// Check that there are sufficient characters left in the input.
|
||||
if (read_backward) {
|
||||
__ mov(ebx, Operand(ebp, kStringStartMinusOne));
|
||||
__ add(ebx, eax);
|
||||
__ cmp(edi, ebx);
|
||||
BranchOrBacktrack(less_equal, on_no_match);
|
||||
} else {
|
||||
__ mov(ebx, edi);
|
||||
__ add(ebx, eax);
|
||||
BranchOrBacktrack(greater, on_no_match);
|
||||
}
|
||||
__ mov(ebx, edi);
|
||||
__ add(ebx, eax);
|
||||
BranchOrBacktrack(greater, on_no_match);
|
||||
|
||||
// Save register to make it available below.
|
||||
__ push(backtrack_stackpointer());
|
||||
|
||||
// Compute pointers to match string and capture string
|
||||
__ add(edx, esi); // Start of capture.
|
||||
__ lea(ebx, Operand(esi, edi, times_1, 0)); // Start of match.
|
||||
if (read_backward) {
|
||||
__ sub(ebx, eax); // Offset by length when matching backwards.
|
||||
}
|
||||
__ add(edx, esi); // Start of capture.
|
||||
__ lea(ecx, Operand(eax, ebx, times_1, 0)); // End of match
|
||||
|
||||
Label loop;
|
||||
@ -409,11 +389,6 @@ void RegExpMacroAssemblerIA32::CheckNotBackReference(int start_reg,
|
||||
// Move current character position to position after match.
|
||||
__ mov(edi, ecx);
|
||||
__ sub(edi, esi);
|
||||
if (read_backward) {
|
||||
// Subtract match length if we matched backward.
|
||||
__ add(edi, register_location(start_reg));
|
||||
__ sub(edi, register_location(start_reg + 1));
|
||||
}
|
||||
// Restore backtrack stackpointer.
|
||||
__ pop(backtrack_stackpointer());
|
||||
|
||||
@ -659,7 +634,7 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
|
||||
__ push(edi);
|
||||
__ push(ebx); // Callee-save on MacOS.
|
||||
__ push(Immediate(0)); // Number of successful matches in a global regexp.
|
||||
__ push(Immediate(0)); // Make room for "string start - 1" constant.
|
||||
__ push(Immediate(0)); // Make room for "input start - 1" constant.
|
||||
|
||||
// Check if we have space on the stack for registers.
|
||||
Label stack_limit_hit;
|
||||
@ -709,7 +684,7 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
|
||||
}
|
||||
// Store this value in a local variable, for use when clearing
|
||||
// position registers.
|
||||
__ mov(Operand(ebp, kStringStartMinusOne), eax);
|
||||
__ mov(Operand(ebp, kInputStartMinusOne), eax);
|
||||
|
||||
#if V8_OS_WIN
|
||||
// Ensure that we write to each stack page, in order. Skipping a page
|
||||
@ -792,7 +767,7 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
|
||||
}
|
||||
|
||||
if (global()) {
|
||||
// Restart matching if the regular expression is flagged as global.
|
||||
// Restart matching if the regular expression is flagged as global.
|
||||
// Increment success counter.
|
||||
__ inc(Operand(ebp, kSuccessfulCaptures));
|
||||
// Capture results have been stored, so the number of remaining global
|
||||
@ -809,7 +784,7 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
|
||||
Immediate(num_saved_registers_ * kPointerSize));
|
||||
|
||||
// Prepare eax to initialize registers with its value in the next run.
|
||||
__ mov(eax, Operand(ebp, kStringStartMinusOne));
|
||||
__ mov(eax, Operand(ebp, kInputStartMinusOne));
|
||||
|
||||
if (global_with_zero_length_check()) {
|
||||
// Special case for zero-length matches.
|
||||
@ -969,13 +944,10 @@ void RegExpMacroAssemblerIA32::LoadCurrentCharacter(int cp_offset,
|
||||
Label* on_end_of_input,
|
||||
bool check_bounds,
|
||||
int characters) {
|
||||
DCHECK(cp_offset >= -1); // ^ and \b can look behind one character.
|
||||
DCHECK(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
|
||||
if (check_bounds) {
|
||||
if (cp_offset >= 0) {
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
} else {
|
||||
CheckPosition(cp_offset, on_end_of_input);
|
||||
}
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
}
|
||||
LoadCurrentCharacterUnchecked(cp_offset, characters);
|
||||
}
|
||||
@ -1059,7 +1031,7 @@ void RegExpMacroAssemblerIA32::WriteCurrentPositionToRegister(int reg,
|
||||
|
||||
void RegExpMacroAssemblerIA32::ClearRegisters(int reg_from, int reg_to) {
|
||||
DCHECK(reg_from <= reg_to);
|
||||
__ mov(eax, Operand(ebp, kStringStartMinusOne));
|
||||
__ mov(eax, Operand(ebp, kInputStartMinusOne));
|
||||
for (int reg = reg_from; reg <= reg_to; reg++) {
|
||||
__ mov(register_location(reg), eax);
|
||||
}
|
||||
@ -1128,14 +1100,8 @@ Operand RegExpMacroAssemblerIA32::register_location(int register_index) {
|
||||
|
||||
void RegExpMacroAssemblerIA32::CheckPosition(int cp_offset,
|
||||
Label* on_outside_input) {
|
||||
if (cp_offset >= 0) {
|
||||
__ cmp(edi, -cp_offset * char_size());
|
||||
BranchOrBacktrack(greater_equal, on_outside_input);
|
||||
} else {
|
||||
__ lea(eax, Operand(edi, cp_offset * char_size()));
|
||||
__ cmp(eax, Operand(ebp, kStringStartMinusOne));
|
||||
BranchOrBacktrack(less_equal, on_outside_input);
|
||||
}
|
||||
__ cmp(edi, -cp_offset * char_size());
|
||||
BranchOrBacktrack(greater_equal, on_outside_input);
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,11 +33,9 @@ class RegExpMacroAssemblerIA32: public NativeRegExpMacroAssembler {
|
||||
// A "greedy loop" is a loop that is both greedy and with a simple
|
||||
// body. It has a particularly simple implementation.
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotAtStart(Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
|
||||
virtual void CheckNotCharacterAfterAnd(uint32_t c,
|
||||
@ -118,9 +116,9 @@ class RegExpMacroAssemblerIA32: public NativeRegExpMacroAssembler {
|
||||
static const int kBackup_edi = kBackup_esi - kPointerSize;
|
||||
static const int kBackup_ebx = kBackup_edi - kPointerSize;
|
||||
static const int kSuccessfulCaptures = kBackup_ebx - kPointerSize;
|
||||
static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
// First register address. Following registers are below it on the stack.
|
||||
static const int kRegisterZero = kStringStartMinusOne - kPointerSize;
|
||||
static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
|
||||
|
||||
// Initial size of code buffer.
|
||||
static const size_t kRegExpCodeSize = 1024;
|
||||
|
@ -270,7 +270,7 @@ static RegExpImpl::IrregexpResult RawMatch(Isolate* isolate,
|
||||
break;
|
||||
BYTECODE(LOAD_CURRENT_CHAR) {
|
||||
int pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos >= subject.length() || pos < 0) {
|
||||
if (pos >= subject.length()) {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
} else {
|
||||
current_char = subject[pos];
|
||||
@ -286,7 +286,7 @@ static RegExpImpl::IrregexpResult RawMatch(Isolate* isolate,
|
||||
}
|
||||
BYTECODE(LOAD_2_CURRENT_CHARS) {
|
||||
int pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos + 2 > subject.length() || pos < 0) {
|
||||
if (pos + 2 > subject.length()) {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
} else {
|
||||
Char next = subject[pos + 1];
|
||||
@ -306,7 +306,7 @@ static RegExpImpl::IrregexpResult RawMatch(Isolate* isolate,
|
||||
BYTECODE(LOAD_4_CURRENT_CHARS) {
|
||||
DCHECK(sizeof(Char) == 1);
|
||||
int pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos + 4 > subject.length() || pos < 0) {
|
||||
if (pos + 4 > subject.length()) {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
} else {
|
||||
Char next1 = subject[pos + 1];
|
||||
@ -497,59 +497,46 @@ static RegExpImpl::IrregexpResult RawMatch(Isolate* isolate,
|
||||
BYTECODE(CHECK_NOT_BACK_REF) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current + len > subject.length() ||
|
||||
CompareChars(&subject[from], &subject[current], len) != 0) {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
break;
|
||||
if (from < 0 || len <= 0) {
|
||||
pc += BC_CHECK_NOT_BACK_REF_LENGTH;
|
||||
break;
|
||||
}
|
||||
if (current + len > subject.length()) {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
break;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (subject[from + i] != subject[current + i]) {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < len) break;
|
||||
current += len;
|
||||
}
|
||||
pc += BC_CHECK_NOT_BACK_REF_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_BACKWARD) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current - len < 0 ||
|
||||
CompareChars(&subject[from], &subject[current - len], len) != 0) {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
current -= len;
|
||||
}
|
||||
pc += BC_CHECK_NOT_BACK_REF_BACKWARD_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current + len > subject.length() ||
|
||||
!BackRefMatchesNoCase(isolate->interp_canonicalize_mapping(),
|
||||
from, current, len, subject)) {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
current += len;
|
||||
if (from < 0 || len <= 0) {
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
|
||||
break;
|
||||
}
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current - len < 0 ||
|
||||
!BackRefMatchesNoCase(isolate->interp_canonicalize_mapping(),
|
||||
from, current - len, len, subject)) {
|
||||
if (current + len > subject.length()) {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
break;
|
||||
} else {
|
||||
if (BackRefMatchesNoCase(isolate->interp_canonicalize_mapping(),
|
||||
from, current, len, subject)) {
|
||||
current += len;
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
|
||||
} else {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
current -= len;
|
||||
}
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_AT_START)
|
||||
@ -560,7 +547,7 @@ static RegExpImpl::IrregexpResult RawMatch(Isolate* isolate,
|
||||
}
|
||||
break;
|
||||
BYTECODE(CHECK_NOT_AT_START)
|
||||
if (current + (insn >> BYTECODE_SHIFT) == 0) {
|
||||
if (current == 0) {
|
||||
pc += BC_CHECK_NOT_AT_START_LENGTH;
|
||||
} else {
|
||||
pc = code_base + Load32Aligned(pc + 4);
|
||||
|
@ -1002,8 +1002,6 @@ class RegExpCompiler {
|
||||
inline void set_limiting_recursion(bool value) {
|
||||
limiting_recursion_ = value;
|
||||
}
|
||||
bool read_backward() { return read_backward_; }
|
||||
void set_read_backward(bool value) { read_backward_ = value; }
|
||||
FrequencyCollator* frequency_collator() { return &frequency_collator_; }
|
||||
|
||||
int current_expansion_factor() { return current_expansion_factor_; }
|
||||
@ -1027,7 +1025,6 @@ class RegExpCompiler {
|
||||
bool reg_exp_too_big_;
|
||||
bool limiting_recursion_;
|
||||
bool optimize_;
|
||||
bool read_backward_;
|
||||
int current_expansion_factor_;
|
||||
FrequencyCollator frequency_collator_;
|
||||
Isolate* isolate_;
|
||||
@ -1063,7 +1060,6 @@ RegExpCompiler::RegExpCompiler(Isolate* isolate, Zone* zone, int capture_count,
|
||||
reg_exp_too_big_(false),
|
||||
limiting_recursion_(false),
|
||||
optimize_(FLAG_regexp_optimization),
|
||||
read_backward_(false),
|
||||
current_expansion_factor_(1),
|
||||
frequency_collator_(),
|
||||
isolate_(isolate),
|
||||
@ -1228,8 +1224,7 @@ void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
|
||||
int value = 0;
|
||||
bool absolute = false;
|
||||
bool clear = false;
|
||||
static const int kNoStore = kMinInt;
|
||||
int store_position = kNoStore;
|
||||
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_;
|
||||
@ -1250,7 +1245,7 @@ void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
|
||||
// we can set undo_action to IGNORE if we know there is no value to
|
||||
// restore.
|
||||
undo_action = RESTORE;
|
||||
DCHECK_EQ(store_position, kNoStore);
|
||||
DCHECK_EQ(store_position, -1);
|
||||
DCHECK(!clear);
|
||||
break;
|
||||
}
|
||||
@ -1258,14 +1253,14 @@ void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
|
||||
if (!absolute) {
|
||||
value++;
|
||||
}
|
||||
DCHECK_EQ(store_position, kNoStore);
|
||||
DCHECK_EQ(store_position, -1);
|
||||
DCHECK(!clear);
|
||||
undo_action = RESTORE;
|
||||
break;
|
||||
case ActionNode::STORE_POSITION: {
|
||||
Trace::DeferredCapture* pc =
|
||||
static_cast<Trace::DeferredCapture*>(action);
|
||||
if (!clear && store_position == kNoStore) {
|
||||
if (!clear && store_position == -1) {
|
||||
store_position = pc->cp_offset();
|
||||
}
|
||||
|
||||
@ -1289,7 +1284,7 @@ void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
|
||||
// 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 == kNoStore) {
|
||||
if (store_position == -1) {
|
||||
clear = true;
|
||||
}
|
||||
undo_action = RESTORE;
|
||||
@ -1320,7 +1315,7 @@ void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
|
||||
}
|
||||
// Perform the chronologically last action (or accumulated increment)
|
||||
// for the register.
|
||||
if (store_position != kNoStore) {
|
||||
if (store_position != -1) {
|
||||
assembler->WriteCurrentPositionToRegister(reg, store_position);
|
||||
} else if (clear) {
|
||||
assembler->ClearRegisters(reg, reg);
|
||||
@ -2318,7 +2313,6 @@ void AssertionNode::FillInBMInfo(Isolate* isolate, int offset, int budget,
|
||||
int BackReferenceNode::EatsAtLeast(int still_to_find,
|
||||
int budget,
|
||||
bool not_at_start) {
|
||||
if (read_backward()) return 0;
|
||||
if (budget <= 0) return 0;
|
||||
return on_success()->EatsAtLeast(still_to_find,
|
||||
budget - 1,
|
||||
@ -2329,7 +2323,6 @@ int BackReferenceNode::EatsAtLeast(int still_to_find,
|
||||
int TextNode::EatsAtLeast(int still_to_find,
|
||||
int budget,
|
||||
bool not_at_start) {
|
||||
if (read_backward()) return 0;
|
||||
int answer = Length();
|
||||
if (answer >= still_to_find) return answer;
|
||||
if (budget <= 0) return answer;
|
||||
@ -2533,8 +2526,8 @@ void TextNode::GetQuickCheckDetails(QuickCheckDetails* details,
|
||||
} else {
|
||||
char_mask = String::kMaxUtf16CodeUnit;
|
||||
}
|
||||
for (int k = 0; k < elements()->length(); k++) {
|
||||
TextElement elm = elements()->at(k);
|
||||
for (int k = 0; k < elms_->length(); k++) {
|
||||
TextElement elm = elms_->at(k);
|
||||
if (elm.text_type() == TextElement::ATOM) {
|
||||
Vector<const uc16> quarks = elm.atom()->data();
|
||||
for (int i = 0; i < characters && i < quarks.length(); i++) {
|
||||
@ -2685,6 +2678,7 @@ void QuickCheckDetails::Clear() {
|
||||
|
||||
|
||||
void QuickCheckDetails::Advance(int by, bool one_byte) {
|
||||
DCHECK(by >= 0);
|
||||
if (by >= characters_) {
|
||||
Clear();
|
||||
return;
|
||||
@ -2786,9 +2780,9 @@ RegExpNode* TextNode::FilterOneByte(int depth, bool ignore_case) {
|
||||
if (depth < 0) return this;
|
||||
DCHECK(!info()->visited);
|
||||
VisitMarker marker(info());
|
||||
int element_count = elements()->length();
|
||||
int element_count = elms_->length();
|
||||
for (int i = 0; i < element_count; i++) {
|
||||
TextElement elm = elements()->at(i);
|
||||
TextElement elm = elms_->at(i);
|
||||
if (elm.text_type() == TextElement::ATOM) {
|
||||
Vector<const uc16> quarks = elm.atom()->data();
|
||||
for (int j = 0; j < quarks.length(); j++) {
|
||||
@ -3152,9 +3146,9 @@ void AssertionNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
||||
return;
|
||||
}
|
||||
if (trace->at_start() == Trace::UNKNOWN) {
|
||||
assembler->CheckNotAtStart(trace->cp_offset(), trace->backtrack());
|
||||
assembler->CheckNotAtStart(trace->backtrack());
|
||||
Trace at_start_trace = *trace;
|
||||
at_start_trace.set_at_start(Trace::TRUE_VALUE);
|
||||
at_start_trace.set_at_start(true);
|
||||
on_success()->Emit(compiler, &at_start_trace);
|
||||
return;
|
||||
}
|
||||
@ -3227,11 +3221,10 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler,
|
||||
bool one_byte = compiler->one_byte();
|
||||
Label* backtrack = trace->backtrack();
|
||||
QuickCheckDetails* quick_check = trace->quick_check_performed();
|
||||
int element_count = elements()->length();
|
||||
int backward_offset = read_backward() ? -Length() : 0;
|
||||
int element_count = elms_->length();
|
||||
for (int i = preloaded ? 0 : element_count - 1; i >= 0; i--) {
|
||||
TextElement elm = elements()->at(i);
|
||||
int cp_offset = trace->cp_offset() + elm.cp_offset() + backward_offset;
|
||||
TextElement elm = elms_->at(i);
|
||||
int cp_offset = trace->cp_offset() + elm.cp_offset();
|
||||
if (elm.text_type() == TextElement::ATOM) {
|
||||
Vector<const uc16> quarks = elm.atom()->data();
|
||||
for (int j = preloaded ? 0 : quarks.length() - 1; j >= 0; j--) {
|
||||
@ -3259,10 +3252,13 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler,
|
||||
break;
|
||||
}
|
||||
if (emit_function != NULL) {
|
||||
bool bounds_check = *checked_up_to < cp_offset + j || read_backward();
|
||||
bool bound_checked =
|
||||
emit_function(isolate, compiler, quarks[j], backtrack,
|
||||
cp_offset + j, bounds_check, preloaded);
|
||||
bool bound_checked = emit_function(isolate,
|
||||
compiler,
|
||||
quarks[j],
|
||||
backtrack,
|
||||
cp_offset + j,
|
||||
*checked_up_to < cp_offset + j,
|
||||
preloaded);
|
||||
if (bound_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
|
||||
}
|
||||
}
|
||||
@ -3272,9 +3268,8 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler,
|
||||
if (first_element_checked && i == 0) continue;
|
||||
if (DeterminedAlready(quick_check, elm.cp_offset())) continue;
|
||||
RegExpCharacterClass* cc = elm.char_class();
|
||||
bool bounds_check = *checked_up_to < cp_offset || read_backward();
|
||||
EmitCharClass(assembler, cc, one_byte, backtrack, cp_offset,
|
||||
bounds_check, preloaded, zone());
|
||||
*checked_up_to < cp_offset, preloaded, zone());
|
||||
UpdateBoundsCheck(cp_offset, checked_up_to);
|
||||
}
|
||||
}
|
||||
@ -3283,7 +3278,7 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler,
|
||||
|
||||
|
||||
int TextNode::Length() {
|
||||
TextElement elm = elements()->last();
|
||||
TextElement elm = elms_->last();
|
||||
DCHECK(elm.cp_offset() >= 0);
|
||||
return elm.cp_offset() + elm.length();
|
||||
}
|
||||
@ -3352,11 +3347,8 @@ void TextNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
||||
}
|
||||
|
||||
Trace successor_trace(*trace);
|
||||
// If we advance backward, we may end up at the start.
|
||||
successor_trace.AdvanceCurrentPositionInTrace(
|
||||
read_backward() ? -Length() : Length(), compiler);
|
||||
successor_trace.set_at_start(read_backward() ? Trace::UNKNOWN
|
||||
: Trace::FALSE_VALUE);
|
||||
successor_trace.set_at_start(false);
|
||||
successor_trace.AdvanceCurrentPositionInTrace(Length(), compiler);
|
||||
RecursionCheck rc(compiler);
|
||||
on_success()->Emit(compiler, &successor_trace);
|
||||
}
|
||||
@ -3368,6 +3360,7 @@ void Trace::InvalidateCurrentCharacter() {
|
||||
|
||||
|
||||
void Trace::AdvanceCurrentPositionInTrace(int by, RegExpCompiler* compiler) {
|
||||
DCHECK(by > 0);
|
||||
// We don't have an instruction for shifting the current character register
|
||||
// down or for using a shifted value for anything so lets just forget that
|
||||
// we preloaded any characters into it.
|
||||
@ -3386,9 +3379,9 @@ void Trace::AdvanceCurrentPositionInTrace(int by, RegExpCompiler* compiler) {
|
||||
|
||||
|
||||
void TextNode::MakeCaseIndependent(Isolate* isolate, bool is_one_byte) {
|
||||
int element_count = elements()->length();
|
||||
int element_count = elms_->length();
|
||||
for (int i = 0; i < element_count; i++) {
|
||||
TextElement elm = elements()->at(i);
|
||||
TextElement elm = elms_->at(i);
|
||||
if (elm.text_type() == TextElement::CHAR_CLASS) {
|
||||
RegExpCharacterClass* cc = elm.char_class();
|
||||
// None of the standard character classes is different in the case
|
||||
@ -3404,14 +3397,16 @@ void TextNode::MakeCaseIndependent(Isolate* isolate, bool is_one_byte) {
|
||||
}
|
||||
|
||||
|
||||
int TextNode::GreedyLoopTextLength() { return Length(); }
|
||||
int TextNode::GreedyLoopTextLength() {
|
||||
TextElement elm = elms_->at(elms_->length() - 1);
|
||||
return elm.cp_offset() + elm.length();
|
||||
}
|
||||
|
||||
|
||||
RegExpNode* TextNode::GetSuccessorOfOmnivorousTextNode(
|
||||
RegExpCompiler* compiler) {
|
||||
if (read_backward()) return NULL;
|
||||
if (elements()->length() != 1) return NULL;
|
||||
TextElement elm = elements()->at(0);
|
||||
if (elms_->length() != 1) return NULL;
|
||||
TextElement elm = elms_->at(0);
|
||||
if (elm.text_type() != TextElement::CHAR_CLASS) return NULL;
|
||||
RegExpCharacterClass* node = elm.char_class();
|
||||
ZoneList<CharacterRange>* ranges = node->ranges(zone());
|
||||
@ -3455,7 +3450,7 @@ int ChoiceNode::GreedyLoopTextLengthForAlternative(
|
||||
SeqRegExpNode* seq_node = static_cast<SeqRegExpNode*>(node);
|
||||
node = seq_node->on_success();
|
||||
}
|
||||
return read_backward() ? -length : length;
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
@ -3886,7 +3881,7 @@ void BoyerMooreLookahead::EmitSkipInstructions(RegExpMacroAssembler* masm) {
|
||||
|
||||
GreedyLoopState::GreedyLoopState(bool not_at_start) {
|
||||
counter_backtrack_trace_.set_backtrack(&label_);
|
||||
if (not_at_start) counter_backtrack_trace_.set_at_start(Trace::FALSE_VALUE);
|
||||
if (not_at_start) counter_backtrack_trace_.set_at_start(false);
|
||||
}
|
||||
|
||||
|
||||
@ -4013,7 +4008,7 @@ Trace* ChoiceNode::EmitGreedyLoop(RegExpCompiler* compiler,
|
||||
macro_assembler->PushCurrentPosition();
|
||||
Label greedy_match_failed;
|
||||
Trace greedy_match_trace;
|
||||
if (not_at_start()) greedy_match_trace.set_at_start(Trace::FALSE_VALUE);
|
||||
if (not_at_start()) greedy_match_trace.set_at_start(false);
|
||||
greedy_match_trace.set_backtrack(&greedy_match_failed);
|
||||
Label loop_label;
|
||||
macro_assembler->Bind(&loop_label);
|
||||
@ -4359,14 +4354,11 @@ void BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
||||
|
||||
DCHECK_EQ(start_reg_ + 1, end_reg_);
|
||||
if (compiler->ignore_case()) {
|
||||
assembler->CheckNotBackReferenceIgnoreCase(start_reg_, read_backward(),
|
||||
assembler->CheckNotBackReferenceIgnoreCase(start_reg_,
|
||||
trace->backtrack());
|
||||
} else {
|
||||
assembler->CheckNotBackReference(start_reg_, read_backward(),
|
||||
trace->backtrack());
|
||||
assembler->CheckNotBackReference(start_reg_, trace->backtrack());
|
||||
}
|
||||
// We are going to advance backward, so we may end up at the start.
|
||||
if (read_backward()) trace->set_at_start(Trace::UNKNOWN);
|
||||
on_success()->Emit(compiler, trace);
|
||||
}
|
||||
|
||||
@ -4727,15 +4719,13 @@ RegExpNode* RegExpAtom::ToNode(RegExpCompiler* compiler,
|
||||
ZoneList<TextElement>* elms =
|
||||
new(compiler->zone()) ZoneList<TextElement>(1, compiler->zone());
|
||||
elms->Add(TextElement::Atom(this), compiler->zone());
|
||||
return new (compiler->zone())
|
||||
TextNode(elms, compiler->read_backward(), on_success);
|
||||
return new(compiler->zone()) TextNode(elms, on_success);
|
||||
}
|
||||
|
||||
|
||||
RegExpNode* RegExpText::ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success) {
|
||||
return new (compiler->zone())
|
||||
TextNode(elements(), compiler->read_backward(), on_success);
|
||||
return new(compiler->zone()) TextNode(elements(), on_success);
|
||||
}
|
||||
|
||||
|
||||
@ -4832,8 +4822,7 @@ bool RegExpCharacterClass::is_standard(Zone* zone) {
|
||||
|
||||
RegExpNode* RegExpCharacterClass::ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success) {
|
||||
return new (compiler->zone())
|
||||
TextNode(this, compiler->read_backward(), on_success);
|
||||
return new(compiler->zone()) TextNode(this, on_success);
|
||||
}
|
||||
|
||||
|
||||
@ -5215,9 +5204,7 @@ RegExpNode* RegExpQuantifier::ToNode(int min,
|
||||
GuardedAlternative(body->ToNode(compiler, answer)));
|
||||
}
|
||||
answer = alternation;
|
||||
if (not_at_start && !compiler->read_backward()) {
|
||||
alternation->set_not_at_start();
|
||||
}
|
||||
if (not_at_start) alternation->set_not_at_start();
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
@ -5229,9 +5216,9 @@ RegExpNode* RegExpQuantifier::ToNode(int min,
|
||||
int reg_ctr = needs_counter
|
||||
? compiler->AllocateRegister()
|
||||
: RegExpCompiler::kNoRegister;
|
||||
LoopChoiceNode* center = new (zone)
|
||||
LoopChoiceNode(body->min_match() == 0, compiler->read_backward(), zone);
|
||||
if (not_at_start && !compiler->read_backward()) center->set_not_at_start();
|
||||
LoopChoiceNode* center = new(zone) LoopChoiceNode(body->min_match() == 0,
|
||||
zone);
|
||||
if (not_at_start) center->set_not_at_start();
|
||||
RegExpNode* loop_return = needs_counter
|
||||
? static_cast<RegExpNode*>(ActionNode::IncrementRegister(reg_ctr, center))
|
||||
: static_cast<RegExpNode*>(center);
|
||||
@ -5307,13 +5294,14 @@ RegExpNode* RegExpAssertion::ToNode(RegExpCompiler* compiler,
|
||||
ZoneList<CharacterRange>* newline_ranges =
|
||||
new(zone) ZoneList<CharacterRange>(3, zone);
|
||||
CharacterRange::AddClassEscape('n', newline_ranges, zone);
|
||||
RegExpCharacterClass* newline_atom = new (zone) RegExpCharacterClass('n');
|
||||
TextNode* newline_matcher = new (zone) TextNode(
|
||||
newline_atom, false, ActionNode::PositiveSubmatchSuccess(
|
||||
stack_pointer_register, position_register,
|
||||
0, // No captures inside.
|
||||
-1, // Ignored if no captures.
|
||||
on_success));
|
||||
RegExpCharacterClass* newline_atom = new(zone) RegExpCharacterClass('n');
|
||||
TextNode* newline_matcher = new(zone) TextNode(
|
||||
newline_atom,
|
||||
ActionNode::PositiveSubmatchSuccess(stack_pointer_register,
|
||||
position_register,
|
||||
0, // No captures inside.
|
||||
-1, // Ignored if no captures.
|
||||
on_success));
|
||||
// Create an end-of-input matcher.
|
||||
RegExpNode* end_of_line = ActionNode::BeginSubmatch(
|
||||
stack_pointer_register,
|
||||
@ -5335,10 +5323,10 @@ RegExpNode* RegExpAssertion::ToNode(RegExpCompiler* compiler,
|
||||
|
||||
RegExpNode* RegExpBackReference::ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success) {
|
||||
return new (compiler->zone())
|
||||
return new(compiler->zone())
|
||||
BackReferenceNode(RegExpCapture::StartRegister(index()),
|
||||
RegExpCapture::EndRegister(index()),
|
||||
compiler->read_backward(), on_success);
|
||||
on_success);
|
||||
}
|
||||
|
||||
|
||||
@ -5348,8 +5336,8 @@ RegExpNode* RegExpEmpty::ToNode(RegExpCompiler* compiler,
|
||||
}
|
||||
|
||||
|
||||
RegExpNode* RegExpLookaround::ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success) {
|
||||
RegExpNode* RegExpLookahead::ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success) {
|
||||
int stack_pointer_register = compiler->AllocateRegister();
|
||||
int position_register = compiler->AllocateRegister();
|
||||
|
||||
@ -5359,16 +5347,19 @@ RegExpNode* RegExpLookaround::ToNode(RegExpCompiler* compiler,
|
||||
int register_start =
|
||||
register_of_first_capture + capture_from_ * registers_per_capture;
|
||||
|
||||
RegExpNode* result;
|
||||
bool was_reading_backward = compiler->read_backward();
|
||||
compiler->set_read_backward(type() == LOOKBEHIND);
|
||||
RegExpNode* success;
|
||||
if (is_positive()) {
|
||||
result = ActionNode::BeginSubmatch(
|
||||
stack_pointer_register, position_register,
|
||||
body()->ToNode(compiler,
|
||||
ActionNode::PositiveSubmatchSuccess(
|
||||
stack_pointer_register, position_register,
|
||||
register_count, register_start, on_success)));
|
||||
RegExpNode* node = ActionNode::BeginSubmatch(
|
||||
stack_pointer_register,
|
||||
position_register,
|
||||
body()->ToNode(
|
||||
compiler,
|
||||
ActionNode::PositiveSubmatchSuccess(stack_pointer_register,
|
||||
position_register,
|
||||
register_count,
|
||||
register_start,
|
||||
on_success)));
|
||||
return node;
|
||||
} else {
|
||||
// We use a ChoiceNode for a negative lookahead because it has most of
|
||||
// the characteristics we need. It has the body of the lookahead as its
|
||||
@ -5383,18 +5374,21 @@ RegExpNode* RegExpLookaround::ToNode(RegExpCompiler* compiler,
|
||||
Zone* zone = compiler->zone();
|
||||
|
||||
GuardedAlternative body_alt(
|
||||
body()->ToNode(compiler, new (zone) NegativeSubmatchSuccess(
|
||||
stack_pointer_register, position_register,
|
||||
register_count, register_start, zone)));
|
||||
body()->ToNode(
|
||||
compiler,
|
||||
success = new(zone) NegativeSubmatchSuccess(stack_pointer_register,
|
||||
position_register,
|
||||
register_count,
|
||||
register_start,
|
||||
zone)));
|
||||
ChoiceNode* choice_node =
|
||||
new(zone) NegativeLookaheadChoiceNode(body_alt,
|
||||
GuardedAlternative(on_success),
|
||||
zone);
|
||||
result = ActionNode::BeginSubmatch(stack_pointer_register,
|
||||
position_register, choice_node);
|
||||
return ActionNode::BeginSubmatch(stack_pointer_register,
|
||||
position_register,
|
||||
choice_node);
|
||||
}
|
||||
compiler->set_read_backward(was_reading_backward);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -5408,10 +5402,8 @@ RegExpNode* RegExpCapture::ToNode(RegExpTree* body,
|
||||
int index,
|
||||
RegExpCompiler* compiler,
|
||||
RegExpNode* on_success) {
|
||||
DCHECK_NOT_NULL(body);
|
||||
int start_reg = RegExpCapture::StartRegister(index);
|
||||
int end_reg = RegExpCapture::EndRegister(index);
|
||||
if (compiler->read_backward()) std::swap(start_reg, end_reg);
|
||||
RegExpNode* store_end = ActionNode::StorePosition(end_reg, true, on_success);
|
||||
RegExpNode* body_node = body->ToNode(compiler, store_end);
|
||||
return ActionNode::StorePosition(start_reg, true, body_node);
|
||||
@ -5422,14 +5414,8 @@ RegExpNode* RegExpAlternative::ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success) {
|
||||
ZoneList<RegExpTree*>* children = nodes();
|
||||
RegExpNode* current = on_success;
|
||||
if (compiler->read_backward()) {
|
||||
for (int i = 0; i < children->length(); i++) {
|
||||
current = children->at(i)->ToNode(compiler, current);
|
||||
}
|
||||
} else {
|
||||
for (int i = children->length() - 1; i >= 0; i--) {
|
||||
current = children->at(i)->ToNode(compiler, current);
|
||||
}
|
||||
for (int i = children->length() - 1; i >= 0; i--) {
|
||||
current = children->at(i)->ToNode(compiler, current);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
@ -6305,17 +6291,22 @@ RegExpEngine::CompilationResult RegExpEngine::Compile(
|
||||
if (!is_start_anchored && !is_sticky) {
|
||||
// Add a .*? at the beginning, outside the body capture, unless
|
||||
// this expression is anchored at the beginning or sticky.
|
||||
RegExpNode* loop_node = RegExpQuantifier::ToNode(
|
||||
0, RegExpTree::kInfinity, false, new (zone) RegExpCharacterClass('*'),
|
||||
&compiler, captured_body, data->contains_anchor);
|
||||
RegExpNode* loop_node =
|
||||
RegExpQuantifier::ToNode(0,
|
||||
RegExpTree::kInfinity,
|
||||
false,
|
||||
new(zone) RegExpCharacterClass('*'),
|
||||
&compiler,
|
||||
captured_body,
|
||||
data->contains_anchor);
|
||||
|
||||
if (data->contains_anchor) {
|
||||
// Unroll loop once, to take care of the case that might start
|
||||
// at the start of input.
|
||||
ChoiceNode* first_step_node = new(zone) ChoiceNode(2, zone);
|
||||
first_step_node->AddAlternative(GuardedAlternative(captured_body));
|
||||
first_step_node->AddAlternative(GuardedAlternative(new (zone) TextNode(
|
||||
new (zone) RegExpCharacterClass('*'), false, loop_node)));
|
||||
first_step_node->AddAlternative(GuardedAlternative(
|
||||
new(zone) TextNode(new(zone) RegExpCharacterClass('*'), loop_node)));
|
||||
node = first_step_node;
|
||||
} else {
|
||||
node = loop_node;
|
||||
|
@ -387,17 +387,17 @@ class DispatchTable : public ZoneObject {
|
||||
VISIT(Text)
|
||||
|
||||
|
||||
#define FOR_EACH_REG_EXP_TREE_TYPE(VISIT) \
|
||||
VISIT(Disjunction) \
|
||||
VISIT(Alternative) \
|
||||
VISIT(Assertion) \
|
||||
VISIT(CharacterClass) \
|
||||
VISIT(Atom) \
|
||||
VISIT(Quantifier) \
|
||||
VISIT(Capture) \
|
||||
VISIT(Lookaround) \
|
||||
VISIT(BackReference) \
|
||||
VISIT(Empty) \
|
||||
#define FOR_EACH_REG_EXP_TREE_TYPE(VISIT) \
|
||||
VISIT(Disjunction) \
|
||||
VISIT(Alternative) \
|
||||
VISIT(Assertion) \
|
||||
VISIT(CharacterClass) \
|
||||
VISIT(Atom) \
|
||||
VISIT(Quantifier) \
|
||||
VISIT(Capture) \
|
||||
VISIT(Lookahead) \
|
||||
VISIT(BackReference) \
|
||||
VISIT(Empty) \
|
||||
VISIT(Text)
|
||||
|
||||
|
||||
@ -603,7 +603,7 @@ class RegExpNode: public ZoneObject {
|
||||
RegExpCompiler* compiler,
|
||||
int characters_filled_in,
|
||||
bool not_at_start) = 0;
|
||||
static const int kNodeIsTooComplexForGreedyLoops = kMinInt;
|
||||
static const int kNodeIsTooComplexForGreedyLoops = -1;
|
||||
virtual int GreedyLoopTextLength() { return kNodeIsTooComplexForGreedyLoops; }
|
||||
// Only returns the successor for a text node of length 1 that matches any
|
||||
// character and that has no guards on it.
|
||||
@ -827,14 +827,14 @@ class ActionNode: public SeqRegExpNode {
|
||||
|
||||
class TextNode: public SeqRegExpNode {
|
||||
public:
|
||||
TextNode(ZoneList<TextElement>* elms, bool read_backward,
|
||||
RegExpNode* on_success)
|
||||
: SeqRegExpNode(on_success), elms_(elms), read_backward_(read_backward) {}
|
||||
TextNode(RegExpCharacterClass* that, bool read_backward,
|
||||
TextNode(ZoneList<TextElement>* elms,
|
||||
RegExpNode* on_success)
|
||||
: SeqRegExpNode(on_success),
|
||||
elms_(new (zone()) ZoneList<TextElement>(1, zone())),
|
||||
read_backward_(read_backward) {
|
||||
elms_(elms) { }
|
||||
TextNode(RegExpCharacterClass* that,
|
||||
RegExpNode* on_success)
|
||||
: SeqRegExpNode(on_success),
|
||||
elms_(new(zone()) ZoneList<TextElement>(1, zone())) {
|
||||
elms_->Add(TextElement::CharClass(that), zone());
|
||||
}
|
||||
virtual void Accept(NodeVisitor* visitor);
|
||||
@ -845,7 +845,6 @@ class TextNode: public SeqRegExpNode {
|
||||
int characters_filled_in,
|
||||
bool not_at_start);
|
||||
ZoneList<TextElement>* elements() { return elms_; }
|
||||
bool read_backward() { return read_backward_; }
|
||||
void MakeCaseIndependent(Isolate* isolate, bool is_one_byte);
|
||||
virtual int GreedyLoopTextLength();
|
||||
virtual RegExpNode* GetSuccessorOfOmnivorousTextNode(
|
||||
@ -874,7 +873,6 @@ class TextNode: public SeqRegExpNode {
|
||||
int* checked_up_to);
|
||||
int Length();
|
||||
ZoneList<TextElement>* elms_;
|
||||
bool read_backward_;
|
||||
};
|
||||
|
||||
|
||||
@ -927,16 +925,15 @@ class AssertionNode: public SeqRegExpNode {
|
||||
|
||||
class BackReferenceNode: public SeqRegExpNode {
|
||||
public:
|
||||
BackReferenceNode(int start_reg, int end_reg, bool read_backward,
|
||||
BackReferenceNode(int start_reg,
|
||||
int end_reg,
|
||||
RegExpNode* on_success)
|
||||
: SeqRegExpNode(on_success),
|
||||
start_reg_(start_reg),
|
||||
end_reg_(end_reg),
|
||||
read_backward_(read_backward) {}
|
||||
end_reg_(end_reg) { }
|
||||
virtual void Accept(NodeVisitor* visitor);
|
||||
int start_register() { return start_reg_; }
|
||||
int end_register() { return end_reg_; }
|
||||
bool read_backward() { return read_backward_; }
|
||||
virtual void Emit(RegExpCompiler* compiler, Trace* trace);
|
||||
virtual int EatsAtLeast(int still_to_find,
|
||||
int recursion_depth,
|
||||
@ -953,7 +950,6 @@ class BackReferenceNode: public SeqRegExpNode {
|
||||
private:
|
||||
int start_reg_;
|
||||
int end_reg_;
|
||||
bool read_backward_;
|
||||
};
|
||||
|
||||
|
||||
@ -1078,7 +1074,6 @@ class ChoiceNode: public RegExpNode {
|
||||
return true;
|
||||
}
|
||||
virtual RegExpNode* FilterOneByte(int depth, bool ignore_case);
|
||||
virtual bool read_backward() { return false; }
|
||||
|
||||
protected:
|
||||
int GreedyLoopTextLengthForAlternative(GuardedAlternative* alternative);
|
||||
@ -1155,12 +1150,12 @@ class NegativeLookaheadChoiceNode: public ChoiceNode {
|
||||
|
||||
class LoopChoiceNode: public ChoiceNode {
|
||||
public:
|
||||
LoopChoiceNode(bool body_can_be_zero_length, bool read_backward, Zone* zone)
|
||||
explicit LoopChoiceNode(bool body_can_be_zero_length, Zone* zone)
|
||||
: ChoiceNode(2, zone),
|
||||
loop_node_(NULL),
|
||||
continue_node_(NULL),
|
||||
body_can_be_zero_length_(body_can_be_zero_length),
|
||||
read_backward_(read_backward) {}
|
||||
body_can_be_zero_length_(body_can_be_zero_length)
|
||||
{ }
|
||||
void AddLoopAlternative(GuardedAlternative alt);
|
||||
void AddContinueAlternative(GuardedAlternative alt);
|
||||
virtual void Emit(RegExpCompiler* compiler, Trace* trace);
|
||||
@ -1174,7 +1169,6 @@ class LoopChoiceNode: public ChoiceNode {
|
||||
RegExpNode* loop_node() { return loop_node_; }
|
||||
RegExpNode* continue_node() { return continue_node_; }
|
||||
bool body_can_be_zero_length() { return body_can_be_zero_length_; }
|
||||
virtual bool read_backward() { return read_backward_; }
|
||||
virtual void Accept(NodeVisitor* visitor);
|
||||
virtual RegExpNode* FilterOneByte(int depth, bool ignore_case);
|
||||
|
||||
@ -1189,7 +1183,6 @@ class LoopChoiceNode: public ChoiceNode {
|
||||
RegExpNode* loop_node_;
|
||||
RegExpNode* continue_node_;
|
||||
bool body_can_be_zero_length_;
|
||||
bool read_backward_;
|
||||
};
|
||||
|
||||
|
||||
@ -1445,7 +1438,9 @@ class Trace {
|
||||
at_start_ == UNKNOWN;
|
||||
}
|
||||
TriBool at_start() { return at_start_; }
|
||||
void set_at_start(TriBool at_start) { at_start_ = at_start; }
|
||||
void set_at_start(bool at_start) {
|
||||
at_start_ = at_start ? TRUE_VALUE : FALSE_VALUE;
|
||||
}
|
||||
Label* backtrack() { return backtrack_; }
|
||||
Label* loop_label() { return loop_label_; }
|
||||
RegExpNode* stop_node() { return stop_node_; }
|
||||
|
@ -181,17 +181,26 @@ void RegExpMacroAssemblerMIPS::CheckCharacterGT(uc16 limit, Label* on_greater) {
|
||||
|
||||
|
||||
void RegExpMacroAssemblerMIPS::CheckAtStart(Label* on_at_start) {
|
||||
__ lw(a1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ Addu(a0, current_input_offset(), Operand(-char_size()));
|
||||
Label not_at_start;
|
||||
// Did we start the match at the start of the string at all?
|
||||
__ lw(a0, MemOperand(frame_pointer(), kStartIndex));
|
||||
BranchOrBacktrack(¬_at_start, ne, a0, Operand(zero_reg));
|
||||
|
||||
// If we did, are we still at the start of the input?
|
||||
__ lw(a1, MemOperand(frame_pointer(), kInputStart));
|
||||
__ Addu(a0, end_of_input_address(), Operand(current_input_offset()));
|
||||
BranchOrBacktrack(on_at_start, eq, a0, Operand(a1));
|
||||
__ bind(¬_at_start);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerMIPS::CheckNotAtStart(int cp_offset,
|
||||
Label* on_not_at_start) {
|
||||
__ lw(a1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ Addu(a0, current_input_offset(),
|
||||
Operand(-char_size() + cp_offset * char_size()));
|
||||
void RegExpMacroAssemblerMIPS::CheckNotAtStart(Label* on_not_at_start) {
|
||||
// Did we start the match at the start of the string at all?
|
||||
__ lw(a0, MemOperand(frame_pointer(), kStartIndex));
|
||||
BranchOrBacktrack(on_not_at_start, ne, a0, Operand(zero_reg));
|
||||
// If we did, are we still at the start of the input?
|
||||
__ lw(a1, MemOperand(frame_pointer(), kInputStart));
|
||||
__ Addu(a0, end_of_input_address(), Operand(current_input_offset()));
|
||||
BranchOrBacktrack(on_not_at_start, ne, a0, Operand(a1));
|
||||
}
|
||||
|
||||
@ -214,26 +223,20 @@ void RegExpMacroAssemblerMIPS::CheckGreedyLoop(Label* on_equal) {
|
||||
|
||||
|
||||
void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, Label* on_no_match) {
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
__ lw(a0, register_location(start_reg)); // Index of start of capture.
|
||||
__ lw(a1, register_location(start_reg + 1)); // Index of end of capture.
|
||||
__ Subu(a1, a1, a0); // Length of capture.
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
// If length is zero, either the capture is empty or it is not participating.
|
||||
// In either case succeed immediately.
|
||||
__ Branch(&fallthrough, eq, a1, Operand(zero_reg));
|
||||
|
||||
if (read_backward) {
|
||||
__ lw(t0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ Addu(t0, t0, a1);
|
||||
BranchOrBacktrack(on_no_match, le, current_input_offset(), Operand(t0));
|
||||
} else {
|
||||
__ Addu(t5, a1, current_input_offset());
|
||||
// Check that there are enough characters left in the input.
|
||||
BranchOrBacktrack(on_no_match, gt, t5, Operand(zero_reg));
|
||||
}
|
||||
__ Addu(t5, a1, current_input_offset());
|
||||
// Check that there are enough characters left in the input.
|
||||
BranchOrBacktrack(on_no_match, gt, t5, Operand(zero_reg));
|
||||
|
||||
if (mode_ == LATIN1) {
|
||||
Label success;
|
||||
@ -244,9 +247,6 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
|
||||
// a1 - length of capture.
|
||||
__ Addu(a0, a0, Operand(end_of_input_address()));
|
||||
__ Addu(a2, end_of_input_address(), Operand(current_input_offset()));
|
||||
if (read_backward) {
|
||||
__ Subu(a2, a2, Operand(a1));
|
||||
}
|
||||
__ Addu(a1, a0, Operand(a1));
|
||||
|
||||
// a0 - Address of start of capture.
|
||||
@ -285,12 +285,6 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
|
||||
__ bind(&success);
|
||||
// Compute new value of character position after the matched part.
|
||||
__ Subu(current_input_offset(), a2, end_of_input_address());
|
||||
if (read_backward) {
|
||||
__ lw(t0, register_location(start_reg)); // Index of start of capture.
|
||||
__ lw(t5, register_location(start_reg + 1)); // Index of end of capture.
|
||||
__ Addu(current_input_offset(), current_input_offset(), Operand(t0));
|
||||
__ Subu(current_input_offset(), current_input_offset(), Operand(t5));
|
||||
}
|
||||
} else {
|
||||
DCHECK(mode_ == UC16);
|
||||
// Put regexp engine registers on stack.
|
||||
@ -319,9 +313,6 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
|
||||
__ mov(s3, a1);
|
||||
// Address of current input position.
|
||||
__ Addu(a1, current_input_offset(), Operand(end_of_input_address()));
|
||||
if (read_backward) {
|
||||
__ Subu(a1, a1, Operand(s3));
|
||||
}
|
||||
// Isolate.
|
||||
__ li(a3, Operand(ExternalReference::isolate_address(masm_->isolate())));
|
||||
|
||||
@ -339,21 +330,17 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
|
||||
|
||||
// Check if function returned non-zero for success or zero for failure.
|
||||
BranchOrBacktrack(on_no_match, eq, v0, Operand(zero_reg));
|
||||
// On success, advance position by length of capture.
|
||||
if (read_backward) {
|
||||
__ Subu(current_input_offset(), current_input_offset(), Operand(s3));
|
||||
} else {
|
||||
__ Addu(current_input_offset(), current_input_offset(), Operand(s3));
|
||||
}
|
||||
// On success, increment position by length of capture.
|
||||
__ Addu(current_input_offset(), current_input_offset(), Operand(s3));
|
||||
}
|
||||
|
||||
__ bind(&fallthrough);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerMIPS::CheckNotBackReference(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match) {
|
||||
void RegExpMacroAssemblerMIPS::CheckNotBackReference(
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
Label success;
|
||||
|
||||
@ -361,35 +348,17 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReference(int start_reg,
|
||||
__ lw(a0, register_location(start_reg));
|
||||
__ lw(a1, register_location(start_reg + 1));
|
||||
__ Subu(a1, a1, a0); // Length to check.
|
||||
// Succeed on empty capture (including no capture).
|
||||
__ Branch(&fallthrough, eq, a1, Operand(zero_reg));
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
__ Branch(&fallthrough, le, a1, Operand(zero_reg));
|
||||
__ Addu(t5, a1, current_input_offset());
|
||||
// Check that there are enough characters left in the input.
|
||||
BranchOrBacktrack(on_no_match, gt, t5, Operand(zero_reg));
|
||||
|
||||
if (read_backward) {
|
||||
__ lw(t0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ Addu(t0, t0, a1);
|
||||
BranchOrBacktrack(on_no_match, le, current_input_offset(), Operand(t0));
|
||||
} else {
|
||||
__ Addu(t5, a1, current_input_offset());
|
||||
// Check that there are enough characters left in the input.
|
||||
BranchOrBacktrack(on_no_match, gt, t5, Operand(zero_reg));
|
||||
}
|
||||
|
||||
// a0 - offset of start of capture.
|
||||
// a1 - length of capture.
|
||||
// Compute pointers to match string and capture string.
|
||||
__ Addu(a0, a0, Operand(end_of_input_address()));
|
||||
__ Addu(a2, end_of_input_address(), Operand(current_input_offset()));
|
||||
if (read_backward) {
|
||||
__ Subu(a2, a2, Operand(a1));
|
||||
}
|
||||
__ Addu(a1, a0, Operand(a1));
|
||||
|
||||
// a0 - Address of start of capture.
|
||||
// a1 - Address of end of capture.
|
||||
// a2 - Address of current input position.
|
||||
|
||||
__ Addu(a1, a1, Operand(a0));
|
||||
|
||||
Label loop;
|
||||
__ bind(&loop);
|
||||
@ -410,12 +379,6 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReference(int start_reg,
|
||||
|
||||
// Move current character position to position after match.
|
||||
__ Subu(current_input_offset(), a2, end_of_input_address());
|
||||
if (read_backward) {
|
||||
__ lw(t0, register_location(start_reg)); // Index of start of capture.
|
||||
__ lw(t5, register_location(start_reg + 1)); // Index of end of capture.
|
||||
__ Addu(current_input_offset(), current_input_offset(), Operand(t0));
|
||||
__ Subu(current_input_offset(), current_input_offset(), Operand(t5));
|
||||
}
|
||||
__ bind(&fallthrough);
|
||||
}
|
||||
|
||||
@ -636,7 +599,7 @@ Handle<HeapObject> RegExpMacroAssemblerMIPS::GetCode(Handle<String> source) {
|
||||
__ Addu(frame_pointer(), sp, Operand(4 * kPointerSize));
|
||||
__ mov(a0, zero_reg);
|
||||
__ push(a0); // Make room for success counter and initialize it to 0.
|
||||
__ push(a0); // Make room for "string start - 1" constant.
|
||||
__ push(a0); // Make room for "position - 1" constant (value irrelevant).
|
||||
|
||||
// Check if we have space on the stack for registers.
|
||||
Label stack_limit_hit;
|
||||
@ -679,7 +642,7 @@ Handle<HeapObject> RegExpMacroAssemblerMIPS::GetCode(Handle<String> source) {
|
||||
__ Subu(a0, a0, t5);
|
||||
// Store this value in a local variable, for use when clearing
|
||||
// position registers.
|
||||
__ sw(a0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ sw(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
|
||||
|
||||
// Initialize code pointer register
|
||||
__ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE);
|
||||
@ -788,7 +751,7 @@ Handle<HeapObject> RegExpMacroAssemblerMIPS::GetCode(Handle<String> source) {
|
||||
__ sw(a2, MemOperand(frame_pointer(), kRegisterOutput));
|
||||
|
||||
// Prepare a0 to initialize registers with its value in the next run.
|
||||
__ lw(a0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ lw(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
|
||||
|
||||
if (global_with_zero_length_check()) {
|
||||
// Special case for zero-length matches.
|
||||
@ -942,13 +905,10 @@ void RegExpMacroAssemblerMIPS::LoadCurrentCharacter(int cp_offset,
|
||||
Label* on_end_of_input,
|
||||
bool check_bounds,
|
||||
int characters) {
|
||||
DCHECK(cp_offset >= -1); // ^ and \b can look behind one character.
|
||||
DCHECK(cp_offset < (1<<30)); // Be sane! (And ensure negation works).
|
||||
if (check_bounds) {
|
||||
if (cp_offset >= 0) {
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
} else {
|
||||
CheckPosition(cp_offset, on_end_of_input);
|
||||
}
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
}
|
||||
LoadCurrentCharacterUnchecked(cp_offset, characters);
|
||||
}
|
||||
@ -1056,7 +1016,7 @@ void RegExpMacroAssemblerMIPS::WriteCurrentPositionToRegister(int reg,
|
||||
|
||||
void RegExpMacroAssemblerMIPS::ClearRegisters(int reg_from, int reg_to) {
|
||||
DCHECK(reg_from <= reg_to);
|
||||
__ lw(a0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ lw(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
|
||||
for (int reg = reg_from; reg <= reg_to; reg++) {
|
||||
__ sw(a0, register_location(reg));
|
||||
}
|
||||
@ -1169,14 +1129,10 @@ MemOperand RegExpMacroAssemblerMIPS::register_location(int register_index) {
|
||||
|
||||
void RegExpMacroAssemblerMIPS::CheckPosition(int cp_offset,
|
||||
Label* on_outside_input) {
|
||||
if (cp_offset >= 0) {
|
||||
BranchOrBacktrack(on_outside_input, ge, current_input_offset(),
|
||||
Operand(-cp_offset * char_size()));
|
||||
} else {
|
||||
__ lw(a1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ Addu(a0, current_input_offset(), Operand(cp_offset * char_size()));
|
||||
BranchOrBacktrack(on_outside_input, le, a0, Operand(a1));
|
||||
}
|
||||
BranchOrBacktrack(on_outside_input,
|
||||
ge,
|
||||
current_input_offset(),
|
||||
Operand(-cp_offset * char_size()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,11 +33,9 @@ class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler {
|
||||
// A "greedy loop" is a loop that is both greedy and with a simple
|
||||
// body. It has a particularly simple implementation.
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotAtStart(Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
|
||||
virtual void CheckNotCharacterAfterAnd(uint32_t c,
|
||||
@ -122,9 +120,9 @@ class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler {
|
||||
// When adding local variables remember to push space for them in
|
||||
// the frame in GetCode.
|
||||
static const int kSuccessfulCaptures = kInputString - kPointerSize;
|
||||
static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
// First register address. Following registers are below it on the stack.
|
||||
static const int kRegisterZero = kStringStartMinusOne - kPointerSize;
|
||||
static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
|
||||
|
||||
// Initial size of code buffer.
|
||||
static const size_t kRegExpCodeSize = 1024;
|
||||
|
@ -61,7 +61,7 @@ namespace internal {
|
||||
* - fp[-16] void* input_string (location of a handle containing the string).
|
||||
* - fp[-20] success counter (only for global regexps to count matches).
|
||||
* - fp[-24] Offset of location before start of input (effectively character
|
||||
* string start - 1). Used to initialize capture registers to a
|
||||
* position -1). Used to initialize capture registers to a
|
||||
* non-position.
|
||||
* - fp[-28] At start (if 1, we are starting at the start of the
|
||||
* string, otherwise 0)
|
||||
@ -91,7 +91,7 @@ namespace internal {
|
||||
* - fp[-56] start index (character index of start). kStartIndex
|
||||
* - fp[-64] void* input_string (location of a handle containing the string). kInputString
|
||||
* - fp[-72] success counter (only for global regexps to count matches). kSuccessfulCaptures
|
||||
* - fp[-80] Offset of location before start of input (effectively character kStringStartMinusOne
|
||||
* - fp[-80] Offset of location before start of input (effectively character kInputStartMinusOne
|
||||
* position -1). Used to initialize capture registers to a
|
||||
* non-position.
|
||||
* --------- The following output registers are 32-bit values. ---------
|
||||
@ -217,17 +217,26 @@ void RegExpMacroAssemblerMIPS::CheckCharacterGT(uc16 limit, Label* on_greater) {
|
||||
|
||||
|
||||
void RegExpMacroAssemblerMIPS::CheckAtStart(Label* on_at_start) {
|
||||
__ ld(a1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ Daddu(a0, current_input_offset(), Operand(-char_size()));
|
||||
Label not_at_start;
|
||||
// Did we start the match at the start of the string at all?
|
||||
__ ld(a0, MemOperand(frame_pointer(), kStartIndex));
|
||||
BranchOrBacktrack(¬_at_start, ne, a0, Operand(zero_reg));
|
||||
|
||||
// If we did, are we still at the start of the input?
|
||||
__ ld(a1, MemOperand(frame_pointer(), kInputStart));
|
||||
__ Daddu(a0, end_of_input_address(), Operand(current_input_offset()));
|
||||
BranchOrBacktrack(on_at_start, eq, a0, Operand(a1));
|
||||
__ bind(¬_at_start);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerMIPS::CheckNotAtStart(int cp_offset,
|
||||
Label* on_not_at_start) {
|
||||
__ ld(a1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ Daddu(a0, current_input_offset(),
|
||||
Operand(-char_size() + cp_offset * char_size()));
|
||||
void RegExpMacroAssemblerMIPS::CheckNotAtStart(Label* on_not_at_start) {
|
||||
// Did we start the match at the start of the string at all?
|
||||
__ ld(a0, MemOperand(frame_pointer(), kStartIndex));
|
||||
BranchOrBacktrack(on_not_at_start, ne, a0, Operand(zero_reg));
|
||||
// If we did, are we still at the start of the input?
|
||||
__ ld(a1, MemOperand(frame_pointer(), kInputStart));
|
||||
__ Daddu(a0, end_of_input_address(), Operand(current_input_offset()));
|
||||
BranchOrBacktrack(on_not_at_start, ne, a0, Operand(a1));
|
||||
}
|
||||
|
||||
@ -250,26 +259,20 @@ void RegExpMacroAssemblerMIPS::CheckGreedyLoop(Label* on_equal) {
|
||||
|
||||
|
||||
void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, Label* on_no_match) {
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
__ ld(a0, register_location(start_reg)); // Index of start of capture.
|
||||
__ ld(a1, register_location(start_reg + 1)); // Index of end of capture.
|
||||
__ Dsubu(a1, a1, a0); // Length of capture.
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
// If length is zero, either the capture is empty or it is not participating.
|
||||
// In either case succeed immediately.
|
||||
__ Branch(&fallthrough, eq, a1, Operand(zero_reg));
|
||||
|
||||
if (read_backward) {
|
||||
__ ld(t1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ Daddu(t1, t1, a1);
|
||||
BranchOrBacktrack(on_no_match, le, current_input_offset(), Operand(t1));
|
||||
} else {
|
||||
__ Daddu(t1, a1, current_input_offset());
|
||||
// Check that there are enough characters left in the input.
|
||||
BranchOrBacktrack(on_no_match, gt, t1, Operand(zero_reg));
|
||||
}
|
||||
__ Daddu(t1, a1, current_input_offset());
|
||||
// Check that there are enough characters left in the input.
|
||||
BranchOrBacktrack(on_no_match, gt, t1, Operand(zero_reg));
|
||||
|
||||
if (mode_ == LATIN1) {
|
||||
Label success;
|
||||
@ -280,9 +283,6 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
|
||||
// a1 - length of capture.
|
||||
__ Daddu(a0, a0, Operand(end_of_input_address()));
|
||||
__ Daddu(a2, end_of_input_address(), Operand(current_input_offset()));
|
||||
if (read_backward) {
|
||||
__ Dsubu(a2, a2, Operand(a1));
|
||||
}
|
||||
__ Daddu(a1, a0, Operand(a1));
|
||||
|
||||
// a0 - Address of start of capture.
|
||||
@ -321,12 +321,6 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
|
||||
__ bind(&success);
|
||||
// Compute new value of character position after the matched part.
|
||||
__ Dsubu(current_input_offset(), a2, end_of_input_address());
|
||||
if (read_backward) {
|
||||
__ ld(t1, register_location(start_reg)); // Index of start of capture.
|
||||
__ ld(a2, register_location(start_reg + 1)); // Index of end of capture.
|
||||
__ Daddu(current_input_offset(), current_input_offset(), Operand(t1));
|
||||
__ Dsubu(current_input_offset(), current_input_offset(), Operand(a2));
|
||||
}
|
||||
} else {
|
||||
DCHECK(mode_ == UC16);
|
||||
// Put regexp engine registers on stack.
|
||||
@ -355,9 +349,6 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
|
||||
__ mov(s3, a1);
|
||||
// Address of current input position.
|
||||
__ Daddu(a1, current_input_offset(), Operand(end_of_input_address()));
|
||||
if (read_backward) {
|
||||
__ Dsubu(a1, a1, Operand(s3));
|
||||
}
|
||||
// Isolate.
|
||||
__ li(a3, Operand(ExternalReference::isolate_address(masm_->isolate())));
|
||||
|
||||
@ -376,20 +367,16 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
|
||||
// Check if function returned non-zero for success or zero for failure.
|
||||
BranchOrBacktrack(on_no_match, eq, v0, Operand(zero_reg));
|
||||
// On success, increment position by length of capture.
|
||||
if (read_backward) {
|
||||
__ Dsubu(current_input_offset(), current_input_offset(), Operand(s3));
|
||||
} else {
|
||||
__ Daddu(current_input_offset(), current_input_offset(), Operand(s3));
|
||||
}
|
||||
__ Daddu(current_input_offset(), current_input_offset(), Operand(s3));
|
||||
}
|
||||
|
||||
__ bind(&fallthrough);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerMIPS::CheckNotBackReference(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match) {
|
||||
void RegExpMacroAssemblerMIPS::CheckNotBackReference(
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
Label success;
|
||||
|
||||
@ -397,28 +384,16 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReference(int start_reg,
|
||||
__ ld(a0, register_location(start_reg));
|
||||
__ ld(a1, register_location(start_reg + 1));
|
||||
__ Dsubu(a1, a1, a0); // Length to check.
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
// Succeed on empty capture (including no capture).
|
||||
__ Branch(&fallthrough, eq, a1, Operand(zero_reg));
|
||||
|
||||
if (read_backward) {
|
||||
__ ld(t1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ Daddu(t1, t1, a1);
|
||||
BranchOrBacktrack(on_no_match, le, current_input_offset(), Operand(t1));
|
||||
} else {
|
||||
__ Daddu(t1, a1, current_input_offset());
|
||||
// Check that there are enough characters left in the input.
|
||||
BranchOrBacktrack(on_no_match, gt, t1, Operand(zero_reg));
|
||||
}
|
||||
__ Daddu(t1, a1, current_input_offset());
|
||||
// Check that there are enough characters left in the input.
|
||||
BranchOrBacktrack(on_no_match, gt, t1, Operand(zero_reg));
|
||||
|
||||
// Compute pointers to match string and capture string.
|
||||
__ Daddu(a0, a0, Operand(end_of_input_address()));
|
||||
__ Daddu(a2, end_of_input_address(), Operand(current_input_offset()));
|
||||
if (read_backward) {
|
||||
__ Dsubu(a2, a2, Operand(a1));
|
||||
}
|
||||
__ Daddu(a1, a1, Operand(a0));
|
||||
|
||||
Label loop;
|
||||
@ -440,12 +415,6 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReference(int start_reg,
|
||||
|
||||
// Move current character position to position after match.
|
||||
__ Dsubu(current_input_offset(), a2, end_of_input_address());
|
||||
if (read_backward) {
|
||||
__ ld(t1, register_location(start_reg)); // Index of start of capture.
|
||||
__ ld(a2, register_location(start_reg + 1)); // Index of end of capture.
|
||||
__ Daddu(current_input_offset(), current_input_offset(), Operand(t1));
|
||||
__ Dsubu(current_input_offset(), current_input_offset(), Operand(a2));
|
||||
}
|
||||
__ bind(&fallthrough);
|
||||
}
|
||||
|
||||
@ -675,7 +644,7 @@ Handle<HeapObject> RegExpMacroAssemblerMIPS::GetCode(Handle<String> source) {
|
||||
__ Daddu(frame_pointer(), sp, Operand(8 * kPointerSize));
|
||||
__ mov(a0, zero_reg);
|
||||
__ push(a0); // Make room for success counter and initialize it to 0.
|
||||
__ push(a0); // Make room for "string start - 1" constant.
|
||||
__ push(a0); // Make room for "position - 1" constant (value irrelevant).
|
||||
|
||||
// Check if we have space on the stack for registers.
|
||||
Label stack_limit_hit;
|
||||
@ -718,7 +687,7 @@ Handle<HeapObject> RegExpMacroAssemblerMIPS::GetCode(Handle<String> source) {
|
||||
__ Dsubu(a0, a0, t1);
|
||||
// Store this value in a local variable, for use when clearing
|
||||
// position registers.
|
||||
__ sd(a0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ sd(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
|
||||
|
||||
// Initialize code pointer register
|
||||
__ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE);
|
||||
@ -828,7 +797,7 @@ Handle<HeapObject> RegExpMacroAssemblerMIPS::GetCode(Handle<String> source) {
|
||||
__ sd(a2, MemOperand(frame_pointer(), kRegisterOutput));
|
||||
|
||||
// Prepare a0 to initialize registers with its value in the next run.
|
||||
__ ld(a0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ ld(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
|
||||
|
||||
if (global_with_zero_length_check()) {
|
||||
// Special case for zero-length matches.
|
||||
@ -982,13 +951,10 @@ void RegExpMacroAssemblerMIPS::LoadCurrentCharacter(int cp_offset,
|
||||
Label* on_end_of_input,
|
||||
bool check_bounds,
|
||||
int characters) {
|
||||
DCHECK(cp_offset >= -1); // ^ and \b can look behind one character.
|
||||
DCHECK(cp_offset < (1<<30)); // Be sane! (And ensure negation works).
|
||||
if (check_bounds) {
|
||||
if (cp_offset >= 0) {
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
} else {
|
||||
CheckPosition(cp_offset, on_end_of_input);
|
||||
}
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
}
|
||||
LoadCurrentCharacterUnchecked(cp_offset, characters);
|
||||
}
|
||||
@ -1096,7 +1062,7 @@ void RegExpMacroAssemblerMIPS::WriteCurrentPositionToRegister(int reg,
|
||||
|
||||
void RegExpMacroAssemblerMIPS::ClearRegisters(int reg_from, int reg_to) {
|
||||
DCHECK(reg_from <= reg_to);
|
||||
__ ld(a0, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ ld(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
|
||||
for (int reg = reg_from; reg <= reg_to; reg++) {
|
||||
__ sd(a0, register_location(reg));
|
||||
}
|
||||
@ -1209,14 +1175,10 @@ MemOperand RegExpMacroAssemblerMIPS::register_location(int register_index) {
|
||||
|
||||
void RegExpMacroAssemblerMIPS::CheckPosition(int cp_offset,
|
||||
Label* on_outside_input) {
|
||||
if (cp_offset >= 0) {
|
||||
BranchOrBacktrack(on_outside_input, ge, current_input_offset(),
|
||||
Operand(-cp_offset * char_size()));
|
||||
} else {
|
||||
__ ld(a1, MemOperand(frame_pointer(), kStringStartMinusOne));
|
||||
__ Daddu(a0, current_input_offset(), Operand(cp_offset * char_size()));
|
||||
BranchOrBacktrack(on_outside_input, le, a0, Operand(a1));
|
||||
}
|
||||
BranchOrBacktrack(on_outside_input,
|
||||
ge,
|
||||
current_input_offset(),
|
||||
Operand(-cp_offset * char_size()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,11 +33,9 @@ class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler {
|
||||
// A "greedy loop" is a loop that is both greedy and with a simple
|
||||
// body. It has a particularly simple implementation.
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotAtStart(Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
|
||||
virtual void CheckNotCharacterAfterAnd(uint32_t c,
|
||||
@ -127,9 +125,9 @@ class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler {
|
||||
// When adding local variables remember to push space for them in
|
||||
// the frame in GetCode.
|
||||
static const int kSuccessfulCaptures = kInputString - kPointerSize;
|
||||
static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
// First register address. Following registers are below it on the stack.
|
||||
static const int kRegisterZero = kStringStartMinusOne - kPointerSize;
|
||||
static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
|
||||
|
||||
#elif defined(MIPS_ABI_O32)
|
||||
// Offsets from frame_pointer() of function parameters and stored registers.
|
||||
@ -160,9 +158,9 @@ class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler {
|
||||
// When adding local variables remember to push space for them in
|
||||
// the frame in GetCode.
|
||||
static const int kSuccessfulCaptures = kInputString - kPointerSize;
|
||||
static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
// First register address. Following registers are below it on the stack.
|
||||
static const int kRegisterZero = kStringStartMinusOne - kPointerSize;
|
||||
static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
|
||||
|
||||
#else
|
||||
# error "undefined MIPS ABI"
|
||||
|
@ -273,9 +273,8 @@ void RegExpMacroAssemblerIrregexp::CheckAtStart(Label* on_at_start) {
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerIrregexp::CheckNotAtStart(int cp_offset,
|
||||
Label* on_not_at_start) {
|
||||
Emit(BC_CHECK_NOT_AT_START, cp_offset);
|
||||
void RegExpMacroAssemblerIrregexp::CheckNotAtStart(Label* on_not_at_start) {
|
||||
Emit(BC_CHECK_NOT_AT_START, 0);
|
||||
EmitOrLink(on_not_at_start);
|
||||
}
|
||||
|
||||
@ -371,23 +370,20 @@ void RegExpMacroAssemblerIrregexp::CheckBitInTable(
|
||||
|
||||
|
||||
void RegExpMacroAssemblerIrregexp::CheckNotBackReference(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_not_equal) {
|
||||
DCHECK(start_reg >= 0);
|
||||
DCHECK(start_reg <= kMaxRegister);
|
||||
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_BACKWARD : BC_CHECK_NOT_BACK_REF,
|
||||
start_reg);
|
||||
Emit(BC_CHECK_NOT_BACK_REF, start_reg);
|
||||
EmitOrLink(on_not_equal);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerIrregexp::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, Label* on_not_equal) {
|
||||
int start_reg,
|
||||
Label* on_not_equal) {
|
||||
DCHECK(start_reg >= 0);
|
||||
DCHECK(start_reg <= kMaxRegister);
|
||||
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD
|
||||
: BC_CHECK_NOT_BACK_REF_NO_CASE,
|
||||
start_reg);
|
||||
Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg);
|
||||
EmitOrLink(on_not_equal);
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler {
|
||||
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
|
||||
virtual void CheckAtStart(Label* on_at_start);
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
|
||||
virtual void CheckNotAtStart(Label* on_not_at_start);
|
||||
virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
|
||||
virtual void CheckNotCharacterAfterAnd(unsigned c,
|
||||
unsigned mask,
|
||||
@ -82,10 +82,8 @@ class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler {
|
||||
uc16 to,
|
||||
Label* on_not_in_range);
|
||||
virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void IfRegisterLT(int register_index, int comparand, Label* if_lt);
|
||||
virtual void IfRegisterGE(int register_index, int comparand, Label* if_ge);
|
||||
|
@ -13,9 +13,9 @@ RegExpMacroAssemblerTracer::RegExpMacroAssemblerTracer(
|
||||
Isolate* isolate, RegExpMacroAssembler* assembler)
|
||||
: RegExpMacroAssembler(isolate, assembler->zone()), assembler_(assembler) {
|
||||
unsigned int type = assembler->Implementation();
|
||||
DCHECK(type < 8);
|
||||
const char* impl_names[] = {"IA32", "ARM", "ARM64", "MIPS",
|
||||
"PPC", "X64", "X87", "Bytecode"};
|
||||
DCHECK(type < 6);
|
||||
const char* impl_names[] = {"IA32", "ARM", "ARM64",
|
||||
"MIPS", "X64", "X87", "Bytecode"};
|
||||
PrintF("RegExpMacroAssembler%s();\n", impl_names[type]);
|
||||
}
|
||||
|
||||
@ -241,11 +241,9 @@ void RegExpMacroAssemblerTracer::CheckAtStart(Label* on_at_start) {
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerTracer::CheckNotAtStart(int cp_offset,
|
||||
Label* on_not_at_start) {
|
||||
PrintF(" CheckNotAtStart(cp_offset=%d, label[%08x]);\n", cp_offset,
|
||||
LabelToInt(on_not_at_start));
|
||||
assembler_->CheckNotAtStart(cp_offset, on_not_at_start);
|
||||
void RegExpMacroAssemblerTracer::CheckNotAtStart(Label* on_not_at_start) {
|
||||
PrintF(" CheckNotAtStart(label[%08x]);\n", LabelToInt(on_not_at_start));
|
||||
assembler_->CheckNotAtStart(on_not_at_start);
|
||||
}
|
||||
|
||||
|
||||
@ -351,21 +349,19 @@ void RegExpMacroAssemblerTracer::CheckBitInTable(
|
||||
|
||||
|
||||
void RegExpMacroAssemblerTracer::CheckNotBackReference(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match) {
|
||||
PrintF(" CheckNotBackReference(register=%d, %s, label[%08x]);\n", start_reg,
|
||||
read_backward ? "backward" : "forward", LabelToInt(on_no_match));
|
||||
assembler_->CheckNotBackReference(start_reg, read_backward, on_no_match);
|
||||
PrintF(" CheckNotBackReference(register=%d, label[%08x]);\n", start_reg,
|
||||
LabelToInt(on_no_match));
|
||||
assembler_->CheckNotBackReference(start_reg, on_no_match);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerTracer::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, Label* on_no_match) {
|
||||
PrintF(" CheckNotBackReferenceIgnoreCase(register=%d, %s, label[%08x]);\n",
|
||||
start_reg, read_backward ? "backward" : "forward",
|
||||
LabelToInt(on_no_match));
|
||||
assembler_->CheckNotBackReferenceIgnoreCase(start_reg, read_backward,
|
||||
on_no_match);
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
PrintF(" CheckNotBackReferenceIgnoreCase(register=%d, label[%08x]);\n",
|
||||
start_reg, LabelToInt(on_no_match));
|
||||
assembler_->CheckNotBackReferenceIgnoreCase(start_reg, on_no_match);
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,11 +30,9 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler {
|
||||
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
|
||||
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotAtStart(Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
|
||||
virtual void CheckNotCharacterAfterAnd(unsigned c,
|
||||
|
@ -71,11 +71,9 @@ class RegExpMacroAssembler {
|
||||
virtual void CheckCharacterGT(uc16 limit, Label* on_greater) = 0;
|
||||
virtual void CheckCharacterLT(uc16 limit, Label* on_less) = 0;
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position) = 0;
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start) = 0;
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match) = 0;
|
||||
virtual void CheckNotAtStart(Label* on_not_at_start) = 0;
|
||||
virtual void CheckNotBackReference(int start_reg, Label* on_no_match) = 0;
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match) = 0;
|
||||
// Check the current character for a match with a literal character. If we
|
||||
// fail to match then goto the on_failure label. End of input always
|
||||
|
@ -64,8 +64,7 @@ namespace internal {
|
||||
* - backup of callee save registers (rbx, possibly rsi and rdi).
|
||||
* - success counter (only useful for global regexp to count matches)
|
||||
* - Offset of location before start of input (effectively character
|
||||
* string start - 1). Used to initialize capture registers to a
|
||||
* non-position.
|
||||
* position -1). Used to initialize capture registers to a non-position.
|
||||
* - At start of string (if 1, we are starting at the start of the
|
||||
* string, otherwise 0)
|
||||
* - register 0 rbp[-n] (Only positions must be stored in the first
|
||||
@ -172,16 +171,25 @@ void RegExpMacroAssemblerX64::CheckCharacterGT(uc16 limit, Label* on_greater) {
|
||||
|
||||
|
||||
void RegExpMacroAssemblerX64::CheckAtStart(Label* on_at_start) {
|
||||
__ leap(rax, Operand(rdi, -char_size()));
|
||||
__ cmpp(rax, Operand(rbp, kStringStartMinusOne));
|
||||
Label not_at_start;
|
||||
// Did we start the match at the start of the string at all?
|
||||
__ cmpl(Operand(rbp, kStartIndex), Immediate(0));
|
||||
BranchOrBacktrack(not_equal, ¬_at_start);
|
||||
// If we did, are we still at the start of the input?
|
||||
__ leap(rax, Operand(rsi, rdi, times_1, 0));
|
||||
__ cmpp(rax, Operand(rbp, kInputStart));
|
||||
BranchOrBacktrack(equal, on_at_start);
|
||||
__ bind(¬_at_start);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerX64::CheckNotAtStart(int cp_offset,
|
||||
Label* on_not_at_start) {
|
||||
__ leap(rax, Operand(rdi, -char_size() + cp_offset * char_size()));
|
||||
__ cmpp(rax, Operand(rbp, kStringStartMinusOne));
|
||||
void RegExpMacroAssemblerX64::CheckNotAtStart(Label* on_not_at_start) {
|
||||
// Did we start the match at the start of the string at all?
|
||||
__ cmpl(Operand(rbp, kStartIndex), Immediate(0));
|
||||
BranchOrBacktrack(not_equal, on_not_at_start);
|
||||
// If we did, are we still at the start of the input?
|
||||
__ leap(rax, Operand(rsi, rdi, times_1, 0));
|
||||
__ cmpp(rax, Operand(rbp, kInputStart));
|
||||
BranchOrBacktrack(not_equal, on_not_at_start);
|
||||
}
|
||||
|
||||
@ -203,7 +211,8 @@ void RegExpMacroAssemblerX64::CheckGreedyLoop(Label* on_equal) {
|
||||
|
||||
|
||||
void RegExpMacroAssemblerX64::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, Label* on_no_match) {
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
ReadPositionFromRegister(rdx, start_reg); // Offset of start of capture
|
||||
ReadPositionFromRegister(rbx, start_reg + 1); // Offset of end of capture
|
||||
@ -213,25 +222,23 @@ void RegExpMacroAssemblerX64::CheckNotBackReferenceIgnoreCase(
|
||||
// rdx = Start offset of capture.
|
||||
// rbx = Length of capture
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
// If length is negative, this code will fail (it's a symptom of a partial or
|
||||
// illegal capture where start of capture after end of capture).
|
||||
// This must not happen (no back-reference can reference a capture that wasn't
|
||||
// closed before in the reg-exp, and we must not generate code that can cause
|
||||
// this condition).
|
||||
|
||||
// If length is zero, either the capture is empty or it is nonparticipating.
|
||||
// In either case succeed immediately.
|
||||
__ j(equal, &fallthrough);
|
||||
|
||||
// -----------------------
|
||||
// rdx - Start of capture
|
||||
// rbx - length of capture
|
||||
// Check that there are sufficient characters left in the input.
|
||||
if (read_backward) {
|
||||
__ movl(rax, Operand(rbp, kStringStartMinusOne));
|
||||
__ addl(rax, rbx);
|
||||
__ cmpl(rdi, rax);
|
||||
BranchOrBacktrack(less_equal, on_no_match);
|
||||
} else {
|
||||
__ movl(rax, rdi);
|
||||
__ addl(rax, rbx);
|
||||
BranchOrBacktrack(greater, on_no_match);
|
||||
}
|
||||
__ movl(rax, rdi);
|
||||
__ addl(rax, rbx);
|
||||
BranchOrBacktrack(greater, on_no_match);
|
||||
|
||||
if (mode_ == LATIN1) {
|
||||
Label loop_increment;
|
||||
@ -241,9 +248,6 @@ void RegExpMacroAssemblerX64::CheckNotBackReferenceIgnoreCase(
|
||||
|
||||
__ leap(r9, Operand(rsi, rdx, times_1, 0));
|
||||
__ leap(r11, Operand(rsi, rdi, times_1, 0));
|
||||
if (read_backward) {
|
||||
__ subp(r11, rbx); // Offset by length when matching backwards.
|
||||
}
|
||||
__ addp(rbx, r9); // End of capture
|
||||
// ---------------------
|
||||
// r11 - current input character address
|
||||
@ -286,11 +290,6 @@ void RegExpMacroAssemblerX64::CheckNotBackReferenceIgnoreCase(
|
||||
// Compute new value of character position after the matched part.
|
||||
__ movp(rdi, r11);
|
||||
__ subq(rdi, rsi);
|
||||
if (read_backward) {
|
||||
// Subtract match length if we matched backward.
|
||||
__ addq(rdi, register_location(start_reg));
|
||||
__ subq(rdi, register_location(start_reg + 1));
|
||||
}
|
||||
} else {
|
||||
DCHECK(mode_ == UC16);
|
||||
// Save important/volatile registers before calling C function.
|
||||
@ -314,9 +313,6 @@ void RegExpMacroAssemblerX64::CheckNotBackReferenceIgnoreCase(
|
||||
__ leap(rcx, Operand(rsi, rdx, times_1, 0));
|
||||
// Set byte_offset2.
|
||||
__ leap(rdx, Operand(rsi, rdi, times_1, 0));
|
||||
if (read_backward) {
|
||||
__ subq(rdx, rbx);
|
||||
}
|
||||
// Set byte_length.
|
||||
__ movp(r8, rbx);
|
||||
// Isolate.
|
||||
@ -328,9 +324,6 @@ void RegExpMacroAssemblerX64::CheckNotBackReferenceIgnoreCase(
|
||||
__ leap(rdi, Operand(rsi, rdx, times_1, 0));
|
||||
// Set byte_offset2.
|
||||
__ movp(rsi, rax);
|
||||
if (read_backward) {
|
||||
__ subq(rsi, rbx);
|
||||
}
|
||||
// Set byte_length.
|
||||
__ movp(rdx, rbx);
|
||||
// Isolate.
|
||||
@ -356,21 +349,17 @@ void RegExpMacroAssemblerX64::CheckNotBackReferenceIgnoreCase(
|
||||
// Check if function returned non-zero for success or zero for failure.
|
||||
__ testp(rax, rax);
|
||||
BranchOrBacktrack(zero, on_no_match);
|
||||
// On success, advance position by length of capture.
|
||||
// On success, increment position by length of capture.
|
||||
// Requires that rbx is callee save (true for both Win64 and AMD64 ABIs).
|
||||
if (read_backward) {
|
||||
__ subq(rdi, rbx);
|
||||
} else {
|
||||
__ addq(rdi, rbx);
|
||||
}
|
||||
__ addq(rdi, rbx);
|
||||
}
|
||||
__ bind(&fallthrough);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerX64::CheckNotBackReference(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match) {
|
||||
void RegExpMacroAssemblerX64::CheckNotBackReference(
|
||||
int start_reg,
|
||||
Label* on_no_match) {
|
||||
Label fallthrough;
|
||||
|
||||
// Find length of back-referenced capture.
|
||||
@ -378,31 +367,25 @@ void RegExpMacroAssemblerX64::CheckNotBackReference(int start_reg,
|
||||
ReadPositionFromRegister(rax, start_reg + 1); // Offset of end of capture
|
||||
__ subp(rax, rdx); // Length to check.
|
||||
|
||||
// At this point, the capture registers are either both set or both cleared.
|
||||
// If the capture length is zero, then the capture is either empty or cleared.
|
||||
// Fall through in both cases.
|
||||
// Fail on partial or illegal capture (start of capture after end of capture).
|
||||
// This must not happen (no back-reference can reference a capture that wasn't
|
||||
// closed before in the reg-exp).
|
||||
__ Check(greater_equal, kInvalidCaptureReferenced);
|
||||
|
||||
// Succeed on empty capture (including non-participating capture)
|
||||
__ j(equal, &fallthrough);
|
||||
|
||||
// -----------------------
|
||||
// rdx - Start of capture
|
||||
// rax - length of capture
|
||||
|
||||
// Check that there are sufficient characters left in the input.
|
||||
if (read_backward) {
|
||||
__ movl(rbx, Operand(rbp, kStringStartMinusOne));
|
||||
__ addl(rbx, rax);
|
||||
__ cmpl(rdi, rbx);
|
||||
BranchOrBacktrack(less_equal, on_no_match);
|
||||
} else {
|
||||
__ movl(rbx, rdi);
|
||||
__ addl(rbx, rax);
|
||||
BranchOrBacktrack(greater, on_no_match);
|
||||
}
|
||||
__ movl(rbx, rdi);
|
||||
__ addl(rbx, rax);
|
||||
BranchOrBacktrack(greater, on_no_match);
|
||||
|
||||
// Compute pointers to match string and capture string
|
||||
__ leap(rbx, Operand(rsi, rdi, times_1, 0)); // Start of match.
|
||||
if (read_backward) {
|
||||
__ subq(rbx, rax); // Offset by length when matching backwards.
|
||||
}
|
||||
__ addp(rdx, rsi); // Start of capture.
|
||||
__ leap(r9, Operand(rdx, rax, times_1, 0)); // End of capture
|
||||
|
||||
@ -433,11 +416,6 @@ void RegExpMacroAssemblerX64::CheckNotBackReference(int start_reg,
|
||||
// Set current character position to position after match.
|
||||
__ movp(rdi, rbx);
|
||||
__ subq(rdi, rsi);
|
||||
if (read_backward) {
|
||||
// Subtract match length if we matched backward.
|
||||
__ addq(rdi, register_location(start_reg));
|
||||
__ subq(rdi, register_location(start_reg + 1));
|
||||
}
|
||||
|
||||
__ bind(&fallthrough);
|
||||
}
|
||||
@ -704,7 +682,7 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
|
||||
#endif
|
||||
|
||||
__ Push(Immediate(0)); // Number of successful matches in a global regexp.
|
||||
__ Push(Immediate(0)); // Make room for "string start - 1" constant.
|
||||
__ Push(Immediate(0)); // Make room for "input start - 1" constant.
|
||||
|
||||
// Check if we have space on the stack for registers.
|
||||
Label stack_limit_hit;
|
||||
@ -754,7 +732,7 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
|
||||
}
|
||||
// Store this value in a local variable, for use when clearing
|
||||
// position registers.
|
||||
__ movp(Operand(rbp, kStringStartMinusOne), rax);
|
||||
__ movp(Operand(rbp, kInputStartMinusOne), rax);
|
||||
|
||||
#if V8_OS_WIN
|
||||
// Ensure that we have written to each stack page, in order. Skipping a page
|
||||
@ -857,7 +835,7 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
|
||||
Immediate(num_saved_registers_ * kIntSize));
|
||||
|
||||
// Prepare rax to initialize registers with its value in the next run.
|
||||
__ movp(rax, Operand(rbp, kStringStartMinusOne));
|
||||
__ movp(rax, Operand(rbp, kInputStartMinusOne));
|
||||
|
||||
if (global_with_zero_length_check()) {
|
||||
// Special case for zero-length matches.
|
||||
@ -1040,13 +1018,10 @@ void RegExpMacroAssemblerX64::LoadCurrentCharacter(int cp_offset,
|
||||
Label* on_end_of_input,
|
||||
bool check_bounds,
|
||||
int characters) {
|
||||
DCHECK(cp_offset >= -1); // ^ and \b can look behind one character.
|
||||
DCHECK(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
|
||||
if (check_bounds) {
|
||||
if (cp_offset >= 0) {
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
} else {
|
||||
CheckPosition(cp_offset, on_end_of_input);
|
||||
}
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
}
|
||||
LoadCurrentCharacterUnchecked(cp_offset, characters);
|
||||
}
|
||||
@ -1149,7 +1124,7 @@ void RegExpMacroAssemblerX64::WriteCurrentPositionToRegister(int reg,
|
||||
|
||||
void RegExpMacroAssemblerX64::ClearRegisters(int reg_from, int reg_to) {
|
||||
DCHECK(reg_from <= reg_to);
|
||||
__ movp(rax, Operand(rbp, kStringStartMinusOne));
|
||||
__ movp(rax, Operand(rbp, kInputStartMinusOne));
|
||||
for (int reg = reg_from; reg <= reg_to; reg++) {
|
||||
__ movp(register_location(reg), rax);
|
||||
}
|
||||
@ -1230,14 +1205,8 @@ Operand RegExpMacroAssemblerX64::register_location(int register_index) {
|
||||
|
||||
void RegExpMacroAssemblerX64::CheckPosition(int cp_offset,
|
||||
Label* on_outside_input) {
|
||||
if (cp_offset >= 0) {
|
||||
__ cmpl(rdi, Immediate(-cp_offset * char_size()));
|
||||
BranchOrBacktrack(greater_equal, on_outside_input);
|
||||
} else {
|
||||
__ leap(rax, Operand(rdi, cp_offset * char_size()));
|
||||
__ cmpp(rax, Operand(rbp, kStringStartMinusOne));
|
||||
BranchOrBacktrack(less_equal, on_outside_input);
|
||||
}
|
||||
__ cmpl(rdi, Immediate(-cp_offset * char_size()));
|
||||
BranchOrBacktrack(greater_equal, on_outside_input);
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,11 +34,9 @@ class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler {
|
||||
// A "greedy loop" is a loop that is both greedy and with a simple
|
||||
// body. It has a particularly simple implementation.
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotAtStart(Label* on_not_at_start);
|
||||
virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
|
||||
virtual void CheckNotCharacterAfterAnd(uint32_t c,
|
||||
@ -173,10 +171,10 @@ class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler {
|
||||
static const int kSuccessfulCaptures = kLastCalleeSaveRegister - kPointerSize;
|
||||
// When adding local variables remember to push space for them in
|
||||
// the frame in GetCode.
|
||||
static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
|
||||
|
||||
// First register address. Following registers are below it on the stack.
|
||||
static const int kRegisterZero = kStringStartMinusOne - kPointerSize;
|
||||
static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
|
||||
|
||||
// Initial size of code buffer.
|
||||
static const size_t kRegExpCodeSize = 1024;
|
||||
|
@ -159,10 +159,7 @@ static MinMaxPair CheckMinMaxMatch(const char* input) {
|
||||
CHECK_EQ(max, min_max.max_match); \
|
||||
}
|
||||
|
||||
|
||||
void TestRegExpParser(bool lookbehind) {
|
||||
FLAG_harmony_regexp_lookbehind = lookbehind;
|
||||
|
||||
TEST(Parser) {
|
||||
CHECK_PARSE_ERROR("?");
|
||||
|
||||
CheckParseEq("abc", "'abc'");
|
||||
@ -194,13 +191,6 @@ void TestRegExpParser(bool lookbehind) {
|
||||
CheckParseEq("foo|(bar|baz)|quux", "(| 'foo' (^ (| 'bar' 'baz')) 'quux')");
|
||||
CheckParseEq("foo(?=bar)baz", "(: 'foo' (-> + 'bar') 'baz')");
|
||||
CheckParseEq("foo(?!bar)baz", "(: 'foo' (-> - 'bar') 'baz')");
|
||||
if (lookbehind) {
|
||||
CheckParseEq("foo(?<=bar)baz", "(: 'foo' (<- + 'bar') 'baz')");
|
||||
CheckParseEq("foo(?<!bar)baz", "(: 'foo' (<- - 'bar') 'baz')");
|
||||
} else {
|
||||
CHECK_PARSE_ERROR("foo(?<=bar)baz");
|
||||
CHECK_PARSE_ERROR("foo(?<!bar)baz");
|
||||
}
|
||||
CheckParseEq("()", "(^ %)");
|
||||
CheckParseEq("(?=)", "(-> + %)");
|
||||
CheckParseEq("[]", "^[\\x00-\\uffff]"); // Doesn't compile on windows
|
||||
@ -277,16 +267,9 @@ void TestRegExpParser(bool lookbehind) {
|
||||
CheckParseEq("(?=a){1,10}a", "(: (-> + 'a') 'a')");
|
||||
CheckParseEq("(?=a){9,10}a", "(: (-> + 'a') 'a')");
|
||||
CheckParseEq("(?!a)?a", "'a'");
|
||||
CheckParseEq("\\1(a)", "(: (<- 1) (^ 'a'))");
|
||||
CheckParseEq("\\1(a)", "(^ 'a')");
|
||||
CheckParseEq("(?!(a))\\1", "(: (-> - (^ 'a')) (<- 1))");
|
||||
CheckParseEq("(?!\\1(a\\1)\\1)\\1",
|
||||
"(: (-> - (: (<- 1) (^ 'a') (<- 1))) (<- 1))");
|
||||
CheckParseEq("\\1\\2(a(?:\\1(b\\1\\2))\\2)\\1",
|
||||
"(: (<- 1) (<- 2) (^ (: 'a' (^ 'b') (<- 2))) (<- 1))");
|
||||
if (lookbehind) {
|
||||
CheckParseEq("\\1\\2(a(?<=\\1(b\\1\\2))\\2)\\1",
|
||||
"(: (<- 1) (<- 2) (^ (: 'a' (<- + (^ 'b')) (<- 2))) (<- 1))");
|
||||
}
|
||||
CheckParseEq("(?!\\1(a\\1)\\1)\\1", "(: (-> - (: (^ 'a') (<- 1))) (<- 1))");
|
||||
CheckParseEq("[\\0]", "[\\x00]");
|
||||
CheckParseEq("[\\11]", "[\\x09]");
|
||||
CheckParseEq("[\\11a]", "[\\x09 a]");
|
||||
@ -417,16 +400,6 @@ void TestRegExpParser(bool lookbehind) {
|
||||
}
|
||||
|
||||
|
||||
TEST(ParserWithLookbehind) {
|
||||
TestRegExpParser(true); // Lookbehind enabled.
|
||||
}
|
||||
|
||||
|
||||
TEST(ParserWithoutLookbehind) {
|
||||
TestRegExpParser(true); // Lookbehind enabled.
|
||||
}
|
||||
|
||||
|
||||
TEST(ParserRegression) {
|
||||
CheckParseEq("[A-Z$-][x]", "(! [A-Z $ -] [x])");
|
||||
CheckParseEq("a{3,4*}", "(: 'a{3,' (# 0 - g '4') '}')");
|
||||
@ -817,7 +790,7 @@ TEST(MacroAssemblerNativeSimple) {
|
||||
|
||||
Label fail, backtrack;
|
||||
m.PushBacktrack(&fail);
|
||||
m.CheckNotAtStart(0, NULL);
|
||||
m.CheckNotAtStart(NULL);
|
||||
m.LoadCurrentCharacter(2, NULL);
|
||||
m.CheckNotCharacter('o', NULL);
|
||||
m.LoadCurrentCharacter(1, NULL, false);
|
||||
@ -884,7 +857,7 @@ TEST(MacroAssemblerNativeSimpleUC16) {
|
||||
|
||||
Label fail, backtrack;
|
||||
m.PushBacktrack(&fail);
|
||||
m.CheckNotAtStart(0, NULL);
|
||||
m.CheckNotAtStart(NULL);
|
||||
m.LoadCurrentCharacter(2, NULL);
|
||||
m.CheckNotCharacter('o', NULL);
|
||||
m.LoadCurrentCharacter(1, NULL, false);
|
||||
@ -1000,12 +973,12 @@ TEST(MacroAssemblerNativeBackReferenceLATIN1) {
|
||||
m.AdvanceCurrentPosition(2);
|
||||
m.WriteCurrentPositionToRegister(1, 0);
|
||||
Label nomatch;
|
||||
m.CheckNotBackReference(0, false, &nomatch);
|
||||
m.CheckNotBackReference(0, &nomatch);
|
||||
m.Fail();
|
||||
m.Bind(&nomatch);
|
||||
m.AdvanceCurrentPosition(2);
|
||||
Label missing_match;
|
||||
m.CheckNotBackReference(0, false, &missing_match);
|
||||
m.CheckNotBackReference(0, &missing_match);
|
||||
m.WriteCurrentPositionToRegister(2, 0);
|
||||
m.Succeed();
|
||||
m.Bind(&missing_match);
|
||||
@ -1050,12 +1023,12 @@ TEST(MacroAssemblerNativeBackReferenceUC16) {
|
||||
m.AdvanceCurrentPosition(2);
|
||||
m.WriteCurrentPositionToRegister(1, 0);
|
||||
Label nomatch;
|
||||
m.CheckNotBackReference(0, false, &nomatch);
|
||||
m.CheckNotBackReference(0, &nomatch);
|
||||
m.Fail();
|
||||
m.Bind(&nomatch);
|
||||
m.AdvanceCurrentPosition(2);
|
||||
Label missing_match;
|
||||
m.CheckNotBackReference(0, false, &missing_match);
|
||||
m.CheckNotBackReference(0, &missing_match);
|
||||
m.WriteCurrentPositionToRegister(2, 0);
|
||||
m.Succeed();
|
||||
m.Bind(&missing_match);
|
||||
@ -1100,7 +1073,7 @@ TEST(MacroAssemblernativeAtStart) {
|
||||
0);
|
||||
|
||||
Label not_at_start, newline, fail;
|
||||
m.CheckNotAtStart(0, ¬_at_start);
|
||||
m.CheckNotAtStart(¬_at_start);
|
||||
// Check that prevchar = '\n' and current = 'f'.
|
||||
m.CheckCharacter('\n', &newline);
|
||||
m.Bind(&fail);
|
||||
@ -1165,16 +1138,16 @@ TEST(MacroAssemblerNativeBackRefNoCase) {
|
||||
m.WriteCurrentPositionToRegister(2, 0);
|
||||
m.AdvanceCurrentPosition(3);
|
||||
m.WriteCurrentPositionToRegister(3, 0);
|
||||
m.CheckNotBackReferenceIgnoreCase(2, false, &fail); // Match "AbC".
|
||||
m.CheckNotBackReferenceIgnoreCase(2, false, &fail); // Match "ABC".
|
||||
m.CheckNotBackReferenceIgnoreCase(2, &fail); // Match "AbC".
|
||||
m.CheckNotBackReferenceIgnoreCase(2, &fail); // Match "ABC".
|
||||
Label expected_fail;
|
||||
m.CheckNotBackReferenceIgnoreCase(2, false, &expected_fail);
|
||||
m.CheckNotBackReferenceIgnoreCase(2, &expected_fail);
|
||||
m.Bind(&fail);
|
||||
m.Fail();
|
||||
|
||||
m.Bind(&expected_fail);
|
||||
m.AdvanceCurrentPosition(3); // Skip "xYz"
|
||||
m.CheckNotBackReferenceIgnoreCase(2, false, &succ);
|
||||
m.CheckNotBackReferenceIgnoreCase(2, &succ);
|
||||
m.Fail();
|
||||
|
||||
m.Bind(&succ);
|
||||
@ -1366,7 +1339,7 @@ TEST(MacroAssemblerNativeLotsOfRegisters) {
|
||||
m.WriteCurrentPositionToRegister(0, 0);
|
||||
m.WriteCurrentPositionToRegister(1, 1);
|
||||
Label done;
|
||||
m.CheckNotBackReference(0, false, &done); // Performs a system-stack push.
|
||||
m.CheckNotBackReference(0, &done); // Performs a system-stack push.
|
||||
m.Bind(&done);
|
||||
m.PushRegister(large_number, RegExpMacroAssembler::kNoStackLimitCheck);
|
||||
m.PopRegister(1);
|
||||
@ -1415,7 +1388,7 @@ TEST(MacroAssembler) {
|
||||
m.Fail();
|
||||
m.Bind(&start);
|
||||
m.PushBacktrack(&fail);
|
||||
m.CheckNotAtStart(0, NULL);
|
||||
m.CheckNotAtStart(NULL);
|
||||
m.LoadCurrentCharacter(0, NULL);
|
||||
m.CheckNotCharacter('f', NULL);
|
||||
m.LoadCurrentCharacter(1, NULL);
|
||||
|
@ -1,159 +0,0 @@
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-regexp-lookbehind
|
||||
|
||||
// Simple fixed-length matches.
|
||||
assertEquals(["a"], "a".match(/^.(?<=a)/));
|
||||
assertNull("b".match(/^.(?<=a)/));
|
||||
assertEquals(["foo"], "foo1".match(/^f..(?<=.oo)/));
|
||||
assertEquals(["foo"], "foo2".match(/^f\w\w(?<=\woo)/));
|
||||
assertNull("boo".match(/^f\w\w(?<=\woo)/));
|
||||
assertNull("fao".match(/^f\w\w(?<=\woo)/));
|
||||
assertNull("foa".match(/^f\w\w(?<=\woo)/));
|
||||
assertEquals(["def"], "abcdef".match(/(?<=abc)\w\w\w/));
|
||||
assertEquals(["def"], "abcdef".match(/(?<=a.c)\w\w\w/));
|
||||
assertEquals(["def"], "abcdef".match(/(?<=a\wc)\w\w\w/));
|
||||
assertEquals(["cde"], "abcdef".match(/(?<=a[a-z])\w\w\w/));
|
||||
assertEquals(["def"], "abcdef".match(/(?<=a[a-z][a-z])\w\w\w/));
|
||||
assertEquals(["def"], "abcdef".match(/(?<=a[a-z]{2})\w\w\w/));
|
||||
assertEquals(["bcd"], "abcdef".match(/(?<=a{1})\w\w\w/));
|
||||
assertEquals(["cde"], "abcdef".match(/(?<=a{1}b{1})\w\w\w/));
|
||||
assertEquals(["def"], "abcdef".match(/(?<=a{1}[a-z]{2})\w\w\w/));
|
||||
|
||||
// Variable-length matches.
|
||||
assertEquals(["def"], "abcdef".match(/(?<=[a|b|c]*)[^a|b|c]{3}/));
|
||||
assertEquals(["def"], "abcdef".match(/(?<=\w*)[^a|b|c]{3}/));
|
||||
|
||||
// Start of line matches.
|
||||
assertEquals(["def"], "abcdef".match(/(?<=^abc)def/));
|
||||
assertEquals(["def"], "abcdef".match(/(?<=^[a-c]{3})def/));
|
||||
assertEquals(["def"], "xyz\nabcdef".match(/(?<=^[a-c]{3})def/m));
|
||||
assertEquals(["ab", "cd", "efg"], "ab\ncd\nefg".match(/(?<=^)\w+/gm));
|
||||
assertEquals(["ab", "cd", "efg"], "ab\ncd\nefg".match(/\w+(?<=$)/gm));
|
||||
assertEquals(["ab", "cd", "efg"], "ab\ncd\nefg".match(/(?<=^)\w+(?<=$)/gm));
|
||||
assertNull("abcdef".match(/(?<=^[^a-c]{3})def/));
|
||||
assertNull("foooo".match(/"^foooo(?<=^o+)$/));
|
||||
assertNull("foooo".match(/"^foooo(?<=^o*)$/));
|
||||
assertEquals(["foo"], "foo".match(/^foo(?<=^fo+)$/));
|
||||
assertEquals(["foooo"], "foooo".match(/^foooo(?<=^fo*)/));
|
||||
assertEquals(["foo", "f"], "foo".match(/^(f)oo(?<=^\1o+)$/));
|
||||
assertEquals(["foo", "f"], "foo".match(/^(f)oo(?<=^\1o+)$/i));
|
||||
assertEquals(["foo\u1234", "f"], "foo\u1234".match(/^(f)oo(?<=^\1o+).$/i));
|
||||
assertEquals(["def"], "abcdefdef".match(/(?<=^\w+)def/));
|
||||
assertEquals(["def", "def"], "abcdefdef".match(/(?<=^\w+)def/g));
|
||||
|
||||
// Word boundary matches.
|
||||
assertEquals(["def"], "abc def".match(/(?<=\b)[d-f]{3}/));
|
||||
assertEquals(["def"], "ab cdef".match(/(?<=\B)\w{3}/));
|
||||
assertEquals(["def"], "ab cdef".match(/(?<=\B)(?<=c(?<=\w))\w{3}/));
|
||||
assertNull("abcdef".match(/(?<=\b)[d-f]{3}/));
|
||||
|
||||
// Negative lookbehind.
|
||||
assertEquals(["abc"], "abcdef".match(/(?<!abc)\w\w\w/));
|
||||
assertEquals(["abc"], "abcdef".match(/(?<!a.c)\w\w\w/));
|
||||
assertEquals(["abc"], "abcdef".match(/(?<!a\wc)\w\w\w/));
|
||||
assertEquals(["abc"], "abcdef".match(/(?<!a[a-z])\w\w\w/));
|
||||
assertEquals(["abc"], "abcdef".match(/(?<!a[a-z]{2})\w\w\w/));
|
||||
assertNull("abcdef".match(/(?<!abc)def/));
|
||||
assertNull("abcdef".match(/(?<!a.c)def/));
|
||||
assertNull("abcdef".match(/(?<!a\wc)def/));
|
||||
assertNull("abcdef".match(/(?<!a[a-z][a-z])def/));
|
||||
assertNull("abcdef".match(/(?<!a[a-z]{2})def/));
|
||||
assertNull("abcdef".match(/(?<!a{1}b{1})cde/));
|
||||
assertNull("abcdef".match(/(?<!a{1}[a-z]{2})def/));
|
||||
|
||||
// Capturing matches.
|
||||
assertEquals(["def", "c"], "abcdef".match(/(?<=(c))def/));
|
||||
assertEquals(["def", "bc"], "abcdef".match(/(?<=(\w{2}))def/));
|
||||
assertEquals(["def", "bc", "c"], "abcdef".match(/(?<=(\w(\w)))def/));
|
||||
assertEquals(["def", "a"], "abcdef".match(/(?<=(\w){3})def/));
|
||||
assertEquals(["d", "bc", undefined], "abcdef".match(/(?<=(bc)|(cd))./));
|
||||
assertEquals(["c", "a", undefined],
|
||||
"abcdef".match(/(?<=([ab]{1,2})\D|(abc))\w/));
|
||||
assertEquals(["ab", "a", "b"], "abcdef".match(/\D(?<=([ab]+))(\w)/));
|
||||
assertEquals(["c", "d"], "abcdef".match(/(?<=b|c)\w/g));
|
||||
assertEquals(["cd", "ef"], "abcdef".match(/(?<=[b-e])\w{2}/g));
|
||||
|
||||
// Captures inside negative lookbehind. (They never capture.)
|
||||
assertEquals(["de", undefined], "abcdef".match(/(?<!(^|[ab]))\w{2}/));
|
||||
|
||||
// Nested lookaround.
|
||||
assertEquals(["ef"], "abcdef".match(/(?<=ab(?=c)\wd)\w\w/));
|
||||
assertEquals(["ef", "bc"], "abcdef".match(/(?<=a(?=([^a]{2})d)\w{3})\w\w/));
|
||||
assertEquals(["ef", "bc"],
|
||||
"abcdef".match(/(?<=a(?=([bc]{2}(?<!a{2}))d)\w{3})\w\w/));
|
||||
assertNull("abcdef".match(/(?<=a(?=([bc]{2}(?<!a*))d)\w{3})\w\w/));
|
||||
assertEquals(["faaa"], "faaao".match(/^faaao?(?<=^f[oa]+(?=o))/));
|
||||
|
||||
// Back references.
|
||||
assertEquals(["b", "b", "bb"], "abb".match(/(.)(?<=(\1\1))/));
|
||||
assertEquals(["B", "B", "bB"], "abB".match(/(.)(?<=(\1\1))/i));
|
||||
assertEquals(["aB", "aB", "a"], "aabAaBa".match(/((\w)\w)(?<=\1\2\1)/i));
|
||||
assertEquals(["Ba", "Ba", "a"], "aabAaBa".match(/(\w(\w))(?<=\1\2\1)/i));
|
||||
assertEquals(["b", "b", "B"], "abaBbAa".match(/(?=(\w))(?<=(\1))./i));
|
||||
assertEquals(["foo", "'", "foo"], " 'foo' ".match(/(?<=(.))(\w+)(?=\1)/));
|
||||
assertEquals(["foo", "\"", "foo"], " \"foo\" ".match(/(?<=(.))(\w+)(?=\1)/));
|
||||
assertNull(" .foo\" ".match(/(?<=(.))(\w+)(?=\1)/));
|
||||
assertNull("ab".match(/(.)(?<=\1\1\1)/));
|
||||
assertNull("abb".match(/(.)(?<=\1\1\1)/));
|
||||
assertEquals(["b", "b"], "abbb".match(/(.)(?<=\1\1\1)/));
|
||||
assertNull("ab".match(/(..)(?<=\1\1\1)/));
|
||||
assertNull("abb".match(/(..)(?<=\1\1\1)/));
|
||||
assertNull("aabb".match(/(..)(?<=\1\1\1)/));
|
||||
assertNull("abab".match(/(..)(?<=\1\1\1)/));
|
||||
assertNull("fabxbab".match(/(..)(?<=\1\1\1)/));
|
||||
assertNull("faxabab".match(/(..)(?<=\1\1\1)/));
|
||||
assertEquals(["ab", "ab"], "fababab".match(/(..)(?<=\1\1\1)/));
|
||||
|
||||
// Back references to captures inside the lookbehind.
|
||||
assertEquals(["d", "C"], "abcCd".match(/(?<=\1(\w))d/i));
|
||||
assertEquals(["d", "x"], "abxxd".match(/(?<=\1([abx]))d/));
|
||||
assertEquals(["c", "ab"], "ababc".match(/(?<=\1(\w+))c/));
|
||||
assertEquals(["c", "b"], "ababbc".match(/(?<=\1(\w+))c/));
|
||||
assertNull("ababdc".match(/(?<=\1(\w+))c/));
|
||||
assertEquals(["c", "abab"], "ababc".match(/(?<=(\w+)\1)c/));
|
||||
|
||||
// Alternations are tried left to right,
|
||||
// and we do not backtrack into a lookbehind.
|
||||
assertEquals(["xabcd", "cd", ""], "xabcd".match(/.*(?<=(..|...|....))(.*)/));
|
||||
assertEquals(["xabcd", "bcd", ""], "xabcd".match(/.*(?<=(xx|...|....))(.*)/));
|
||||
assertEquals(["xxabcd", "bcd", ""], "xxabcd".match(/.*(?<=(xx|...))(.*)/));
|
||||
assertEquals(["xxabcd", "xx", "abcd"], "xxabcd".match(/.*(?<=(xx|xxx))(.*)/));
|
||||
|
||||
// We do not backtrack into a lookbehind.
|
||||
// The lookbehind captures "abc" so that \1 does not match. We do not backtrack
|
||||
// to capture only "bc" in the lookbehind.
|
||||
assertNull("abcdbc".match(/(?<=([abc]+)).\1/));
|
||||
|
||||
// Greedy loop.
|
||||
assertEquals(["c", "bbbbbb"], "abbbbbbc".match(/(?<=(b+))c/));
|
||||
assertEquals(["c", "b1234"], "ab1234c".match(/(?<=(b\d+))c/));
|
||||
assertEquals(["c", "b12b23b34"], "ab12b23b34c".match(/(?<=((?:b\d{2})+))c/));
|
||||
|
||||
// Sticky
|
||||
var re1 = /(?<=^(\w+))def/g;
|
||||
assertEquals(["def", "abc"], re1.exec("abcdefdef"));
|
||||
assertEquals(["def", "abcdef"], re1.exec("abcdefdef"));
|
||||
var re2 = /\Bdef/g;
|
||||
assertEquals(["def"], re2.exec("abcdefdef"));
|
||||
assertEquals(["def"], re2.exec("abcdefdef"));
|
||||
|
||||
// Misc
|
||||
assertNull("abcdef".match(/(?<=$abc)def/));
|
||||
assertEquals(["foo"], "foo".match(/^foo(?<=foo)$/));
|
||||
assertEquals(["foo"], "foo".match(/^f.o(?<=foo)$/));
|
||||
assertNull("fno".match(/^f.o(?<=foo)$/));
|
||||
assertNull("foo".match(/^foo(?<!foo)$/));
|
||||
assertNull("foo".match(/^f.o(?<!foo)$/));
|
||||
assertEquals(["fno"], "fno".match(/^f.o(?<!foo)$/));
|
||||
assertEquals(["foooo"], "foooo".match(/^foooo(?<=fo+)$/));
|
||||
assertEquals(["foooo"], "foooo".match(/^foooo(?<=fo*)$/));
|
||||
assertEquals(["abc", "abc"], /(abc\1)/.exec("abc"));
|
||||
assertEquals(["abc", "abc"], /(abc\1)/.exec("abc\u1234"));
|
||||
assertEquals(["abc", "abc"], /(abc\1)/i.exec("abc"));
|
||||
assertEquals(["abc", "abc"], /(abc\1)/i.exec("abc\u1234"));
|
||||
var oob_subject = "abcdefghijklmnabcdefghijklmn".substr(14);
|
||||
assertNull(oob_subject.match(/(?=(abcdefghijklmn))(?<=\1)a/i));
|
||||
assertNull(oob_subject.match(/(?=(abcdefghijklmn))(?<=\1)a/));
|
Loading…
Reference in New Issue
Block a user