Coverage Report

Created: 2026-09-26 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibRegex/RegexByteCode.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include "RegexBytecodeStreamOptimizer.h"
10
#include "RegexMatch.h"
11
#include "RegexOptions.h"
12
13
#include <AK/Concepts.h>
14
#include <AK/DisjointChunks.h>
15
#include <AK/Forward.h>
16
#include <AK/HashMap.h>
17
#include <AK/NonnullOwnPtr.h>
18
#include <AK/OwnPtr.h>
19
#include <AK/Traits.h>
20
#include <AK/TypeCasts.h>
21
#include <AK/Types.h>
22
#include <AK/Vector.h>
23
#include <LibUnicode/Forward.h>
24
25
namespace regex {
26
27
using ByteCodeValueType = u64;
28
29
#define ENUMERATE_OPCODES                          \
30
6
    __ENUMERATE_OPCODE(Compare)                    \
31
6
    __ENUMERATE_OPCODE(Jump)                       \
32
6
    __ENUMERATE_OPCODE(JumpNonEmpty)               \
33
6
    __ENUMERATE_OPCODE(ForkJump)                   \
34
6
    __ENUMERATE_OPCODE(ForkStay)                   \
35
6
    __ENUMERATE_OPCODE(ForkReplaceJump)            \
36
6
    __ENUMERATE_OPCODE(ForkReplaceStay)            \
37
6
    __ENUMERATE_OPCODE(FailForks)                  \
38
6
    __ENUMERATE_OPCODE(SaveLeftCaptureGroup)       \
39
6
    __ENUMERATE_OPCODE(SaveRightCaptureGroup)      \
40
6
    __ENUMERATE_OPCODE(SaveRightNamedCaptureGroup) \
41
6
    __ENUMERATE_OPCODE(CheckBegin)                 \
42
6
    __ENUMERATE_OPCODE(CheckEnd)                   \
43
6
    __ENUMERATE_OPCODE(CheckBoundary)              \
44
6
    __ENUMERATE_OPCODE(Save)                       \
45
6
    __ENUMERATE_OPCODE(Restore)                    \
46
6
    __ENUMERATE_OPCODE(GoBack)                     \
47
6
    __ENUMERATE_OPCODE(ClearCaptureGroup)          \
48
6
    __ENUMERATE_OPCODE(Repeat)                     \
49
6
    __ENUMERATE_OPCODE(ResetRepeat)                \
50
6
    __ENUMERATE_OPCODE(Checkpoint)                 \
51
6
    __ENUMERATE_OPCODE(Exit)
52
53
// clang-format off
54
enum class OpCodeId : ByteCodeValueType {
55
#define __ENUMERATE_OPCODE(x) x,
56
    ENUMERATE_OPCODES
57
#undef __ENUMERATE_OPCODE
58
59
    First = Compare,
60
    Last = Exit,
61
};
62
// clang-format on
63
64
#define ENUMERATE_CHARACTER_COMPARE_TYPES                    \
65
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(Undefined)            \
66
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(Inverse)              \
67
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(TemporaryInverse)     \
68
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(AnyChar)              \
69
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(Char)                 \
70
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(String)               \
71
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(CharClass)            \
72
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(CharRange)            \
73
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(Reference)            \
74
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(Property)             \
75
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(GeneralCategory)      \
76
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(Script)               \
77
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(ScriptExtension)      \
78
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(RangeExpressionDummy) \
79
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(LookupTable)          \
80
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(And)                  \
81
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(Or)                   \
82
0
    __ENUMERATE_CHARACTER_COMPARE_TYPE(EndAndOr)
83
84
enum class CharacterCompareType : ByteCodeValueType {
85
#define __ENUMERATE_CHARACTER_COMPARE_TYPE(x) x,
86
    ENUMERATE_CHARACTER_COMPARE_TYPES
87
#undef __ENUMERATE_CHARACTER_COMPARE_TYPE
88
};
89
90
#define ENUMERATE_CHARACTER_CLASSES    \
91
0
    __ENUMERATE_CHARACTER_CLASS(Alnum) \
92
0
    __ENUMERATE_CHARACTER_CLASS(Cntrl) \
93
0
    __ENUMERATE_CHARACTER_CLASS(Lower) \
94
0
    __ENUMERATE_CHARACTER_CLASS(Space) \
95
0
    __ENUMERATE_CHARACTER_CLASS(Alpha) \
96
0
    __ENUMERATE_CHARACTER_CLASS(Digit) \
97
0
    __ENUMERATE_CHARACTER_CLASS(Print) \
98
0
    __ENUMERATE_CHARACTER_CLASS(Upper) \
99
0
    __ENUMERATE_CHARACTER_CLASS(Blank) \
100
0
    __ENUMERATE_CHARACTER_CLASS(Graph) \
101
0
    __ENUMERATE_CHARACTER_CLASS(Punct) \
102
0
    __ENUMERATE_CHARACTER_CLASS(Word)  \
103
0
    __ENUMERATE_CHARACTER_CLASS(Xdigit)
104
105
enum class CharClass : ByteCodeValueType {
106
#define __ENUMERATE_CHARACTER_CLASS(x) x,
107
    ENUMERATE_CHARACTER_CLASSES
108
#undef __ENUMERATE_CHARACTER_CLASS
109
};
110
111
#define ENUMERATE_BOUNDARY_CHECK_TYPES    \
112
0
    __ENUMERATE_BOUNDARY_CHECK_TYPE(Word) \
113
0
    __ENUMERATE_BOUNDARY_CHECK_TYPE(NonWord)
114
115
enum class BoundaryCheckType : ByteCodeValueType {
116
#define __ENUMERATE_BOUNDARY_CHECK_TYPE(x) x,
117
    ENUMERATE_BOUNDARY_CHECK_TYPES
118
#undef __ENUMERATE_BOUNDARY_CHECK_TYPE
119
};
120
121
struct CharRange {
122
    u32 const from;
123
    u32 const to;
124
125
    CharRange(u64 value)
126
56.8M
        : from(value >> 32)
127
56.8M
        , to(value & 0xffffffff)
128
56.8M
    {
129
56.8M
    }
130
131
    CharRange(u32 from, u32 to)
132
90.6M
        : from(from)
133
90.6M
        , to(to)
134
90.6M
    {
135
90.6M
    }
136
137
18.3M
    operator ByteCodeValueType() const { return ((u64)from << 32) | to; }
138
};
139
140
struct CompareTypeAndValuePair {
141
    CharacterCompareType type;
142
    ByteCodeValueType value;
143
};
144
145
class OpCode;
146
147
class ByteCode : public DisjointChunks<ByteCodeValueType> {
148
    using Base = DisjointChunks<ByteCodeValueType>;
149
150
public:
151
    ByteCode()
152
186M
    {
153
186M
        ensure_opcodes_initialized();
154
186M
    }
155
156
66.1M
    ByteCode(ByteCode const&) = default;
157
252M
    virtual ~ByteCode() = default;
158
159
3.00M
    ByteCode& operator=(ByteCode&&) = default;
160
    ByteCode& operator=(Base&& value)
161
0
    {
162
0
        static_cast<Base&>(*this) = move(value);
163
0
        return *this;
164
0
    }
165
166
    template<typename... Args>
167
    void empend(Args&&... args)
168
379M
    {
169
379M
        if (is_empty())
170
65.4M
            Base::append({});
171
379M
        Base::last_chunk().empend(forward<Args>(args)...);
172
379M
    }
void regex::ByteCode::empend<unsigned long>(unsigned long&&)
Line
Count
Source
168
276M
    {
169
276M
        if (is_empty())
170
65.4M
            Base::append({});
171
276M
        Base::last_chunk().empend(forward<Args>(args)...);
172
276M
    }
void regex::ByteCode::empend<unsigned long&>(unsigned long&)
Line
Count
Source
168
102M
    {
169
102M
        if (is_empty())
170
941
            Base::append({});
171
102M
        Base::last_chunk().empend(forward<Args>(args)...);
172
102M
    }
void regex::ByteCode::empend<unsigned int>(unsigned int&&)
Line
Count
Source
168
587k
    {
169
587k
        if (is_empty())
170
0
            Base::append({});
171
587k
        Base::last_chunk().empend(forward<Args>(args)...);
172
587k
    }
173
    template<typename T>
174
    void append(T&& value)
175
188M
    {
176
188M
        if (is_empty())
177
71.4M
            Base::append({});
178
188M
        Base::last_chunk().append(forward<T>(value));
179
188M
    }
void regex::ByteCode::append<unsigned long>(unsigned long&&)
Line
Count
Source
175
97.1M
    {
176
97.1M
        if (is_empty())
177
71.4M
            Base::append({});
178
97.1M
        Base::last_chunk().append(forward<T>(value));
179
97.1M
    }
void regex::ByteCode::append<regex::CharRange>(regex::CharRange&&)
Line
Count
Source
175
11.5M
    {
176
11.5M
        if (is_empty())
177
0
            Base::append({});
178
11.5M
        Base::last_chunk().append(forward<T>(value));
179
11.5M
    }
void regex::ByteCode::append<int>(int&&)
Line
Count
Source
175
11.8M
    {
176
11.8M
        if (is_empty())
177
0
            Base::append({});
178
11.8M
        Base::last_chunk().append(forward<T>(value));
179
11.8M
    }
void regex::ByteCode::append<unsigned long&>(unsigned long&)
Line
Count
Source
175
67.6M
    {
176
67.6M
        if (is_empty())
177
0
            Base::append({});
178
67.6M
        Base::last_chunk().append(forward<T>(value));
179
67.6M
    }
180
    template<typename T>
181
    void prepend(T&& value)
182
1.29M
    {
183
1.29M
        if (is_empty())
184
380
            return append(forward<T>(value));
185
1.29M
        Base::first_chunk().prepend(forward<T>(value));
186
1.29M
    }
187
188
    void append(Span<ByteCodeValueType const> value)
189
325k
    {
190
325k
        if (is_empty())
191
0
            Base::append({});
192
325k
        auto& last = Base::last_chunk();
193
325k
        last.ensure_capacity(value.size());
194
325k
        for (auto v : value)
195
44.2M
            last.unchecked_append(v);
196
325k
    }
197
198
    void ensure_capacity(size_t capacity)
199
325k
    {
200
325k
        if (is_empty())
201
552
            Base::append({});
202
325k
        Base::last_chunk().ensure_capacity(capacity);
203
325k
    }
204
205
    void last_chunk() const = delete;
206
    void first_chunk() const = delete;
207
208
    void insert_bytecode_compare_values(Vector<CompareTypeAndValuePair>&& pairs)
209
60.2M
    {
210
60.2M
        Optimizer::append_character_class(*this, move(pairs));
211
60.2M
    }
212
213
    void insert_bytecode_check_boundary(BoundaryCheckType type)
214
330k
    {
215
330k
        ByteCode bytecode;
216
330k
        bytecode.empend((ByteCodeValueType)OpCodeId::CheckBoundary);
217
330k
        bytecode.empend((ByteCodeValueType)type);
218
219
330k
        extend(move(bytecode));
220
330k
    }
221
222
    void insert_bytecode_clear_capture_group(size_t index)
223
36.0M
    {
224
36.0M
        empend(static_cast<ByteCodeValueType>(OpCodeId::ClearCaptureGroup));
225
36.0M
        empend(index);
226
36.0M
    }
227
228
    void insert_bytecode_compare_string(StringView view)
229
1.65M
    {
230
1.65M
        empend(static_cast<ByteCodeValueType>(OpCodeId::Compare));
231
1.65M
        empend(static_cast<u64>(1)); // number of arguments
232
1.65M
        empend(2 + view.length());   // size of arguments
233
1.65M
        empend(static_cast<ByteCodeValueType>(CharacterCompareType::String));
234
1.65M
        insert_string(view);
235
1.65M
    }
236
237
    void insert_bytecode_group_capture_left(size_t capture_groups_count)
238
1.84M
    {
239
1.84M
        empend(static_cast<ByteCodeValueType>(OpCodeId::SaveLeftCaptureGroup));
240
1.84M
        empend(capture_groups_count);
241
1.84M
    }
242
243
    void insert_bytecode_group_capture_right(size_t capture_groups_count)
244
1.84M
    {
245
1.84M
        empend(static_cast<ByteCodeValueType>(OpCodeId::SaveRightCaptureGroup));
246
1.84M
        empend(capture_groups_count);
247
1.84M
    }
248
249
    void insert_bytecode_group_capture_right(size_t capture_groups_count, StringView name)
250
488
    {
251
488
        empend(static_cast<ByteCodeValueType>(OpCodeId::SaveRightNamedCaptureGroup));
252
488
        empend(reinterpret_cast<ByteCodeValueType>(name.characters_without_null_termination()));
253
488
        empend(name.length());
254
488
        empend(capture_groups_count);
255
488
    }
256
257
    enum class LookAroundType {
258
        LookAhead,
259
        LookBehind,
260
        NegatedLookAhead,
261
        NegatedLookBehind,
262
    };
263
    void insert_bytecode_lookaround(ByteCode&& lookaround_body, LookAroundType type, size_t match_length = 0)
264
2.38k
    {
265
        // FIXME: The save stack will grow infinitely with repeated failures
266
        //        as we do not discard that on failure (we don't necessarily know how many to pop with the current architecture).
267
2.38k
        switch (type) {
268
168
        case LookAroundType::LookAhead: {
269
            // SAVE
270
            // REGEXP BODY
271
            // RESTORE
272
168
            empend((ByteCodeValueType)OpCodeId::Save);
273
168
            extend(move(lookaround_body));
274
168
            empend((ByteCodeValueType)OpCodeId::Restore);
275
168
            return;
276
0
        }
277
80
        case LookAroundType::NegatedLookAhead: {
278
            // JUMP _A
279
            // LABEL _L
280
            // REGEXP BODY
281
            // FAIL
282
            // LABEL _A
283
            // SAVE
284
            // FORKJUMP _L
285
            // RESTORE
286
80
            auto body_length = lookaround_body.size();
287
80
            empend((ByteCodeValueType)OpCodeId::Jump);
288
80
            empend((ByteCodeValueType)body_length + 1); // JUMP to label _A
289
80
            extend(move(lookaround_body));
290
80
            empend((ByteCodeValueType)OpCodeId::FailForks);
291
80
            empend((ByteCodeValueType)OpCodeId::Save);
292
80
            empend((ByteCodeValueType)OpCodeId::ForkJump);
293
80
            empend((ByteCodeValueType) - (body_length + 4)); // JUMP to label _L
294
80
            empend((ByteCodeValueType)OpCodeId::Restore);
295
80
            return;
296
0
        }
297
231
        case LookAroundType::LookBehind:
298
            // SAVE
299
            // GOBACK match_length(BODY)
300
            // REGEXP BODY
301
            // RESTORE
302
231
            empend((ByteCodeValueType)OpCodeId::Save);
303
231
            empend((ByteCodeValueType)OpCodeId::GoBack);
304
231
            empend((ByteCodeValueType)match_length);
305
231
            extend(move(lookaround_body));
306
231
            empend((ByteCodeValueType)OpCodeId::Restore);
307
231
            return;
308
1.90k
        case LookAroundType::NegatedLookBehind: {
309
            // JUMP _A
310
            // LABEL _L
311
            // GOBACK match_length(BODY)
312
            // REGEXP BODY
313
            // FAIL
314
            // LABEL _A
315
            // SAVE
316
            // FORKJUMP _L
317
            // RESTORE
318
1.90k
            auto body_length = lookaround_body.size();
319
1.90k
            empend((ByteCodeValueType)OpCodeId::Jump);
320
1.90k
            empend((ByteCodeValueType)body_length + 3); // JUMP to label _A
321
1.90k
            empend((ByteCodeValueType)OpCodeId::GoBack);
322
1.90k
            empend((ByteCodeValueType)match_length);
323
1.90k
            extend(move(lookaround_body));
324
1.90k
            empend((ByteCodeValueType)OpCodeId::FailForks);
325
1.90k
            empend((ByteCodeValueType)OpCodeId::Save);
326
1.90k
            empend((ByteCodeValueType)OpCodeId::ForkJump);
327
1.90k
            empend((ByteCodeValueType) - (body_length + 6)); // JUMP to label _L
328
1.90k
            empend((ByteCodeValueType)OpCodeId::Restore);
329
1.90k
            return;
330
0
        }
331
2.38k
        }
332
333
0
        VERIFY_NOT_REACHED();
334
0
    }
335
336
    void insert_bytecode_alternation(ByteCode&& left, ByteCode&& right)
337
1.04k
    {
338
339
        // FORKJUMP _ALT
340
        // REGEXP ALT2
341
        // JUMP  _END
342
        // LABEL _ALT
343
        // REGEXP ALT1
344
        // LABEL _END
345
346
        // Optimisation: Eliminate extra work by unifying common pre-and-postfix exprs.
347
1.04k
        Optimizer::append_alternation(*this, move(left), move(right));
348
1.04k
    }
349
350
    template<Integral T>
351
    static void transform_bytecode_repetition_min_max(ByteCode& bytecode_to_repeat, T minimum, Optional<T> maximum, size_t min_repetition_mark_id, size_t max_repetition_mark_id, bool greedy = true)
352
28.8k
    {
353
28.8k
        if (!maximum.has_value()) {
354
18.6k
            if (minimum == 0)
355
524
                return transform_bytecode_repetition_any(bytecode_to_repeat, greedy);
356
18.1k
            if (minimum == 1)
357
13.9k
                return transform_bytecode_repetition_min_one(bytecode_to_repeat, greedy);
358
18.1k
        }
359
360
14.3k
        ByteCode new_bytecode;
361
14.3k
        new_bytecode.insert_bytecode_repetition_n(bytecode_to_repeat, minimum, min_repetition_mark_id);
362
363
14.3k
        if (maximum.has_value()) {
364
            // (REPEAT REGEXP MIN)
365
            // LABEL _MAX_LOOP            |
366
            // FORK END                   |
367
            // REGEXP                     |
368
            // REPEAT _MAX_LOOP MAX-MIN   | if max > min
369
            // FORK END                   |
370
            // REGEXP                     |
371
            // LABEL END                  |
372
            // RESET _MAX_LOOP            |
373
10.1k
            auto jump_kind = static_cast<ByteCodeValueType>(greedy ? OpCodeId::ForkStay : OpCodeId::ForkJump);
374
10.1k
            if (maximum.value() > minimum) {
375
9.81k
                new_bytecode.empend(jump_kind);
376
9.81k
                new_bytecode.empend((ByteCodeValueType)0); // Placeholder for the jump target.
377
9.81k
                auto pre_loop_fork_jump_index = new_bytecode.size();
378
9.81k
                new_bytecode.extend(bytecode_to_repeat);
379
9.81k
                auto repetitions = maximum.value() - minimum;
380
9.81k
                auto fork_jump_address = new_bytecode.size();
381
9.81k
                if (repetitions > 1) {
382
9.34k
                    new_bytecode.empend((ByteCodeValueType)OpCodeId::Repeat);
383
9.34k
                    new_bytecode.empend(bytecode_to_repeat.size() + 2);
384
9.34k
                    new_bytecode.empend(static_cast<ByteCodeValueType>(repetitions - 1));
385
9.34k
                    new_bytecode.empend(max_repetition_mark_id);
386
9.34k
                    new_bytecode.empend(jump_kind);
387
9.34k
                    new_bytecode.empend((ByteCodeValueType)0); // Placeholder for the jump target.
388
9.34k
                    auto post_loop_fork_jump_index = new_bytecode.size();
389
9.34k
                    new_bytecode.extend(bytecode_to_repeat);
390
9.34k
                    fork_jump_address = new_bytecode.size();
391
392
9.34k
                    new_bytecode[post_loop_fork_jump_index - 1] = (ByteCodeValueType)(fork_jump_address - post_loop_fork_jump_index);
393
394
9.34k
                    new_bytecode.empend((ByteCodeValueType)OpCodeId::ResetRepeat);
395
9.34k
                    new_bytecode.empend((ByteCodeValueType)max_repetition_mark_id);
396
9.34k
                }
397
9.81k
                new_bytecode[pre_loop_fork_jump_index - 1] = (ByteCodeValueType)(fork_jump_address - pre_loop_fork_jump_index);
398
9.81k
            }
399
10.1k
        } else {
400
            // no maximum value set, repeat finding if possible:
401
            // (REPEAT REGEXP MIN)
402
            // LABEL _START
403
            // CHECKPOINT _C
404
            // REGEXP
405
            // JUMP_NONEMPTY _C _START FORK
406
407
            // Note: This is only safe because REPEAT will leave one iteration outside (see repetition_n)
408
4.16k
            auto checkpoint = s_next_checkpoint_serial_id++;
409
4.16k
            new_bytecode.insert(new_bytecode.size() - bytecode_to_repeat.size(), (ByteCodeValueType)OpCodeId::Checkpoint);
410
4.16k
            new_bytecode.insert(new_bytecode.size() - bytecode_to_repeat.size(), (ByteCodeValueType)checkpoint);
411
412
4.16k
            auto jump_kind = static_cast<ByteCodeValueType>(greedy ? OpCodeId::ForkJump : OpCodeId::ForkStay);
413
4.16k
            new_bytecode.empend((ByteCodeValueType)OpCodeId::JumpNonEmpty);
414
4.16k
            new_bytecode.empend(-bytecode_to_repeat.size() - 4 - 2); // Jump to the last iteration
415
4.16k
            new_bytecode.empend(checkpoint);                         // if _C is not empty.
416
4.16k
            new_bytecode.empend(jump_kind);
417
4.16k
        }
418
419
14.3k
        bytecode_to_repeat = move(new_bytecode);
420
14.3k
    }
_ZN5regex8ByteCode37transform_bytecode_repetition_min_maxITkN2AK8Concepts8IntegralEjEEvRS0_T_NS2_8OptionalIS5_EEmmb
Line
Count
Source
352
399
    {
353
399
        if (!maximum.has_value()) {
354
21
            if (minimum == 0)
355
2
                return transform_bytecode_repetition_any(bytecode_to_repeat, greedy);
356
19
            if (minimum == 1)
357
2
                return transform_bytecode_repetition_min_one(bytecode_to_repeat, greedy);
358
19
        }
359
360
395
        ByteCode new_bytecode;
361
395
        new_bytecode.insert_bytecode_repetition_n(bytecode_to_repeat, minimum, min_repetition_mark_id);
362
363
395
        if (maximum.has_value()) {
364
            // (REPEAT REGEXP MIN)
365
            // LABEL _MAX_LOOP            |
366
            // FORK END                   |
367
            // REGEXP                     |
368
            // REPEAT _MAX_LOOP MAX-MIN   | if max > min
369
            // FORK END                   |
370
            // REGEXP                     |
371
            // LABEL END                  |
372
            // RESET _MAX_LOOP            |
373
378
            auto jump_kind = static_cast<ByteCodeValueType>(greedy ? OpCodeId::ForkStay : OpCodeId::ForkJump);
374
378
            if (maximum.value() > minimum) {
375
377
                new_bytecode.empend(jump_kind);
376
377
                new_bytecode.empend((ByteCodeValueType)0); // Placeholder for the jump target.
377
377
                auto pre_loop_fork_jump_index = new_bytecode.size();
378
377
                new_bytecode.extend(bytecode_to_repeat);
379
377
                auto repetitions = maximum.value() - minimum;
380
377
                auto fork_jump_address = new_bytecode.size();
381
377
                if (repetitions > 1) {
382
345
                    new_bytecode.empend((ByteCodeValueType)OpCodeId::Repeat);
383
345
                    new_bytecode.empend(bytecode_to_repeat.size() + 2);
384
345
                    new_bytecode.empend(static_cast<ByteCodeValueType>(repetitions - 1));
385
345
                    new_bytecode.empend(max_repetition_mark_id);
386
345
                    new_bytecode.empend(jump_kind);
387
345
                    new_bytecode.empend((ByteCodeValueType)0); // Placeholder for the jump target.
388
345
                    auto post_loop_fork_jump_index = new_bytecode.size();
389
345
                    new_bytecode.extend(bytecode_to_repeat);
390
345
                    fork_jump_address = new_bytecode.size();
391
392
345
                    new_bytecode[post_loop_fork_jump_index - 1] = (ByteCodeValueType)(fork_jump_address - post_loop_fork_jump_index);
393
394
345
                    new_bytecode.empend((ByteCodeValueType)OpCodeId::ResetRepeat);
395
345
                    new_bytecode.empend((ByteCodeValueType)max_repetition_mark_id);
396
345
                }
397
377
                new_bytecode[pre_loop_fork_jump_index - 1] = (ByteCodeValueType)(fork_jump_address - pre_loop_fork_jump_index);
398
377
            }
399
378
        } else {
400
            // no maximum value set, repeat finding if possible:
401
            // (REPEAT REGEXP MIN)
402
            // LABEL _START
403
            // CHECKPOINT _C
404
            // REGEXP
405
            // JUMP_NONEMPTY _C _START FORK
406
407
            // Note: This is only safe because REPEAT will leave one iteration outside (see repetition_n)
408
17
            auto checkpoint = s_next_checkpoint_serial_id++;
409
17
            new_bytecode.insert(new_bytecode.size() - bytecode_to_repeat.size(), (ByteCodeValueType)OpCodeId::Checkpoint);
410
17
            new_bytecode.insert(new_bytecode.size() - bytecode_to_repeat.size(), (ByteCodeValueType)checkpoint);
411
412
17
            auto jump_kind = static_cast<ByteCodeValueType>(greedy ? OpCodeId::ForkJump : OpCodeId::ForkStay);
413
17
            new_bytecode.empend((ByteCodeValueType)OpCodeId::JumpNonEmpty);
414
17
            new_bytecode.empend(-bytecode_to_repeat.size() - 4 - 2); // Jump to the last iteration
415
17
            new_bytecode.empend(checkpoint);                         // if _C is not empty.
416
17
            new_bytecode.empend(jump_kind);
417
17
        }
418
419
395
        bytecode_to_repeat = move(new_bytecode);
420
395
    }
_ZN5regex8ByteCode37transform_bytecode_repetition_min_maxITkN2AK8Concepts8IntegralEmEEvRS0_T_NS2_8OptionalIS5_EEmmb
Line
Count
Source
352
28.4k
    {
353
28.4k
        if (!maximum.has_value()) {
354
18.6k
            if (minimum == 0)
355
522
                return transform_bytecode_repetition_any(bytecode_to_repeat, greedy);
356
18.1k
            if (minimum == 1)
357
13.9k
                return transform_bytecode_repetition_min_one(bytecode_to_repeat, greedy);
358
18.1k
        }
359
360
13.9k
        ByteCode new_bytecode;
361
13.9k
        new_bytecode.insert_bytecode_repetition_n(bytecode_to_repeat, minimum, min_repetition_mark_id);
362
363
13.9k
        if (maximum.has_value()) {
364
            // (REPEAT REGEXP MIN)
365
            // LABEL _MAX_LOOP            |
366
            // FORK END                   |
367
            // REGEXP                     |
368
            // REPEAT _MAX_LOOP MAX-MIN   | if max > min
369
            // FORK END                   |
370
            // REGEXP                     |
371
            // LABEL END                  |
372
            // RESET _MAX_LOOP            |
373
9.81k
            auto jump_kind = static_cast<ByteCodeValueType>(greedy ? OpCodeId::ForkStay : OpCodeId::ForkJump);
374
9.81k
            if (maximum.value() > minimum) {
375
9.43k
                new_bytecode.empend(jump_kind);
376
9.43k
                new_bytecode.empend((ByteCodeValueType)0); // Placeholder for the jump target.
377
9.43k
                auto pre_loop_fork_jump_index = new_bytecode.size();
378
9.43k
                new_bytecode.extend(bytecode_to_repeat);
379
9.43k
                auto repetitions = maximum.value() - minimum;
380
9.43k
                auto fork_jump_address = new_bytecode.size();
381
9.43k
                if (repetitions > 1) {
382
8.99k
                    new_bytecode.empend((ByteCodeValueType)OpCodeId::Repeat);
383
8.99k
                    new_bytecode.empend(bytecode_to_repeat.size() + 2);
384
8.99k
                    new_bytecode.empend(static_cast<ByteCodeValueType>(repetitions - 1));
385
8.99k
                    new_bytecode.empend(max_repetition_mark_id);
386
8.99k
                    new_bytecode.empend(jump_kind);
387
8.99k
                    new_bytecode.empend((ByteCodeValueType)0); // Placeholder for the jump target.
388
8.99k
                    auto post_loop_fork_jump_index = new_bytecode.size();
389
8.99k
                    new_bytecode.extend(bytecode_to_repeat);
390
8.99k
                    fork_jump_address = new_bytecode.size();
391
392
8.99k
                    new_bytecode[post_loop_fork_jump_index - 1] = (ByteCodeValueType)(fork_jump_address - post_loop_fork_jump_index);
393
394
8.99k
                    new_bytecode.empend((ByteCodeValueType)OpCodeId::ResetRepeat);
395
8.99k
                    new_bytecode.empend((ByteCodeValueType)max_repetition_mark_id);
396
8.99k
                }
397
9.43k
                new_bytecode[pre_loop_fork_jump_index - 1] = (ByteCodeValueType)(fork_jump_address - pre_loop_fork_jump_index);
398
9.43k
            }
399
9.81k
        } else {
400
            // no maximum value set, repeat finding if possible:
401
            // (REPEAT REGEXP MIN)
402
            // LABEL _START
403
            // CHECKPOINT _C
404
            // REGEXP
405
            // JUMP_NONEMPTY _C _START FORK
406
407
            // Note: This is only safe because REPEAT will leave one iteration outside (see repetition_n)
408
4.14k
            auto checkpoint = s_next_checkpoint_serial_id++;
409
4.14k
            new_bytecode.insert(new_bytecode.size() - bytecode_to_repeat.size(), (ByteCodeValueType)OpCodeId::Checkpoint);
410
4.14k
            new_bytecode.insert(new_bytecode.size() - bytecode_to_repeat.size(), (ByteCodeValueType)checkpoint);
411
412
4.14k
            auto jump_kind = static_cast<ByteCodeValueType>(greedy ? OpCodeId::ForkJump : OpCodeId::ForkStay);
413
4.14k
            new_bytecode.empend((ByteCodeValueType)OpCodeId::JumpNonEmpty);
414
4.14k
            new_bytecode.empend(-bytecode_to_repeat.size() - 4 - 2); // Jump to the last iteration
415
4.14k
            new_bytecode.empend(checkpoint);                         // if _C is not empty.
416
4.14k
            new_bytecode.empend(jump_kind);
417
4.14k
        }
418
419
13.9k
        bytecode_to_repeat = move(new_bytecode);
420
13.9k
    }
421
422
    template<Integral T>
423
    void insert_bytecode_repetition_n(ByteCode& bytecode_to_repeat, T n, size_t repetition_mark_id)
424
17.8k
    {
425
        // LABEL _LOOP
426
        // REGEXP
427
        // REPEAT _LOOP N-1
428
        // REGEXP
429
17.8k
        if (n == 0)
430
1.70k
            return;
431
432
        // Note: this bytecode layout allows callers to repeat the last REGEXP instruction without the
433
        // REPEAT instruction forcing another loop.
434
16.1k
        extend(bytecode_to_repeat);
435
436
16.1k
        if (n > 1) {
437
14.6k
            empend(static_cast<ByteCodeValueType>(OpCodeId::Repeat));
438
14.6k
            empend(bytecode_to_repeat.size());
439
14.6k
            empend(static_cast<ByteCodeValueType>(n - 1));
440
14.6k
            empend(repetition_mark_id);
441
442
14.6k
            extend(bytecode_to_repeat);
443
14.6k
        }
444
16.1k
    }
_ZN5regex8ByteCode28insert_bytecode_repetition_nITkN2AK8Concepts8IntegralEjEEvRS0_T_m
Line
Count
Source
424
3.85k
    {
425
        // LABEL _LOOP
426
        // REGEXP
427
        // REPEAT _LOOP N-1
428
        // REGEXP
429
3.85k
        if (n == 0)
430
503
            return;
431
432
        // Note: this bytecode layout allows callers to repeat the last REGEXP instruction without the
433
        // REPEAT instruction forcing another loop.
434
3.34k
        extend(bytecode_to_repeat);
435
436
3.34k
        if (n > 1) {
437
3.08k
            empend(static_cast<ByteCodeValueType>(OpCodeId::Repeat));
438
3.08k
            empend(bytecode_to_repeat.size());
439
3.08k
            empend(static_cast<ByteCodeValueType>(n - 1));
440
3.08k
            empend(repetition_mark_id);
441
442
3.08k
            extend(bytecode_to_repeat);
443
3.08k
        }
444
3.34k
    }
_ZN5regex8ByteCode28insert_bytecode_repetition_nITkN2AK8Concepts8IntegralEmEEvRS0_T_m
Line
Count
Source
424
13.9k
    {
425
        // LABEL _LOOP
426
        // REGEXP
427
        // REPEAT _LOOP N-1
428
        // REGEXP
429
13.9k
        if (n == 0)
430
1.20k
            return;
431
432
        // Note: this bytecode layout allows callers to repeat the last REGEXP instruction without the
433
        // REPEAT instruction forcing another loop.
434
12.7k
        extend(bytecode_to_repeat);
435
436
12.7k
        if (n > 1) {
437
11.5k
            empend(static_cast<ByteCodeValueType>(OpCodeId::Repeat));
438
11.5k
            empend(bytecode_to_repeat.size());
439
11.5k
            empend(static_cast<ByteCodeValueType>(n - 1));
440
11.5k
            empend(repetition_mark_id);
441
442
11.5k
            extend(bytecode_to_repeat);
443
11.5k
        }
444
12.7k
    }
445
446
    static void transform_bytecode_repetition_min_one(ByteCode& bytecode_to_repeat, bool greedy)
447
647k
    {
448
        // LABEL _START = -bytecode_to_repeat.size()
449
        // CHECKPOINT _C
450
        // REGEXP
451
        // JUMP_NONEMPTY _C _START FORKSTAY (FORKJUMP -> Greedy)
452
453
647k
        auto checkpoint = s_next_checkpoint_serial_id++;
454
647k
        bytecode_to_repeat.prepend((ByteCodeValueType)checkpoint);
455
647k
        bytecode_to_repeat.prepend((ByteCodeValueType)OpCodeId::Checkpoint);
456
457
647k
        bytecode_to_repeat.empend((ByteCodeValueType)OpCodeId::JumpNonEmpty);
458
647k
        bytecode_to_repeat.empend(-bytecode_to_repeat.size() - 3); // Jump to the _START label...
459
647k
        bytecode_to_repeat.empend(checkpoint);                     // ...if _C is not empty
460
461
647k
        if (greedy)
462
647k
            bytecode_to_repeat.empend(static_cast<ByteCodeValueType>(OpCodeId::ForkJump));
463
8
        else
464
8
            bytecode_to_repeat.empend(static_cast<ByteCodeValueType>(OpCodeId::ForkStay));
465
647k
    }
466
467
    static void transform_bytecode_repetition_any(ByteCode& bytecode_to_repeat, bool greedy)
468
915k
    {
469
        // LABEL _START
470
        // FORKJUMP _END  (FORKSTAY -> Greedy)
471
        // CHECKPOINT _C
472
        // REGEXP
473
        // JUMP_NONEMPTY _C _START JUMP
474
        // LABEL _END
475
476
        // LABEL _START = m_bytes.size();
477
915k
        ByteCode bytecode;
478
479
915k
        if (greedy)
480
915k
            bytecode.empend(static_cast<ByteCodeValueType>(OpCodeId::ForkStay));
481
10
        else
482
10
            bytecode.empend(static_cast<ByteCodeValueType>(OpCodeId::ForkJump));
483
484
915k
        bytecode.empend(bytecode_to_repeat.size() + 2 + 4); // Jump to the _END label
485
486
915k
        auto checkpoint = s_next_checkpoint_serial_id++;
487
915k
        bytecode.empend(static_cast<ByteCodeValueType>(OpCodeId::Checkpoint));
488
915k
        bytecode.empend(static_cast<ByteCodeValueType>(checkpoint));
489
490
915k
        bytecode.extend(bytecode_to_repeat);
491
492
915k
        bytecode.empend(static_cast<ByteCodeValueType>(OpCodeId::JumpNonEmpty));
493
915k
        bytecode.empend(-bytecode.size() - 3); // Jump(...) to the _START label...
494
915k
        bytecode.empend(checkpoint);           // ...only if _C passes.
495
915k
        bytecode.empend((ByteCodeValueType)OpCodeId::Jump);
496
        // LABEL _END = bytecode.size()
497
498
915k
        bytecode_to_repeat = move(bytecode);
499
915k
    }
500
501
    static void transform_bytecode_repetition_zero_or_one(ByteCode& bytecode_to_repeat, bool greedy)
502
2.06M
    {
503
        // FORKJUMP _END (FORKSTAY -> Greedy)
504
        // REGEXP
505
        // LABEL _END
506
2.06M
        ByteCode bytecode;
507
508
2.06M
        if (greedy)
509
1.83M
            bytecode.empend(static_cast<ByteCodeValueType>(OpCodeId::ForkStay));
510
230k
        else
511
230k
            bytecode.empend(static_cast<ByteCodeValueType>(OpCodeId::ForkJump));
512
513
2.06M
        bytecode.empend(bytecode_to_repeat.size()); // Jump to the _END label
514
515
2.06M
        bytecode.extend(move(bytecode_to_repeat));
516
        // LABEL _END = bytecode.size()
517
518
2.06M
        bytecode_to_repeat = move(bytecode);
519
2.06M
    }
520
521
    OpCode& get_opcode(MatchState& state) const;
522
523
9.82k
    static void reset_checkpoint_serial_id() { s_next_checkpoint_serial_id = 0; }
524
525
private:
526
    void insert_string(StringView view)
527
1.65M
    {
528
1.65M
        empend((ByteCodeValueType)view.length());
529
95.5M
        for (size_t i = 0; i < view.length(); ++i)
530
93.9M
            empend((ByteCodeValueType)view[i]);
531
1.65M
    }
532
533
    void ensure_opcodes_initialized();
534
    ALWAYS_INLINE OpCode& get_opcode_by_id(OpCodeId id) const;
535
    static OwnPtr<OpCode> s_opcodes[(size_t)OpCodeId::Last + 1];
536
    static bool s_opcodes_initialized;
537
    static size_t s_next_checkpoint_serial_id;
538
};
539
540
#define ENUMERATE_EXECUTION_RESULTS                          \
541
0
    __ENUMERATE_EXECUTION_RESULT(Continue)                   \
542
0
    __ENUMERATE_EXECUTION_RESULT(Fork_PrioHigh)              \
543
0
    __ENUMERATE_EXECUTION_RESULT(Fork_PrioLow)               \
544
0
    __ENUMERATE_EXECUTION_RESULT(Failed)                     \
545
0
    __ENUMERATE_EXECUTION_RESULT(Failed_ExecuteLowPrioForks) \
546
0
    __ENUMERATE_EXECUTION_RESULT(Succeeded)
547
548
enum class ExecutionResult : u8 {
549
#define __ENUMERATE_EXECUTION_RESULT(x) x,
550
    ENUMERATE_EXECUTION_RESULTS
551
#undef __ENUMERATE_EXECUTION_RESULT
552
};
553
554
StringView execution_result_name(ExecutionResult result);
555
StringView opcode_id_name(OpCodeId opcode_id);
556
StringView boundary_check_type_name(BoundaryCheckType);
557
StringView character_compare_type_name(CharacterCompareType result);
558
StringView character_class_name(CharClass ch_class);
559
560
class OpCode {
561
public:
562
132
    OpCode() = default;
563
0
    virtual ~OpCode() = default;
564
565
    virtual OpCodeId opcode_id() const = 0;
566
    virtual size_t size() const = 0;
567
    virtual ExecutionResult execute(MatchInput const& input, MatchState& state) const = 0;
568
569
    ALWAYS_INLINE ByteCodeValueType argument(size_t offset) const
570
1.06G
    {
571
1.06G
        return m_bytecode->at(state().instruction_position + 1 + offset);
572
1.06G
    }
573
574
    ALWAYS_INLINE StringView name() const;
575
    static StringView name(OpCodeId);
576
577
816M
    ALWAYS_INLINE void set_state(MatchState& state) { m_state = &state; }
578
579
816M
    ALWAYS_INLINE void set_bytecode(ByteCode& bytecode) { m_bytecode = &bytecode; }
580
581
    ALWAYS_INLINE MatchState const& state() const
582
1.10G
    {
583
1.10G
        VERIFY(m_state);
584
1.10G
        return *m_state;
585
1.10G
    }
586
587
    ByteString to_byte_string() const
588
0
    {
589
0
        return ByteString::formatted("[{:#02X}] {}", (int)opcode_id(), name(opcode_id()));
590
0
    }
591
592
    virtual ByteString arguments_string() const = 0;
593
594
0
    ALWAYS_INLINE ByteCode const& bytecode() const { return *m_bytecode; }
595
596
protected:
597
    ByteCode* m_bytecode { nullptr };
598
    MatchState* m_state { nullptr };
599
};
600
601
class OpCode_Exit final : public OpCode {
602
public:
603
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
604
11.8k
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::Exit; }
605
714k
    ALWAYS_INLINE size_t size() const override { return 1; }
606
0
    ByteString arguments_string() const override { return ByteString::empty(); }
607
};
608
609
class OpCode_FailForks final : public OpCode {
610
public:
611
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
612
6.27k
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::FailForks; }
613
4.35k
    ALWAYS_INLINE size_t size() const override { return 1; }
