Browse Source

LibRegex: Check inverse_matched after every op, not just at the end

Fixes #13755.

Co-Authored-By: Damien Firmenich <fir.damien@gmail.com>
Ali Mohammad Pur 3 years ago
parent
commit
1409a48da6
2 changed files with 20 additions and 2 deletions
  1. 11 0
      Tests/LibRegex/Regex.cpp
  2. 9 2
      Userland/Libraries/LibRegex/RegexByteCode.cpp

+ 11 - 0
Tests/LibRegex/Regex.cpp

@@ -1034,3 +1034,14 @@ TEST_CASE(single_match_flag)
         EXPECT_EQ(result.matches.first().view.to_string(), "A"sv);
     }
 }
+
+TEST_CASE(inversion_state_in_char_class)
+{
+    // #13755, /[\S\s]/.exec("hello") should be [ "h" ], not null.
+    Regex<ECMA262> re("[\\S\\s]", ECMAScriptFlags::Global | (ECMAScriptFlags)regex::AllFlags::SingleMatch);
+
+    auto result = re.match("hello");
+    EXPECT_EQ(result.success, true);
+    EXPECT_EQ(result.matches.size(), 1u);
+    EXPECT_EQ(result.matches.first().view.to_string(), "h"sv);
+}

+ 9 - 2
Userland/Libraries/LibRegex/RegexByteCode.cpp

@@ -458,16 +458,18 @@ ALWAYS_INLINE ExecutionResult OpCode_Compare::execute(MatchInput const& input, M
 
         auto compare_type = (CharacterCompareType)m_bytecode->at(offset++);
 
-        if (compare_type == CharacterCompareType::Inverse)
+        if (compare_type == CharacterCompareType::Inverse) {
             inverse = true;
+            continue;
 
-        else if (compare_type == CharacterCompareType::TemporaryInverse) {
+        } else if (compare_type == CharacterCompareType::TemporaryInverse) {
             // If "TemporaryInverse" is given, negate the current inversion state only for the next opcode.
             // it follows that this cannot be the last compare element.
             VERIFY(i != arguments_count() - 1);
 
             temporary_inverse = true;
             reset_temp_inverse = false;
+            continue;
 
         } else if (compare_type == CharacterCompareType::Char) {
             u32 ch = m_bytecode->at(offset++);
@@ -598,6 +600,11 @@ ALWAYS_INLINE ExecutionResult OpCode_Compare::execute(MatchInput const& input, M
             VERIFY_NOT_REACHED();
             break;
         }
+
+        if (current_inversion_state() && !inverse_matched) {
+            advance_string_position(state, input.view);
+            inverse_matched = true;
+        }
     }
 
     if (current_inversion_state() && !inverse_matched)