Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions deps/v8/src/regexp/regexp-compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
return CompilationResult::RegExpTooBig();
};

ZoneVector<RegExpNode*> work_list(zone());
ZoneVector<WorkItem> work_list(zone());
work_list_ = &work_list;
Label fail;
macro_assembler_->PushBacktrack(&fail);
Expand All @@ -357,11 +357,13 @@ RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
macro_assembler_->BindJumpTarget(&fail);
macro_assembler_->Fail();
while (!work_list.empty()) {
RegExpNode* node = work_list.back();
WorkItem item = work_list.back();
RegExpNode* node = item.node;
TRACE_WITH_NODE(this, "Popping from worklist ", node);
work_list.pop_back();
node->set_on_work_list(false);
if (!node->label()->is_bound()) {
set_flags(item.flags);
if (node->Emit(this, &new_trace).IsError()) {
return ReportError();
}
Expand Down Expand Up @@ -2132,6 +2134,7 @@ EmitResult AssertionNode::EmitBoundaryCheck(RegExpCompiler* compiler,
}
bool at_boundary = (assertion_type_ == AssertionNode::AT_BOUNDARY);
if (next_is_word_character == Trace::UNKNOWN) {
const RegExpFlags flags = compiler->flags();
Label before_non_word;
Label before_word;
if (trace->characters_preloaded() != 1) {
Expand All @@ -2145,7 +2148,7 @@ EmitResult AssertionNode::EmitBoundaryCheck(RegExpCompiler* compiler,
RETURN_IF_ERROR(BacktrackIfPrevious(compiler, trace,
at_boundary ? kIsNonWord : kIsWord));
assembler->GoTo(&ok);

compiler->set_flags(flags);
assembler->Bind(&before_word);
RETURN_IF_ERROR(BacktrackIfPrevious(compiler, trace,
at_boundary ? kIsWord : kIsNonWord));
Expand Down Expand Up @@ -3841,8 +3844,13 @@ class Analysis : public NodeVisitor {
}

void VisitChoice(ChoiceNode* that) override {
// Flags need to be reset to the state of the ChoiceNode at the beginning
// of each alternative, as flags might be modified when visiting an
// alternative.
RegExpFlags header_flags = flags();
for (int i = 0; i < that->alternatives()->length(); i++) {
EnsureAnalyzed(that->alternatives()->at(i).node());
set_flags(header_flags);
if (has_failed()) return;
STATIC_FOR_EACH(Propagators::VisitChoice(that, i));
}
Expand Down
9 changes: 7 additions & 2 deletions deps/v8/src/regexp/regexp-compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -570,10 +570,15 @@ class RegExpCompiler {
// lead surrogate and start matching from there.
RegExpNode* OptionallyStepBackToLeadSurrogate(RegExpNode* on_success);

struct WorkItem {
RegExpNode* node;
RegExpFlags flags;
};

inline void AddWork(RegExpNode* node) {
if (!node->on_work_list() && !node->label()->is_bound()) {
node->set_on_work_list(true);
work_list_->push_back(node);
work_list_->push_back({node, flags()});
}
}

Expand Down Expand Up @@ -644,7 +649,7 @@ class RegExpCompiler {
int next_register_;
int unicode_lookaround_stack_register_;
int unicode_lookaround_position_register_;
ZoneVector<RegExpNode*>* work_list_;
ZoneVector<WorkItem>* work_list_;
int recursion_depth_;
RegExpFlags flags_;
RegExpMacroAssembler* macro_assembler_;
Expand Down
2 changes: 1 addition & 1 deletion deps/v8/src/regexp/regexp-parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -988,7 +988,7 @@ RegExpTree* RegExpParserImpl<CharT>::ParseDisjunction() {
// Restore previous state.
state = state->previous_state();
builder = state->builder();

flags_ = builder->flags();
builder->AddAtom(body);
// For compatibility with JSC and ES3, we allow quantifiers after
// lookaheads, and break in all cases.
Expand Down
30 changes: 30 additions & 0 deletions deps/v8/test/mjsunit/regexp-modifiers.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,33 @@ test(
/(?m:^f(?si:.o)$)/, ['foo', '\nfoO', 'f\no\n', '\nf\rO\n'],
['Foo', 'F\no\n']);
test(/(?i:.oo)/, ['Foo', 'FOO', 'fOo', 'foO']);

// Nodes inside a modifier group deferred to the work list must be compiled with
// the group's inner flags, not the outer flags.
test(
/^(?i:(?:a|b)(?:c|d)(?:e|f)(?:g|h)(?:i|j)(?:k|l)(?:m|n)X)/,
['acegikmx', 'acegikmX', 'bdfhjlnx', 'bdfhjlnX'],
['acegikmy', 'bdfhjlny']);

// Symmetrically, nodes inside a (?-i:...) group under outer /i must compile
// case-sensitively even when deferred.
test(
/^(?-i:(?:a|b)(?:c|d)(?:e|f)(?:g|h)(?:i|j)(?:k|l)(?:m|n)x)/i,
['acegikmx', 'bdfhjlnx'],
['acegikmX', 'bdfhjlnX']);

test(/(?i:x|[a-f])/, ['F']);
test(/(?i:foo)[x-z]/v, ['Foox', 'fOoz'], ['fooX','FooZ']);

// Boundary assertions inside modifier groups with alternations must not leak
// flags between branches during code generation.
test(/(?i:\.|\bNULL)/, ['null', 'NULL', 'Null', '.foo'], ['xyz']);
test(/(?i:\bNULL|\.)/, ['null', 'NULL', '.foo'], ['xyz']);
test(/(?i:\.|\BNULL)/, ['xnull', 'xNULL'], ['null', 'xyz']);
test(/(?i:\W|\bNULL)/, ['null', 'NULL', '!'], ['x']);
test(/(?i:!|\?|\bNULL)/, ['null', 'NULL', '!', '?'], ['x']);
test(/(?i:\.|\bnUll)/, ['null', 'NULL']);
test(/(?i:\.|\bnuLl)/, ['null', 'NULL']);
test(/(?i:\.|\bnulL)/, ['null', 'NULL']);
test(/(?-i:(?i:\.|\bNULL))/, ['null', 'NULL', '.foo'], ['xyz']);
test(/(?i:(?-i:\.|\bNULL))/, ['NULL', '.foo'], ['null', 'xyz']);