614
0
    ByteString arguments_string() const override { return ByteString::empty(); }
615
};
616
617
class OpCode_Save final : public OpCode {
618
public:
619
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
620
2.35k
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::Save; }
621
3.09k
    ALWAYS_INLINE size_t size() const override { return 1; }
622
0
    ByteString arguments_string() const override { return ByteString::empty(); }
623
};
624
625
class OpCode_Restore final : public OpCode {
626
public:
627
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
628
2.35k
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::Restore; }
629
3.09k
    ALWAYS_INLINE size_t size() const override { return 1; }
630
0
    ByteString arguments_string() const override { return ByteString::empty(); }
631
};
632
633
class OpCode_GoBack final : public OpCode {
634
public:
635
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
636
2.08k
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::GoBack; }
637
2.57k
    ALWAYS_INLINE size_t size() const override { return 2; }
638
0
    ALWAYS_INLINE size_t count() const { return argument(0); }
639
0
    ByteString arguments_string() const override { return ByteString::formatted("count={}", count()); }
640
};
641
642
class OpCode_Jump final : public OpCode {
643
public:
644
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
645
14.9M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::Jump; }
646
56.8M
    ALWAYS_INLINE size_t size() const override { return 2; }
647
27.2M
    ALWAYS_INLINE ssize_t offset() const { return argument(0); }
