mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
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:
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user