diff --git a/common.gypi b/common.gypi index 2967d1e63a48..e208978fabbf 100644 --- a/common.gypi +++ b/common.gypi @@ -44,7 +44,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.40', + 'v8_embedder_string': '-node.45', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/regexp/regexp-compiler.cc b/deps/v8/src/regexp/regexp-compiler.cc index a3e7c170d430..ba1452be9fca 100644 --- a/deps/v8/src/regexp/regexp-compiler.cc +++ b/deps/v8/src/regexp/regexp-compiler.cc @@ -346,7 +346,7 @@ RegExpCompiler::CompilationResult RegExpCompiler::Assemble( return CompilationResult::RegExpTooBig(); }; - ZoneVector work_list(zone()); + ZoneVector work_list(zone()); work_list_ = &work_list; Label fail; macro_assembler_->PushBacktrack(&fail); @@ -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(); } @@ -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) { @@ -2146,6 +2149,7 @@ EmitResult AssertionNode::EmitBoundaryCheck(RegExpCompiler* compiler, 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)); @@ -3841,8 +3845,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)); } @@ -3880,11 +3889,13 @@ class Analysis : public NodeVisitor { NegativeLookaroundChoiceNode* that) override { DCHECK_EQ(that->alternatives()->length(), 2); // Lookaround and continue. + RegExpFlags header_flags = flags(); EnsureAnalyzed(that->lookaround_node()); if (has_failed()) return; STATIC_FOR_EACH( Propagators::VisitNegativeLookaroundChoiceLookaroundNode(that)); + set_flags(header_flags); EnsureAnalyzed(that->continue_node()); if (has_failed()) return; STATIC_FOR_EACH( diff --git a/deps/v8/src/regexp/regexp-compiler.h b/deps/v8/src/regexp/regexp-compiler.h index 6e5a8dcb66e2..d90bf720da0d 100644 --- a/deps/v8/src/regexp/regexp-compiler.h +++ b/deps/v8/src/regexp/regexp-compiler.h @@ -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()}); } } @@ -644,7 +649,7 @@ class RegExpCompiler { int next_register_; int unicode_lookaround_stack_register_; int unicode_lookaround_position_register_; - ZoneVector* work_list_; + ZoneVector* work_list_; int recursion_depth_; RegExpFlags flags_; RegExpMacroAssembler* macro_assembler_; diff --git a/deps/v8/src/regexp/regexp-parser.cc b/deps/v8/src/regexp/regexp-parser.cc index 2792cd0f1e91..0c9bbad8497d 100644 --- a/deps/v8/src/regexp/regexp-parser.cc +++ b/deps/v8/src/regexp/regexp-parser.cc @@ -988,6 +988,7 @@ RegExpTree* RegExpParserImpl::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 diff --git a/deps/v8/test/mjsunit/regexp-modifiers.js b/deps/v8/test/mjsunit/regexp-modifiers.js index e012dc8d406c..91b1f2364bdc 100644 --- a/deps/v8/test/mjsunit/regexp-modifiers.js +++ b/deps/v8/test/mjsunit/regexp-modifiers.js @@ -73,3 +73,40 @@ test( /(?m:^f(?si:.o)$)/, ['foo', '\nfoO', 'f\no\n', '\nf\rO\n'], ['Foo', 'F\no\n']); test(/(?i:.oo)/, ['Foo', 'FOO', 'fOo', 'foO']); + +test(/(?i:foo)[x-z]/v, ['Foox', 'fOoz'], ['fooX','FooZ']); + +// 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']); + +// 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']); + +// A modifier group inside a negative lookaround must not leak its flags into +// the continuation. Analysis visits the two branches of the lookaround choice +// in sequence, so without a reset the continuation's classes are made case +// independent under the group's flags rather than the pattern's. +test(/(?!(?i:x|y))[K]/, ['K'], ['k']); +test(/(?