648
    ByteString arguments_string() const override
649
0
    {
650
0
        return ByteString::formatted("offset={} [&{}]", offset(), state().instruction_position + size() + offset());
651
0
    }
652
};
653
654
class OpCode_ForkJump : public OpCode {
655
public:
656
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
657
4.69M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::ForkJump; }
658
6.31M
    ALWAYS_INLINE size_t size() const override { return 2; }
659
2.91M
    ALWAYS_INLINE ssize_t offset() const { return argument(0); }
660
    ByteString arguments_string() const override
661
0
    {
662
0
        return ByteString::formatted("offset={} [&{}], sp: {}", offset(), state().instruction_position + size() + offset(), state().string_position);
663
0
    }
664
};
665
666
class OpCode_ForkReplaceJump final : public OpCode_ForkJump {
667
public:
668
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
669
0
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::ForkReplaceJump; }
670
};
671
672
class OpCode_ForkStay : public OpCode {
673
public:
674
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
675
97.6M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::ForkStay; }
676
299M
    ALWAYS_INLINE size_t size() const override { return 2; }
677
108M
    ALWAYS_INLINE ssize_t offset() const { return argument(0); }
678
    ByteString arguments_string() const override
679
0
    {
680
0
        return ByteString::formatted("offset={} [&{}], sp: {}", offset(), state().instruction_position + size() + offset(), state().string_position);
681
0
    }
