Issue #2083 - Part 3: Fix RegExpShared rooting hazards now it's a GC thing.

Based on Mozilla bug 1345177.
Changes from the original bug's patch:

- The original patch didn't have a dotAll for a call to irregexp::ParsePattern,
  so let's make our dotAll a member of the MutableHandleRegExpShared re.
- Make RegExpShared::initializeNamedCaptures, introduced in Issue #1285, static.
  This resolves a build bustage where static RegExpShared::compile was trying to
  use a member function.
This commit is contained in:
Job Bautista
2023-01-26 15:20:34 +08:00
committed by roytam1
parent d163d08cb2
commit 2ac60a27c7
7 changed files with 90 additions and 74 deletions
+7 -6
View File
@@ -164,10 +164,11 @@ CreateRegExpSearchResult(JSContext* cx, const MatchPairs& matches)
* steps 3, 9-14, except 12.a.i, 12.c.i.1.
*/
static RegExpRunStatus
ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLinearString input,
size_t searchIndex, MatchPairs* matches, size_t* endIndex)
ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, MutableHandleRegExpShared re,
HandleLinearString input, size_t searchIndex, MatchPairs* matches,
size_t* endIndex)
{
RegExpRunStatus status = re.execute(cx, input, searchIndex, matches, endIndex);
RegExpRunStatus status = RegExpShared::execute(cx, re, input, searchIndex, matches, endIndex);
/* Out of spec: Update RegExpStatics. */
if (status == RegExpRunStatus_Success && res) {
@@ -175,7 +176,7 @@ ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLin
if (!res->updateFromMatchPairs(cx, input, *matches))
return RegExpRunStatus_Error;
} else {
res->updateLazily(cx, input, &re, searchIndex);
res->updateLazily(cx, input, re, searchIndex);
}
}
return status;
@@ -193,7 +194,7 @@ js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*>
ScopedMatchPairs matches(&cx->tempLifoAlloc());
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex,
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &shared, input, *lastIndex,
&matches, nullptr);
if (status == RegExpRunStatus_Error)
return false;
@@ -1036,7 +1037,7 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
}
/* Steps 3, 11-14, except 12.a.i, 12.c.i.1. */
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, matches, endIndex);
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &re, input, lastIndex, matches, endIndex);
if (status == RegExpRunStatus_Error)
return RegExpRunStatus_Error;
+1 -1
View File
@@ -1259,7 +1259,7 @@ IsNativeRegExpEnabled(JSContext* cx)
}
RegExpCode
irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
irregexp::CompilePattern(JSContext* cx, HandleRegExpShared shared, RegExpCompileData* data,
HandleLinearString sample, bool is_global, bool ignore_case,
bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
bool unicode)
+1 -1
View File
@@ -103,7 +103,7 @@ struct RegExpCode
};
RegExpCode
CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
CompilePattern(JSContext* cx, HandleRegExpShared shared, RegExpCompileData* data,
HandleLinearString sample, bool is_global, bool ignore_case,
bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
bool unicode);
+2 -1
View File
@@ -448,7 +448,8 @@ CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper,
}
// Get an equivalent RegExpShared associated with the current compartment.
return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), shared);
RootedAtom source(cx, re->getSource());
return cx->compartment()->regExps.get(cx, source, re->getFlags(), shared);
}
bool
+55 -48
View File
@@ -283,7 +283,8 @@ RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp,
MutableHandleRegExpShared shared)
{
MOZ_ASSERT(!regexp->hasShared());
if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), shared))
RootedAtom source(cx, regexp->getSource());
if (!cx->compartment()->regExps.get(cx, source, regexp->getFlags(), shared))
return false;
regexp->setShared(*shared);
@@ -507,14 +508,15 @@ RegExpObject::toString(JSContext* cx) const
}
#ifdef DEBUG
bool
RegExpShared::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input)
/* static */ bool
RegExpShared::dumpBytecode(JSContext* cx, MutableHandleRegExpShared re, bool match_only,
HandleLinearString input)
{
CompilationMode mode = match_only ? MatchOnly : Normal;
if (!compileIfNecessary(cx, input, mode, ForceByteCode))
if (!RegExpShared::compileIfNecessary(cx, re, input, mode, ForceByteCode))
return false;
const uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
const uint8_t* byteCode = re->compilation(mode, input->hasLatin1Chars()).byteCode;
const uint8_t* pc = byteCode;
auto Load32Aligned = [](const uint8_t* pc) -> int32_t {
@@ -897,7 +899,7 @@ RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
if (!getShared(cx, regexp, &shared))
return false;
return shared->dumpBytecode(cx, match_only, input);
return RegExpShared::dumpBytecode(cx, &shared, match_only, input);
}
#endif
@@ -976,21 +978,23 @@ RegExpShared::discardJitCode()
comp.jitCode = nullptr;
}
bool
RegExpShared::compile(JSContext* cx, HandleLinearString input,
/* static */ bool
RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force)
{
TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
AutoTraceLog logCompile(logger, TraceLogger_IrregexpCompile);
RootedAtom pattern(cx, source);
return compile(cx, pattern, input, mode, force);
RootedAtom pattern(cx, re->source);
return compile(cx, re, pattern, input, mode, force);
}
bool
RegExpShared::initializeNamedCaptures(JSContext* cx, irregexp::CharacterVectorVector* names, irregexp::IntegerVector* indices)
/* static */ bool
RegExpShared::initializeNamedCaptures(JSContext* cx, HandleRegExpShared re,
irregexp::CharacterVectorVector* names,
irregexp::IntegerVector* indices)
{
MOZ_ASSERT(!groupsTemplate_);
MOZ_ASSERT(!re->groupsTemplate_);
MOZ_ASSERT(names);
MOZ_ASSERT(indices);
MOZ_ASSERT(names->length() == indices->length());
@@ -1032,17 +1036,17 @@ RegExpShared::initializeNamedCaptures(JSContext* cx, irregexp::CharacterVectorVe
AddTypePropertyId(cx, templateObject, id, TypeSet::Int32Type());
}
groupsTemplate_ = templateObject;
numNamedCaptures_ = numNamedCaptures;
re->groupsTemplate_ = templateObject;
re->numNamedCaptures_ = numNamedCaptures;
return true;
}
bool
RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force)
/* static */ bool
RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleAtom pattern,
HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force)
{
if (!ignoreCase() && !StringHasRegExpMetaChars(pattern))
canStringMatch = true;
if (!re->ignoreCase() && !StringHasRegExpMetaChars(pattern))
re->canStringMatch = true;
CompileOptions options(cx);
TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
@@ -1052,34 +1056,36 @@ RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString inpu
/* Parse the pattern. */
irregexp::RegExpCompileData data;
if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern,
multiline(), mode == MatchOnly, unicode(), ignoreCase(),
global(), sticky(), dotAll(), &data))
re->multiline(), mode == MatchOnly, re->unicode(),
re->ignoreCase(), re->global(), re->sticky(),
re->dotAll(), &data))
{
return false;
}
this->parenCount = data.capture_count;
re->parenCount = data.capture_count;
if (data.capture_name_list) {
// convert LifoAlloc'd named capture info to NativeObject
if (!initializeNamedCaptures(cx, data.capture_name_list, data.capture_index_list)) {
if (!initializeNamedCaptures(cx, re, data.capture_name_list, data.capture_index_list)) {
return false;
}
}
irregexp::RegExpCode code = irregexp::CompilePattern(cx, this, &data, input,
irregexp::RegExpCode code = irregexp::CompilePattern(cx, re, &data, input,
false /* global() */,
ignoreCase(),
re->ignoreCase(),
input->hasLatin1Chars(),
mode == MatchOnly,
force == ForceByteCode,
sticky(), unicode());
re->sticky(),
re->unicode());
if (code.empty())
return false;
MOZ_ASSERT(!code.jitCode || !code.byteCode);
MOZ_ASSERT_IF(force == ForceByteCode, code.byteCode);
RegExpCompilation& compilation = this->compilation(mode, input->hasLatin1Chars());
RegExpCompilation& compilation = re->compilation(mode, input->hasLatin1Chars());
if (code.jitCode)
compilation.jitCode = code.jitCode;
else if (code.byteCode)
@@ -1088,18 +1094,19 @@ RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString inpu
return true;
}
bool
RegExpShared::compileIfNecessary(JSContext* cx, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force)
/* static */ bool
RegExpShared::compileIfNecessary(JSContext* cx, MutableHandleRegExpShared re,
HandleLinearString input, CompilationMode mode,
ForceByteCodeEnum force)
{
if (isCompiled(mode, input->hasLatin1Chars(), force))
if (re->isCompiled(mode, input->hasLatin1Chars(), force))
return true;
return compile(cx, input, mode, force);
return compile(cx, re, input, mode, force);
}
RegExpRunStatus
RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
MatchPairs* matches, size_t* endIndex)
/* static */ RegExpRunStatus
RegExpShared::execute(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
size_t start, MatchPairs* matches, size_t* endIndex)
{
MOZ_ASSERT_IF(matches, !endIndex);
MOZ_ASSERT_IF(!matches, endIndex);
@@ -1108,14 +1115,14 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
CompilationMode mode = matches ? Normal : MatchOnly;
/* Compile the code at point-of-use. */
if (!compileIfNecessary(cx, input, mode, DontForceByteCode))
if (!compileIfNecessary(cx, re, input, mode, DontForceByteCode))
return RegExpRunStatus_Error;
/*
* Ensure sufficient memory for output vector.
* No need to initialize it. The RegExp engine fills them in on a match.
*/
if (matches && !matches->allocOrExpandArray(pairCount())) {
if (matches && !matches->allocOrExpandArray(re->pairCount())) {
ReportOutOfMemory(cx);
return RegExpRunStatus_Error;
}
@@ -1125,14 +1132,14 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
// Reset the Irregexp backtrack stack if it grows during execution.
irregexp::RegExpStackScope stackScope(cx->runtime());
if (canStringMatch) {
MOZ_ASSERT(pairCount() == 1);
size_t sourceLength = source->length();
if (sticky()) {
if (re->canStringMatch) {
MOZ_ASSERT(re->pairCount() == 1);
size_t sourceLength = re->source->length();
if (re->sticky()) {
// First part checks size_t overflow.
if (sourceLength + start < sourceLength || sourceLength + start > length)
return RegExpRunStatus_Success_NotFound;
if (!HasSubstringAt(input, source, start))
if (!HasSubstringAt(input, re->source, start))
return RegExpRunStatus_Success_NotFound;
if (matches) {
@@ -1146,7 +1153,7 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
return RegExpRunStatus_Success;
}
int res = StringFindPattern(input, source, start);
int res = StringFindPattern(input, re->source, start);
if (res == -1)
return RegExpRunStatus_Success_NotFound;
@@ -1162,7 +1169,7 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
}
do {
jit::JitCode* code = compilation(mode, input->hasLatin1Chars()).jitCode;
jit::JitCode* code = re->compilation(mode, input->hasLatin1Chars()).jitCode;
if (!code)
break;
@@ -1201,10 +1208,10 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
} while (false);
// Compile bytecode for the RegExp if necessary.
if (!compileIfNecessary(cx, input, mode, ForceByteCode))
if (!compileIfNecessary(cx, re, input, mode, ForceByteCode))
return RegExpRunStatus_Error;
uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
uint8_t* byteCode = re->compilation(mode, input->hasLatin1Chars()).byteCode;
AutoTraceLog logInterpreter(logger, TraceLogger_IrregexpExecute);
AutoStableStringChars inputChars(cx);
@@ -1353,7 +1360,7 @@ RegExpCompartment::sweep(JSRuntime* rt)
}
bool
RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags,
RegExpCompartment::get(JSContext* cx, HandleAtom source, RegExpFlag flags,
MutableHandleRegExpShared result)
{
DependentAddPtr<Set> p(cx, set_.get(), Key(source, flags));
+20 -15
View File
@@ -42,6 +42,10 @@ class MatchPairs;
class RegExpShared;
class RegExpStatics;
using RootedRegExpShared = JS::Rooted<RegExpShared*>;
using HandleRegExpShared = JS::Handle<RegExpShared*>;
using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
namespace frontend { class TokenStream; }
enum RegExpFlag : uint8_t
@@ -150,13 +154,14 @@ class RegExpShared : public gc::TenuredCell
/* Internal functions. */
RegExpShared(JSAtom* source, RegExpFlag flags);
bool compile(JSContext* cx, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force);
bool compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force);
static bool compile(JSContext* cx, MutableHandleRegExpShared res, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force);
static bool compile(JSContext* cx, MutableHandleRegExpShared res, HandleAtom pattern,
HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force);
bool compileIfNecessary(JSContext* cx, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force);
static bool compileIfNecessary(JSContext* cx, MutableHandleRegExpShared res,
HandleLinearString input, CompilationMode mode,
ForceByteCodeEnum force);
const RegExpCompilation& compilation(CompilationMode mode, bool latin1) const {
return compilationArray[CompilationIndex(mode, latin1)];
@@ -171,8 +176,9 @@ class RegExpShared : public gc::TenuredCell
// Execute this RegExp on input starting from searchIndex, filling in
// matches if specified and otherwise only determining if there is a match.
RegExpRunStatus execute(JSContext* cx, HandleLinearString input, size_t searchIndex,
MatchPairs* matches, size_t* endIndex);
static RegExpRunStatus execute(JSContext* cx, MutableHandleRegExpShared res,
HandleLinearString input, size_t searchIndex,
MatchPairs* matches, size_t* endIndex);
// Register a table with this RegExpShared, and take ownership.
bool addTable(uint8_t* table) {
@@ -190,7 +196,9 @@ class RegExpShared : public gc::TenuredCell
size_t pairCount() const { return getParenCount() + 1; }
// not public due to circular inclusion problems
bool initializeNamedCaptures(JSContext* cx, irregexp::CharacterVectorVector* names, irregexp::IntegerVector* indices);
static bool initializeNamedCaptures(JSContext* cx, HandleRegExpShared re,
irregexp::CharacterVectorVector* names,
irregexp::IntegerVector* indices);
PlainObject* getGroupsTemplate() { return groupsTemplate_; }
uint32_t numNamedCaptures() const { return numNamedCaptures_; }
@@ -245,14 +253,11 @@ class RegExpShared : public gc::TenuredCell
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
#ifdef DEBUG
bool dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input);
static bool dumpBytecode(JSContext* cx, MutableHandleRegExpShared res, bool match_only,
HandleLinearString input);
#endif
};
using RootedRegExpShared = JS::Rooted<RegExpShared*>;
using HandleRegExpShared = JS::Handle<RegExpShared*>;
using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
class RegExpCompartment
{
struct Key {
@@ -323,7 +328,7 @@ class RegExpCompartment
bool empty() { return set_.empty(); }
bool get(JSContext* cx, JSAtom* source, RegExpFlag flags, MutableHandleRegExpShared shared);
bool get(JSContext* cx, HandleAtom source, RegExpFlag flags, MutableHandleRegExpShared shared);
/* Like 'get', but compile 'maybeOpt' (if non-null). */
bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt,
+4 -2
View File
@@ -81,7 +81,8 @@ RegExpStatics::executeLazy(JSContext* cx)
/* Retrieve or create the RegExpShared in this compartment. */
RootedRegExpShared shared(cx);
if (!cx->compartment()->regExps.get(cx, lazySource, lazyFlags, &shared))
RootedAtom source(cx, lazySource);
if (!cx->compartment()->regExps.get(cx, source, lazyFlags, &shared))
return false;
/*
@@ -91,7 +92,8 @@ RegExpStatics::executeLazy(JSContext* cx)
/* Execute the full regular expression. */
RootedLinearString input(cx, matchesInput);
RegExpRunStatus status = shared->execute(cx, input, lazyIndex, &this->matches, nullptr);
RegExpRunStatus status = RegExpShared::execute(cx, &shared, input, lazyIndex, &this->matches,
nullptr);
if (status == RegExpRunStatus_Error)
return false;