e443b89f23
Includes support for modifier spans and duplicate named capture groups. Drops the flow graph implementation to ease maintenance. TEST=corelib/regexp Bug: https://github.com/dart-lang/sdk/issues/56573 Bug: https://github.com/dart-lang/sdk/issues/61337 Bug: https://github.com/dart-lang/sdk/issues/62349 Bug: https://github.com/dart-lang/sdk/issues/62708 Change-Id: I05640ba945a4fa5476e7ad463738f4f39d842c14 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/480121 Reviewed-by: Lasse Nielsen <lrn@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
215 lines
8.1 KiB
C++
215 lines
8.1 KiB
C++
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_REGEXP_BASE_H_
|
|
#define RUNTIME_VM_REGEXP_BASE_H_
|
|
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
|
|
#include "platform/assert.h"
|
|
#include "platform/globals.h"
|
|
#include "platform/unicode.h"
|
|
|
|
#define DCHECK(x) DEBUG_ASSERT(x)
|
|
#define DCHECK_NULL(x) DEBUG_ASSERT((x) == nullptr)
|
|
#define DCHECK_NOT_NULL(x) DEBUG_ASSERT((x) != nullptr)
|
|
#define DCHECK_EQ(a, b) DEBUG_ASSERT((a) == (b))
|
|
#define DCHECK_NE(a, b) DEBUG_ASSERT((a) != (b))
|
|
#define DCHECK_LT(a, b) DEBUG_ASSERT((a) < (b))
|
|
#define DCHECK_GT(a, b) DEBUG_ASSERT((a) > (b))
|
|
#define DCHECK_LE(a, b) DEBUG_ASSERT((a) <= (b))
|
|
#define DCHECK_GE(a, b) DEBUG_ASSERT((a) >= (b))
|
|
#define DCHECK_IMPLIES(a, b) DEBUG_ASSERT(!(a) || (b))
|
|
|
|
#define CHECK(x) RELEASE_ASSERT(x)
|
|
#define CHECK_EQ(a, b) RELEASE_ASSERT((a) == (b))
|
|
#define CHECK_NE(a, b) RELEASE_ASSERT((a) != (b))
|
|
#define CHECK_LT(a, b) RELEASE_ASSERT((a) < (b))
|
|
#define CHECK_GT(a, b) RELEASE_ASSERT((a) > (b))
|
|
#define CHECK_LE(a, b) RELEASE_ASSERT((a) <= (b))
|
|
#define CHECK_GE(a, b) RELEASE_ASSERT((a) >= (b))
|
|
#define CHECK_IMPLIES(a, b) RELEASE_ASSERT(!(a) || (b))
|
|
|
|
#define SBXCHECK(x) RELEASE_ASSERT(x)
|
|
#define SBXCHECK_LT(a, b) RELEASE_ASSERT((a) < (b))
|
|
#define SBXCHECK_GT(a, b) RELEASE_ASSERT((a) > (b))
|
|
#define SBXCHECK_LE(a, b) RELEASE_ASSERT((a) <= (b))
|
|
#define SBXCHECK_GE(a, b) RELEASE_ASSERT((a) >= (b))
|
|
|
|
#define V8_INLINE DART_FORCE_INLINE
|
|
#define V8_NOINLINE DART_NOINLINE
|
|
#define V8_NOEXCEPT
|
|
#define V8_PRESERVE_MOST
|
|
#define V8_LIKELY LIKELY
|
|
#define V8_UNLIKELY UNLIKELY
|
|
#define V8_ASSUME(x)
|
|
#define V8_NODISCARD [[nodiscard]]
|
|
#define V8_INTL_SUPPORT 1
|
|
#define V8_ALLOW_UNUSED DART_UNUSED
|
|
#define V8_WARN_UNUSED_RESULT DART_WARN_UNUSED_RESULT
|
|
#define COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER 1
|
|
|
|
#ifdef DART_HAS_COMPUTED_GOTO
|
|
#define V8_HAS_COMPUTED_GOTO 1
|
|
#define V8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH 1
|
|
#endif
|
|
|
|
#define CONCAT_(a, ...) a##__VA_ARGS__
|
|
#define CONCAT(a, ...) CONCAT_(a, __VA_ARGS__)
|
|
|
|
// COUNT_MACRO_ARGS(...) returns the number of arguments passed. Currently, up
|
|
// to 8 arguments are supported.
|
|
#define COUNT_MACRO_ARGS(...) \
|
|
EXPAND(COUNT_MACRO_ARGS_IMPL(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0))
|
|
#define COUNT_MACRO_ARGS_IMPL(_8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
|
|
// GET_NTH_ARG(N, ...) returns the Nth argument in the list of arguments
|
|
// following. Currently, up to N=8 is supported.
|
|
#define GET_NTH_ARG(N, ...) CONCAT(GET_NTH_ARG_IMPL_, N)(__VA_ARGS__)
|
|
#define GET_NTH_ARG_IMPL_0(_0, ...) _0
|
|
#define GET_NTH_ARG_IMPL_1(_0, _1, ...) _1
|
|
#define GET_NTH_ARG_IMPL_2(_0, _1, _2, ...) _2
|
|
#define GET_NTH_ARG_IMPL_3(_0, _1, _2, _3, ...) _3
|
|
#define GET_NTH_ARG_IMPL_4(_0, _1, _2, _3, _4, ...) _4
|
|
#define GET_NTH_ARG_IMPL_5(_0, _1, _2, _3, _4, _5, ...) _5
|
|
#define GET_NTH_ARG_IMPL_6(_0, _1, _2, _3, _4, _5, _6, ...) _6
|
|
#define GET_NTH_ARG_IMPL_7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7
|
|
|
|
// Expands to true if __VA_ARGS__ is empty, false otherwise.
|
|
#define IS_VA_EMPTY(...) GET_NTH_ARG(0, __VA_OPT__(false, ) true)
|
|
|
|
// UNPAREN(x) removes a layer of nested parentheses on x, if any. This means
|
|
// that both UNPAREN(x) and UNPAREN((x)) expand to x. This is helpful for macros
|
|
// that want to support multi argument templates with commas, e.g.
|
|
//
|
|
// #define FOO(Type, Name) UNPAREN(Type) Name;
|
|
//
|
|
// will work with both
|
|
//
|
|
// FOO(int, x);
|
|
// FOO((Foo<int, double, float>), x);
|
|
#define UNPAREN(X) CONCAT(DROP_, UNPAREN_ X)
|
|
#define UNPAREN_(...) UNPAREN_ __VA_ARGS__
|
|
#define DROP_UNPAREN_
|
|
|
|
// clang-format off
|
|
#define INT_0_TO_127_LIST(V) \
|
|
V(0) V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) V(9) \
|
|
V(10) V(11) V(12) V(13) V(14) V(15) V(16) V(17) V(18) V(19) \
|
|
V(20) V(21) V(22) V(23) V(24) V(25) V(26) V(27) V(28) V(29) \
|
|
V(30) V(31) V(32) V(33) V(34) V(35) V(36) V(37) V(38) V(39) \
|
|
V(40) V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) V(49) \
|
|
V(50) V(51) V(52) V(53) V(54) V(55) V(56) V(57) V(58) V(59) \
|
|
V(60) V(61) V(62) V(63) V(64) V(65) V(66) V(67) V(68) V(69) \
|
|
V(70) V(71) V(72) V(73) V(74) V(75) V(76) V(77) V(78) V(79) \
|
|
V(80) V(81) V(82) V(83) V(84) V(85) V(86) V(87) V(88) V(89) \
|
|
V(90) V(91) V(92) V(93) V(94) V(95) V(96) V(97) V(98) V(99) \
|
|
V(100) V(101) V(102) V(103) V(104) V(105) V(106) V(107) V(108) V(109) \
|
|
V(110) V(111) V(112) V(113) V(114) V(115) V(116) V(117) V(118) V(119) \
|
|
V(120) V(121) V(122) V(123) V(124) V(125) V(126) V(127)
|
|
// clang-format on
|
|
|
|
namespace base {
|
|
|
|
using uc16 = uint16_t;
|
|
using uc32 = uint32_t;
|
|
constexpr int kUC16Size = sizeof(uc16);
|
|
|
|
// Returns the value (0 .. 15) of a hexadecimal character c.
|
|
// If c is not a legal hexadecimal character, returns a value < 0.
|
|
inline int HexValue(uc32 c) {
|
|
c -= '0';
|
|
if (static_cast<unsigned>(c) <= 9) return c;
|
|
c = (c | 0x20) - ('a' - '0'); // detect 0x11..0x16 and 0x31..0x36.
|
|
if (static_cast<unsigned>(c) <= 5) return c + 10;
|
|
return -1;
|
|
}
|
|
|
|
template <typename D, typename S>
|
|
D saturated_cast(S in) {
|
|
if (in < std::numeric_limits<D>::min()) {
|
|
return std::numeric_limits<D>::min();
|
|
}
|
|
if (in > std::numeric_limits<D>::max()) {
|
|
return std::numeric_limits<D>::max();
|
|
}
|
|
return static_cast<D>(in);
|
|
}
|
|
|
|
// Checks if value is in range [lower_limit, higher_limit] using a single
|
|
// branch.
|
|
template <typename T, typename U>
|
|
requires((std::is_integral_v<T> || std::is_enum_v<T>) &&
|
|
(std::is_integral_v<U> || std::is_enum_v<U>)) &&
|
|
(sizeof(U) <= sizeof(T))
|
|
inline constexpr bool IsInRange(T value, U lower_limit, U higher_limit) {
|
|
ASSERT(lower_limit <= higher_limit);
|
|
using unsigned_T = std::make_unsigned_t<T>;
|
|
// Use static_cast to support enum classes.
|
|
return static_cast<unsigned_T>(static_cast<unsigned_T>(value) -
|
|
static_cast<unsigned_T>(lower_limit)) <=
|
|
static_cast<unsigned_T>(static_cast<unsigned_T>(higher_limit) -
|
|
static_cast<unsigned_T>(lower_limit));
|
|
}
|
|
|
|
}; // namespace base
|
|
|
|
namespace dart {
|
|
|
|
using Address = uintptr_t;
|
|
|
|
constexpr bool FLAG_correctness_fuzzer_suppressions = false;
|
|
constexpr bool FLAG_regexp_possessive_quantifier = false;
|
|
constexpr bool FLAG_js_regexp_modifiers = true;
|
|
constexpr bool FLAG_js_regexp_duplicate_named_groups = true;
|
|
constexpr bool FLAG_trace_regexp_parser = false;
|
|
constexpr bool FLAG_regexp_unroll = false;
|
|
constexpr bool FLAG_regexp_optimization = false;
|
|
constexpr bool FLAG_regexp_quick_check = true;
|
|
constexpr bool FLAG_regexp_tier_up = false;
|
|
constexpr bool FLAG_regexp_peephole_optimization = false;
|
|
|
|
class JSRegExp {
|
|
public:
|
|
static constexpr uint32_t kNoBacktrackLimit = 0;
|
|
static constexpr int RegistersForCaptureCount(int count) {
|
|
return (count + 1) * 2;
|
|
}
|
|
};
|
|
|
|
class DisallowGarbageCollection {};
|
|
|
|
// Compare 8bit/16bit chars to 8bit/16bit chars.
|
|
template <typename lchar, typename rchar>
|
|
inline bool CompareCharsEqualUnsigned(const lchar* lhs,
|
|
const rchar* rhs,
|
|
size_t chars) {
|
|
static_assert(std::is_unsigned_v<lchar>);
|
|
static_assert(std::is_unsigned_v<rchar>);
|
|
if constexpr (sizeof(*lhs) == sizeof(*rhs)) {
|
|
// memcmp compares byte-by-byte, but for equality it doesn't matter whether
|
|
// two-byte char comparison is little- or big-endian.
|
|
return memcmp(lhs, rhs, chars * sizeof(*lhs)) == 0;
|
|
}
|
|
for (const lchar* limit = lhs + chars; lhs < limit; ++lhs, ++rhs) {
|
|
if (*lhs != *rhs) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename lchar, typename rchar>
|
|
inline bool CompareCharsEqual(const lchar* lhs,
|
|
const rchar* rhs,
|
|
size_t chars) {
|
|
using ulchar = std::make_unsigned_t<lchar>;
|
|
using urchar = std::make_unsigned_t<rchar>;
|
|
return CompareCharsEqualUnsigned(reinterpret_cast<const ulchar*>(lhs),
|
|
reinterpret_cast<const urchar*>(rhs), chars);
|
|
}
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_REGEXP_BASE_H_
|