682
};
683
684
class OpCode_ForkReplaceStay final : public OpCode_ForkStay {
685
public:
686
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
687
0
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::ForkReplaceStay; }
688
};
689
690
class OpCode_CheckBegin final : public OpCode {
691
public:
692
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
693
71.4k
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::CheckBegin; }
694
7.14M
    ALWAYS_INLINE size_t size() const override { return 1; }
695
0
    ByteString arguments_string() const override { return ByteString::empty(); }
696
};
697
698
class OpCode_CheckEnd final : public OpCode {
699
public:
700
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
701
66.7k
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::CheckEnd; }
702
4.99M
    ALWAYS_INLINE size_t size() const override { return 1; }
703
0
    ByteString arguments_string() const override { return ByteString::empty(); }
704
};
705
706
class OpCode_CheckBoundary final : public OpCode {
707
public:
708
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
709
254k
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::CheckBoundary; }
710
256k
    ALWAYS_INLINE size_t size() const override { return 2; }
711
0
    ALWAYS_INLINE size_t arguments_count() const { return 1; }
712
0
    ALWAYS_INLINE BoundaryCheckType type() const { return static_cast<BoundaryCheckType>(argument(0)); }
713
0
    ByteString arguments_string() const override { return ByteString::formatted("kind={} ({})", (long unsigned int)argument(0), boundary_check_type_name(type())); }
