1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-09 09:34:57 +09:00

LibJS: Store RegExp flags as a bitmask

This avoids having to do O(n) contains() in the various flag accessors.

Yields a ~20% speed-up on the following microbenchmark:

    const re = /foo/dgimsvy;
    for (let i = 0; i < 1_000_000; ++i)
        re.flags;
This commit is contained in:
Andreas Kling 2024-10-25 17:29:03 +02:00 committed by Andreas Kling
parent 6b82ab06fd
commit 257ebea364
Notes: github-actions[bot] 2024-10-26 13:43:53 +00:00
5 changed files with 53 additions and 15 deletions

View file

@ -140,14 +140,14 @@
__JS_ENUMERATE(dispose, dispose)
#define JS_ENUMERATE_REGEXP_FLAGS \
__JS_ENUMERATE(hasIndices, has_indices, d) \
__JS_ENUMERATE(global, global, g) \
__JS_ENUMERATE(ignoreCase, ignore_case, i) \
__JS_ENUMERATE(multiline, multiline, m) \
__JS_ENUMERATE(dotAll, dot_all, s) \
__JS_ENUMERATE(unicodeSets, unicode_sets, v) \
__JS_ENUMERATE(unicode, unicode, u) \
__JS_ENUMERATE(sticky, sticky, y)
__JS_ENUMERATE(HasIndices, hasIndices, has_indices, d) \
__JS_ENUMERATE(Global, global, global, g) \
__JS_ENUMERATE(IgnoreCase, ignoreCase, ignore_case, i) \
__JS_ENUMERATE(Multiline, multiline, multiline, m) \
__JS_ENUMERATE(DotAll, dotAll, dot_all, s) \
__JS_ENUMERATE(UnicodeSets, unicodeSets, unicode_sets, v) \
__JS_ENUMERATE(Unicode, unicode, unicode, u) \
__JS_ENUMERATE(Sticky, sticky, sticky, y)
namespace JS {

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -160,10 +161,29 @@ RegExpObject::RegExpObject(Object& prototype)
{
}
static RegExpObject::Flags to_flag_bits(StringView flags)
{
RegExpObject::Flags flag_bits = static_cast<RegExpObject::Flags>(0);
for (auto ch : flags) {
switch (ch) {
#define __JS_ENUMERATE(FlagName, flagName, flag_name, flag_char) \
case #flag_char[0]: \
flag_bits |= RegExpObject::Flags::FlagName; \
break;
JS_ENUMERATE_REGEXP_FLAGS
#undef __JS_ENUMERATE
default:
break;
}
}
return flag_bits;
}
RegExpObject::RegExpObject(Regex<ECMA262> regex, ByteString pattern, ByteString flags, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_pattern(move(pattern))
, m_flags(move(flags))
, m_flag_bits(to_flag_bits(m_flags))
, m_regex(move(regex))
{
VERIFY(m_regex->parser_result.error == regex::Error::NoError);
@ -228,6 +248,7 @@ ThrowCompletionOr<NonnullGCPtr<RegExpObject>> RegExpObject::regexp_initialize(VM
m_pattern = move(pattern);
// 17. Set obj.[[OriginalFlags]] to F.
m_flag_bits = to_flag_bits(flags);
m_flags = move(flags);
// 18. Let capturingGroupsCount be CountLeftCapturingParensWithin(parseResult).

View file

@ -1,11 +1,13 @@
/*
* Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/EnumBits.h>
#include <AK/Optional.h>
#include <AK/Result.h>
#include <LibJS/Runtime/Object.h>
@ -37,6 +39,17 @@ public:
| regex::ECMAScriptFlags::BrowserExtended
};
enum class Flags {
HasIndices = 1 << 0,
Global = 1 << 1,
IgnoreCase = 1 << 2,
Multiline = 1 << 3,
DotAll = 1 << 4,
UnicodeSets = 1 << 5,
Unicode = 1 << 6,
Sticky = 1 << 7,
};
static NonnullGCPtr<RegExpObject> create(Realm&);
static NonnullGCPtr<RegExpObject> create(Realm&, Regex<ECMA262> regex, ByteString pattern, ByteString flags);
@ -48,6 +61,7 @@ public:
ByteString const& pattern() const { return m_pattern; }
ByteString const& flags() const { return m_flags; }
Flags flag_bits() const { return m_flag_bits; }
Regex<ECMA262> const& regex() { return *m_regex; }
Regex<ECMA262> const& regex() const { return *m_regex; }
Realm& realm() { return *m_realm; }
@ -64,10 +78,13 @@ private:
ByteString m_pattern;
ByteString m_flags;
Flags m_flag_bits { 0 };
bool m_legacy_features_enabled { false }; // [[LegacyFeaturesEnabled]]
// Note: This is initialized in RegExpAlloc, but will be non-null afterwards
GCPtr<Realm> m_realm; // [[Realm]]
Optional<Regex<ECMA262>> m_regex;
};
AK_ENUM_BITWISE_OPERATORS(RegExpObject::Flags);
}

View file

@ -50,7 +50,7 @@ void RegExpPrototype::initialize(Realm& realm)
define_native_accessor(realm, vm.names.flags, flags, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.source, source, {}, Attribute::Configurable);
#define __JS_ENUMERATE(flagName, flag_name, flag_char) \
#define __JS_ENUMERATE(FlagName, flagName, flag_name, flag_char) \
define_native_accessor(realm, vm.names.flagName, flag_name, {}, Attribute::Configurable);
JS_ENUMERATE_REGEXP_FLAGS
#undef __JS_ENUMERATE
@ -441,7 +441,7 @@ size_t advance_string_index(Utf16View const& string, size_t index, bool unicode)
// 22.2.6.15 get RegExp.prototype.sticky, https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky
// 22.2.6.18 get RegExp.prototype.unicode, https://tc39.es/ecma262/#sec-get-regexp.prototype.unicode
// 22.2.6.19 get RegExp.prototype.unicodeSets, https://tc39.es/ecma262/#sec-get-regexp.prototype.unicodesets
#define __JS_ENUMERATE(flagName, flag_name, flag_char) \
#define __JS_ENUMERATE(FlagName, flagName, flag_name, flag_char) \
JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::flag_name) \
{ \
auto& realm = *vm.current_realm(); \
@ -456,10 +456,10 @@ size_t advance_string_index(Utf16View const& string, size_t index, bool unicode)
return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "RegExp"); \
} \
/* 3. Let flags be R.[[OriginalFlags]]. */ \
auto const& flags = static_cast<RegExpObject&>(*regexp_object).flags(); \
auto flags = static_cast<RegExpObject&>(*regexp_object).flag_bits(); \
/* 4. If flags contains codeUnit, return true. */ \
/* 5. Return false. */ \
return Value(flags.contains(#flag_char##sv)); \
return Value(has_flag(flags, RegExpObject::Flags::FlagName)); \
}
JS_ENUMERATE_REGEXP_FLAGS
#undef __JS_ENUMERATE
@ -505,7 +505,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::flags)
// 17. If unicodeSets is true, append the code unit 0x0076 (LATIN SMALL LETTER V) as the last code unit of result.
// 18. Let sticky be ToBoolean(? Get(R, "sticky")).
// 19. If sticky is true, append the code unit 0x0079 (LATIN SMALL LETTER Y) as the last code unit of result.
#define __JS_ENUMERATE(flagName, flag_name, flag_char) \
#define __JS_ENUMERATE(FlagName, flagName, flag_name, flag_char) \
auto flag_##flag_name = TRY(regexp_object->get(vm.names.flagName)); \
if (flag_##flag_name.to_boolean()) \
builder.append(#flag_char##sv);

View file

@ -38,7 +38,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(compile);
#define __JS_ENUMERATE(_, flag_name, ...) \
#define __JS_ENUMERATE(FlagName, flagName, flag_name, ...) \
JS_DECLARE_NATIVE_FUNCTION(flag_name);
JS_ENUMERATE_REGEXP_FLAGS
#undef __JS_ENUMERATE