[vm] Fix race compiling regexp.

TEST=iso-stress
Change-Id: I013d7d91d37b62ed60d7f067a62c6f419c0d0a7c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/483362
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
This commit is contained in:
Ryan Macnak
2026-02-24 15:23:18 -08:00
committed by Commit Queue
parent 96611671aa
commit d5e2f31993
7 changed files with 49 additions and 66 deletions
+13 -16
View File
@@ -108,36 +108,33 @@ DEFINE_NATIVE_ENTRY(RegExp_getIsCaseSensitive, 0, 1) {
return Bool::Get(!IsIgnoreCase(regexp.flags())).ptr();
}
DEFINE_NATIVE_ENTRY(RegExp_getGroupCount, 0, 1) {
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
ASSERT(!regexp.IsNull());
if (regexp.num_bracket_expressions() != -1) {
return Smi::New(regexp.num_bracket_expressions());
}
static ObjectPtr ThrowUninitialized(const RegExp& regexp) {
const String& pattern = String::Handle(regexp.pattern());
const String& errmsg =
String::Handle(String::New("Regular expression is not initialized yet."));
const String& message = String::Handle(String::Concat(errmsg, pattern));
const Array& args = Array::Handle(Array::New(1));
args.SetAt(0, message);
Exceptions::ThrowByType(Exceptions::kFormat, args);
Exceptions::ThrowByType(Exceptions::kState, args);
return Object::null();
}
DEFINE_NATIVE_ENTRY(RegExp_getGroupCount, 0, 1) {
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
ASSERT(!regexp.IsNull());
if (regexp.num_bracket_expressions() != -1) {
return Smi::New(regexp.num_bracket_expressions());
}
return ThrowUninitialized(regexp);
}
DEFINE_NATIVE_ENTRY(RegExp_getGroupNameMap, 0, 1) {
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
ASSERT(!regexp.IsNull());
if (regexp.num_bracket_expressions() != -1) {
if (regexp.num_bracket_expressions<std::memory_order_acquire>() != -1) {
return regexp.capture_name_map();
}
const String& pattern = String::Handle(regexp.pattern());
const String& errmsg = String::Handle(
String::New("Regular expression is not initialized yet. "));
const String& message = String::Handle(String::Concat(errmsg, pattern));
const Array& args = Array::Handle(Array::New(1));
args.SetAt(0, message);
Exceptions::ThrowByType(Exceptions::kFormat, args);
return Object::null();
return ThrowUninitialized(regexp);
}
static ObjectPtr ExecuteMatch(Thread* thread,
-4
View File
@@ -27552,10 +27552,6 @@ void RegExp::set_bytecode(bool is_one_byte,
}
}
void RegExp::set_num_bracket_expressions(intptr_t value) const {
untag()->num_bracket_expressions_ = value;
}
void RegExp::set_capture_name_map(const Array& array) const {
untag()->set_capture_name_map<std::memory_order_release>(array.ptr());
}
+8 -21
View File
@@ -13040,9 +13040,12 @@ class RegExp : public Instance {
}
StringPtr pattern() const { return untag()->pattern(); }
template <std::memory_order order = std::memory_order_relaxed>
intptr_t num_bracket_expressions() const {
return untag()->num_bracket_expressions_;
return untag()->num_bracket_expressions<order>();
}
ArrayPtr capture_name_map() const {
return untag()->capture_name_map<std::memory_order_acquire>();
}
@@ -13057,23 +13060,6 @@ class RegExp : public Instance {
: untag()->two_byte<std::memory_order_acquire>();
}
}
bool has_bytecode(bool is_one_byte, bool sticky) const {
if (sticky) {
if (is_one_byte) {
return Object::null() !=
untag()->one_byte_sticky<std::memory_order_relaxed>();
} else {
return Object::null() !=
untag()->two_byte_sticky<std::memory_order_relaxed>();
}
} else {
if (is_one_byte) {
return Object::null() != untag()->one_byte<std::memory_order_relaxed>();
} else {
return Object::null() != untag()->two_byte<std::memory_order_relaxed>();
}
}
}
static intptr_t function_offset(intptr_t cid, bool sticky) {
if (sticky) {
@@ -13122,9 +13108,10 @@ class RegExp : public Instance {
bool sticky,
const TypedData& bytecode) const;
void set_num_bracket_expressions(SmiPtr value) const;
void set_num_bracket_expressions(const Smi& value) const;
void set_num_bracket_expressions(intptr_t value) const;
template <std::memory_order order = std::memory_order_relaxed>
void set_num_bracket_expressions(intptr_t value) const {
return untag()->set_num_bracket_expressions<order>(value);
}
void set_capture_name_map(const Array& array) const;
void set_num_registers(bool is_one_byte, intptr_t value) const {
StoreNonPointer<intptr_t, intptr_t, std::memory_order_relaxed>(
+5 -2
View File
@@ -3727,11 +3727,14 @@ class UntaggedRegExp : public UntaggedInstance {
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
std::atomic<intptr_t> num_bracket_expressions_;
template <std::memory_order order = std::memory_order_relaxed>
intptr_t num_bracket_expressions() {
return num_bracket_expressions_.load(std::memory_order_relaxed);
return num_bracket_expressions_.load(order);
}
template <std::memory_order order = std::memory_order_relaxed>
void set_num_bracket_expressions(intptr_t value) {
num_bracket_expressions_.store(value, std::memory_order_relaxed);
num_bracket_expressions_.store(value, order);
}
// The same pattern may use different amount of registers if compiled
+1 -1
View File
@@ -1120,9 +1120,9 @@ int IrregexpInterpreter::Match(Thread* thread,
bool is_sticky) {
// bool is_any_unicode = IsEitherUnicode((regexp_data.flags()));
bool is_one_byte = subject_string.IsOneByteString();
SBXCHECK(regexp_data.has_bytecode(is_one_byte, is_sticky));
const TypedData& code_array =
TypedData::Handle(regexp_data.bytecode(is_one_byte, is_sticky));
SBXCHECK(!code_array.IsNull());
int total_register_count = regexp_data.num_registers(is_one_byte);
// MatchInternal only supports returning a single match per call. In global
// mode, i.e. when output_registers has space for more than one match, we
+15 -13
View File
@@ -180,7 +180,7 @@ bool RegExpImpl::EnsureCompiledIrregexp(Thread* thread,
const String& sample_subject,
bool is_one_byte,
bool sticky) {
if (re_data.has_bytecode(is_one_byte, sticky)) return true;
if (re_data.bytecode(is_one_byte, sticky) != TypedData::null()) return true;
return CompileIrregexpFromSource(thread, re_data, sample_subject, is_one_byte,
sticky, RegExpCompilationTarget::kBytecode);
@@ -277,22 +277,24 @@ bool RegExpImpl::CompileIrregexpFromSource(
return false;
}
if (compile_data.compilation_target == RegExpCompilationTarget::kNative) {
UNREACHABLE();
} else {
DCHECK_EQ(compile_data.compilation_target,
RegExpCompilationTarget::kBytecode);
// Store code generated by compiler in bytecode and trampoline to
// interpreter in code.
re_data.set_bytecode(is_one_byte, sticky,
TypedData::Cast(*compile_data.code));
}
// Set num_bracket_expression after setting capture_name_map.
// RegExp_getGroupNameMap will read num_bracket_expression first and assume
// capture_name_map is available if the count is not -1.
const Array& capture_name_map =
Array::Handle(zone, RegExpStatics::CreateCaptureNameMap(
thread->isolate(), compile_data.named_captures));
re_data.set_capture_name_map(capture_name_map);
re_data.set_num_bracket_expressions<std::memory_order_release>(
compile_data.capture_count);
// Set bytecode after setting num_registers. RegExpStatics::Interpret will
// read bytecode first and assume num_register is available if bytecode is not
// null.
re_data.set_num_registers(is_one_byte, compile_data.register_count);
re_data.set_num_bracket_expressions(compile_data.capture_count);
DCHECK_EQ(compile_data.compilation_target,
RegExpCompilationTarget::kBytecode);
re_data.set_bytecode(is_one_byte, sticky,
TypedData::Cast(*compile_data.code));
return true;
}
@@ -493,7 +495,7 @@ ObjectPtr RegExpStatics::Interpret(Thread* thread,
int start_index,
bool sticky) {
bool is_one_byte = subject.IsOneByteString();
if (!regexp.has_bytecode(is_one_byte, sticky)) {
if (regexp.bytecode(is_one_byte, sticky) == TypedData::null()) {
if (!RegExpImpl::CompileIrregexpFromSource(
thread, regexp, subject, is_one_byte, sticky,
RegExpCompilationTarget::kBytecode)) {
+7 -9
View File
@@ -10,11 +10,12 @@
// the algorithm defined in ECMAScript 2020 21.2.2.8.2 (Runtime
// Semantics: Canonicalize) step 3.
#if 1 /*V8_INTL_SUPPORT*/
#include "vm/regexp/special-case.h"
#include <mutex>
#include "unicode/uniset.h"
namespace dart {
icu::UnicodeSet BuildIgnoreSet() {
@@ -49,9 +50,8 @@ struct IgnoreSetData {
//static
const icu::UnicodeSet& RegExpCaseFolding::IgnoreSet() {
static IgnoreSetData* set = nullptr;
if (set == nullptr) {
set = new IgnoreSetData();
}
static std::once_flag init_once = {};
std::call_once(init_once, [] { set = new IgnoreSetData(); });
return set->set;
}
@@ -80,11 +80,9 @@ struct SpecialAddSetData {
//static
const icu::UnicodeSet& RegExpCaseFolding::SpecialAddSet() {
static SpecialAddSetData* set = nullptr;
if (set == nullptr) {
set = new SpecialAddSetData();
}
static std::once_flag init_once = {};
std::call_once(init_once, [] { set = new SpecialAddSetData(); });
return set->set;
}
} // namespace dart
#endif // V8_INTL_SUPPORT