714
};
715
716
class OpCode_ClearCaptureGroup final : public OpCode {
717
public:
718
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
719
33.9M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::ClearCaptureGroup; }
720
37.9M
    ALWAYS_INLINE size_t size() const override { return 2; }
721
0
    ALWAYS_INLINE size_t id() const { return argument(0); }
722
0
    ByteString arguments_string() const override { return ByteString::formatted("id={}", id()); }
723
};
724
725
class OpCode_SaveLeftCaptureGroup final : public OpCode {
726
public:
727
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
728
18.6M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::SaveLeftCaptureGroup; }
729
39.2M
    ALWAYS_INLINE size_t size() const override { return 2; }
730
53.9M
    ALWAYS_INLINE size_t id() const { return argument(0); }
731
0
    ByteString arguments_string() const override { return ByteString::formatted("id={}", id()); }
732
};
733
734
class OpCode_SaveRightCaptureGroup final : public OpCode {
735
public:
736
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
737
18.2M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::SaveRightCaptureGroup; }
738
30.9M
    ALWAYS_INLINE size_t size() const override { return 2; }
739
12.6M
    ALWAYS_INLINE size_t id() const { return argument(0); }
740
0
    ByteString arguments_string() const override { return ByteString::formatted("id={}", id()); }
741
};
742
743
class OpCode_SaveRightNamedCaptureGroup final : public OpCode {
744
public:
745
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
746
58
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::SaveRightNamedCaptureGroup; }
747
131
    ALWAYS_INLINE size_t size() const override { return 4; }
