diff --git a/Libraries/LibRegex/RegexByteCode.cpp b/Libraries/LibRegex/RegexByteCode.cpp index 5447b4f0edf..8458cdf61b8 100644 --- a/Libraries/LibRegex/RegexByteCode.cpp +++ b/Libraries/LibRegex/RegexByteCode.cpp @@ -217,6 +217,12 @@ ALWAYS_INLINE ExecutionResult OpCode_FailForks::execute(MatchInput const& input, input.fail_counter += state.forks_since_last_save; return ExecutionResult::Failed_ExecuteLowPrioForks; } +ALWAYS_INLINE ExecutionResult OpCode_PopSaved::execute(MatchInput const& input, MatchState&) const +{ + input.saved_positions.take_last(); + input.saved_code_unit_positions.take_last(); + return ExecutionResult::Failed_ExecuteLowPrioForks; +} ALWAYS_INLINE ExecutionResult OpCode_Jump::execute(MatchInput const&, MatchState& state) const { diff --git a/Libraries/LibRegex/RegexByteCode.h b/Libraries/LibRegex/RegexByteCode.h index 5fede59fd65..b9a8cdb57b2 100644 --- a/Libraries/LibRegex/RegexByteCode.h +++ b/Libraries/LibRegex/RegexByteCode.h @@ -31,6 +31,7 @@ using ByteCodeValueType = u64; __ENUMERATE_OPCODE(ForkReplaceJump) \ __ENUMERATE_OPCODE(ForkReplaceStay) \ __ENUMERATE_OPCODE(FailForks) \ + __ENUMERATE_OPCODE(PopSaved) \ __ENUMERATE_OPCODE(SaveLeftCaptureGroup) \ __ENUMERATE_OPCODE(SaveRightCaptureGroup) \ __ENUMERATE_OPCODE(SaveRightNamedCaptureGroup) \ @@ -266,9 +267,15 @@ public: switch (type) { case LookAroundType::LookAhead: { // SAVE + // FORKJUMP _BODY + // POPSAVED + // LABEL _BODY // REGEXP BODY // RESTORE empend((ByteCodeValueType)OpCodeId::Save); + empend((ByteCodeValueType)OpCodeId::ForkJump); + empend((ByteCodeValueType)1); + empend((ByteCodeValueType)OpCodeId::PopSaved); extend(move(lookaround_body)); empend((ByteCodeValueType)OpCodeId::Restore); return; @@ -613,6 +620,14 @@ public: ByteString arguments_string() const override { return ByteString::empty(); } }; +class OpCode_PopSaved final : public OpCode { +public: + ExecutionResult execute(MatchInput const& input, MatchState& state) const override; + ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::PopSaved; } + ALWAYS_INLINE size_t size() const override { return 1; } + ByteString arguments_string() const override { return ByteString::empty(); } +}; + class OpCode_Save final : public OpCode { public: ExecutionResult execute(MatchInput const& input, MatchState& state) const override; diff --git a/Tests/LibRegex/Regex.cpp b/Tests/LibRegex/Regex.cpp index 5d9e5045d3f..5da284c3b0a 100644 --- a/Tests/LibRegex/Regex.cpp +++ b/Tests/LibRegex/Regex.cpp @@ -738,6 +738,8 @@ TEST_CASE(ECMA262_match) // Optimizer bug, ignoring an enabled trailing 'invert' when comparing blocks, ladybird#3421. { "[^]*[^]"sv, "i"sv, true }, { "xx|...|...."sv, "cd"sv, false }, + // Tests nested lookahead with alternation - verifies proper save/restore stack cleanup + { "a(?=.(?=c)|b)b"sv, "ab"sv, true }, }; for (auto& test : tests) {