blob: b0273d8846cb8f6fe21da78da65ea7cdb64953cf [file] [log] [blame]
// 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_