748
0
    ALWAYS_INLINE StringView name() const { return { reinterpret_cast<char*>(argument(0)), length() }; }
749
0
    ALWAYS_INLINE size_t length() const { return argument(1); }
750
0
    ALWAYS_INLINE size_t id() const { return argument(2); }
751
    ByteString arguments_string() const override
752
0
    {
753
0
        return ByteString::formatted("name={}, length={}", name(), length());
754
0
    }
755
};
756
757
class OpCode_Compare final : public OpCode {
758
public:
759
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
760
218M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::Compare; }
761
322M
    ALWAYS_INLINE size_t size() const override { return arguments_size() + 3; }
762
228M
    ALWAYS_INLINE size_t arguments_count() const { return argument(0); }
763
322M
    ALWAYS_INLINE size_t arguments_size() const { return argument(1); }
764
    ByteString arguments_string() const override;
765
    Vector<ByteString> variable_arguments_to_byte_string(Optional<MatchInput const&> input = {}) const;
766
    Vector<CompareTypeAndValuePair> flat_compares() const;
767
    static bool matches_character_class(CharClass, u32, bool insensitive);
768
769
private:
770
    ALWAYS_INLINE static void compare_char(MatchInput const& input, MatchState& state, u32 ch1, bool inverse, bool& inverse_matched);
771
    ALWAYS_INLINE static bool compare_string(MatchInput const& input, MatchState& state, RegexStringView str, bool& had_zero_length_match);
772
    ALWAYS_INLINE static void compare_character_class(MatchInput const& input, MatchState& state, CharClass character_class, u32 ch, bool inverse, bool& inverse_matched);
773
    ALWAYS_INLINE static void compare_character_range(MatchInput const& input, MatchState& state, u32 from, u32 to, u32 ch, bool inverse, bool& inverse_matched);
774
    ALWAYS_INLINE static void compare_property(MatchInput const& input, MatchState& state, Unicode::Property property, bool inverse, bool& inverse_matched);
775
    ALWAYS_INLINE static void compare_general_category(MatchInput const& input, MatchState& state, Unicode::GeneralCategory general_category, bool inverse, bool& inverse_matched);
776
    ALWAYS_INLINE static void compare_script(MatchInput const& input, MatchState& state, Unicode::Script script, bool inverse, bool& inverse_matched);
777
    ALWAYS_INLINE static void compare_script_extension(MatchInput const& input, MatchState& state, Unicode::Script script, bool inverse, bool& inverse_matched);
778
};
779
780
class OpCode_Repeat : public OpCode {
781
public:
782
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
783
22.1M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::Repeat; }
784
25.7M
    ALWAYS_INLINE size_t size() const override { return 4; }
785
11.4M
    ALWAYS_INLINE size_t offset() const { return argument(0); }
786
5.48M
    ALWAYS_INLINE u64 count() const { return argument(1); }
787
8.18M
    ALWAYS_INLINE size_t id() const { return argument(2); }
788
    ByteString arguments_string() const override
789
0
    {
790
0
        auto reps = id() < state().repetition_marks.size() ? state().repetition_marks.at(id()) : 0;
791
0
        return ByteString::formatted("offset={} [&{}] count={} id={} rep={}, sp: {}",
792
0
            static_cast<ssize_t>(offset()),
793
0
            state().instruction_position - offset(),
794
0
            count() + 1,
795
0
            id(),
796
0
            reps + 1,
797
0
            state().string_position);
798
0
    }
799
};
800
801
class OpCode_ResetRepeat : public OpCode {
802
public:
803
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
804
5.75M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::ResetRepeat; }
805
12.8M
    ALWAYS_INLINE size_t size() const override { return 2; }
806
20.8M
    ALWAYS_INLINE size_t id() const { return argument(0); }
807
    ByteString arguments_string() const override
808
0
    {
809
0
        auto reps = id() < state().repetition_marks.size() ? state().repetition_marks.at(id()) : 0;
810
0
        return ByteString::formatted("id={} rep={}", id(), reps + 1);
811
0
    }
812
};
813
814
class OpCode_Checkpoint final : public OpCode {
815
public:
816
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
817
42.0M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::Checkpoint; }
818
101M
    ALWAYS_INLINE size_t size() const override { return 2; }
819
57.3M
    ALWAYS_INLINE size_t id() const { return argument(0); }
820
0
    ByteString arguments_string() const override { return ByteString::formatted("id={}", id()); }
821
};
822
823
class OpCode_JumpNonEmpty final : public OpCode {
824
public:
825
    ExecutionResult execute(MatchInput const& input, MatchState& state) const override;
826
56.7M
    ALWAYS_INLINE OpCodeId opcode_id() const override { return OpCodeId::JumpNonEmpty; }
827
181M
    ALWAYS_INLINE size_t size() const override { return 4; }
828
92.8M
    ALWAYS_INLINE ssize_t offset() const { return argument(0); }
829
46.3M
    ALWAYS_INLINE ssize_t checkpoint() const { return argument(1); }
830
68.5M
    ALWAYS_INLINE OpCodeId form() const { return (OpCodeId)argument(2); }
831
    ByteString arguments_string() const override
832
0
    {
833
0
        return ByteString::formatted("{} offset={} [&{}], cp={}",
834
0
            opcode_id_name(form()),
835
0
            offset(), state().instruction_position + size() + offset(),
836
0
            checkpoint());
837
0
    }
838
};
839
840
ALWAYS_INLINE OpCode& ByteCode::get_opcode(regex::MatchState& state) const
841
816M
{
842
816M
    OpCodeId opcode_id;
843
816M
    if (auto opcode_ptr = static_cast<DisjointChunks<ByteCodeValueType> const&>(*this).find(state.instruction_position))
844
815M
        opcode_id = (OpCodeId)*opcode_ptr;
845
720k
    else
846
720k
        opcode_id = OpCodeId::Exit;
847
848
816M
    auto& opcode = get_opcode_by_id(opcode_id);
849
816M
    opcode.set_state(state);
850
816M
    return opcode;
851
816M
}
852
853
ALWAYS_INLINE OpCode& ByteCode::get_opcode_by_id(OpCodeId id) const
854
816M
{
855
816M
    VERIFY(id >= OpCodeId::First && id <= OpCodeId::Last);
856
857
816M
    auto& opcode = s_opcodes[(u32)id];
858
816M
    opcode->set_bytecode(*const_cast<ByteCode*>(this));
859
816M
    return *opcode;
860
816M
}
861
862
template<typename T>
863
bool is(OpCode const&);
864
865
template<typename T>
866
ALWAYS_INLINE bool is(OpCode const&)
867
{
868
    return false;
869
}
870
871
template<typename T>
872
ALWAYS_INLINE bool is(OpCode const* opcode)
873
{
874
    return is<T>(*opcode);
875
}
876
877
template<>
878
ALWAYS_INLINE bool is<OpCode_ForkStay>(OpCode const& opcode)
879
0
{
880
0
    return opcode.opcode_id() == OpCodeId::ForkStay;
881
0
}
882
883
template<>
884
ALWAYS_INLINE bool is<OpCode_Exit>(OpCode const& opcode)
885
0
{
886
0
    return opcode.opcode_id() == OpCodeId::Exit;
887
0
}
888
889
template<>
890
ALWAYS_INLINE bool is<OpCode_Compare>(OpCode const& opcode)
891
0
{
892
0
    return opcode.opcode_id() == OpCodeId::Compare;
893
0
}
894
895
template<typename T>
896
ALWAYS_INLINE T const& to(OpCode const& opcode)
897
0
{
898
0
    return verify_cast<T>(opcode);
899
0
}
900
901
template<typename T>
902
ALWAYS_INLINE T* to(OpCode* opcode)
903
{
904
    return verify_cast<T>(opcode);
905
}
906
907
template<typename T>
908
ALWAYS_INLINE T const* to(OpCode const* opcode)
909
{
910
    return verify_cast<T>(opcode);
911
}
912
913
template<typename T>
914
ALWAYS_INLINE T& to(OpCode& opcode)
915
0
{
916
0
    return verify_cast<T>(opcode);
917
0
}
918
919
}