Bug 820124, Part 1/2 - Use RegExpShared for lazy RegExpStatics. r=dvander

This commit is contained in:
Sean Stangl 2013-01-11 17:19:31 -08:00
parent f2f6f1e8d7
commit 51920c3c44
9 changed files with 120 additions and 65 deletions

View File

@ -114,7 +114,7 @@ js::CreateRegExpMatchResult(JSContext *cx, HandleString string, MatchPairs &matc
}
RegExpRunStatus
ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re, RegExpObject &regexp,
ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re,
JSLinearString *input, StableCharPtr chars, size_t length,
size_t *lastIndex, MatchConduit &matches)
{
@ -126,7 +126,7 @@ ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re, RegExpObj
/* Only one MatchPair slot provided: execute short-circuiting regexp. */
status = re.executeMatchOnly(cx, chars, length, lastIndex, *matches.u.pair);
if (status == RegExpRunStatus_Success && res)
res->updateLazily(cx, input, &regexp, lastIndex_orig);
res->updateLazily(cx, input, &re, lastIndex_orig);
} else {
/* Vector of MatchPairs provided: execute full regexp. */
status = re.execute(cx, chars, length, lastIndex, *matches.u.pairs);
@ -151,7 +151,7 @@ js::ExecuteRegExpLegacy(JSContext *cx, RegExpStatics *res, RegExpObject &reobj,
MatchConduit conduit(&matches);
RegExpRunStatus status =
ExecuteRegExpImpl(cx, res, *shared, reobj, input, chars, length, lastIndex, conduit);
ExecuteRegExpImpl(cx, res, *shared, input, chars, length, lastIndex, conduit);
if (status == RegExpRunStatus_Error)
return false;
@ -580,7 +580,7 @@ js::ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string, Match
/* Steps 8-21. */
size_t lastIndexInt(i);
RegExpRunStatus status =
ExecuteRegExpImpl(cx, res, *re, *reobj, stableInput, chars, length, &lastIndexInt, matches);
ExecuteRegExpImpl(cx, res, *re, stableInput, chars, length, &lastIndexInt, matches);
if (status == RegExpRunStatus_Error)
return RegExpRunStatus_Error;

View File

@ -339,7 +339,6 @@ DeclMarkerImpl(Object, DebugScopeObject)
DeclMarkerImpl(Object, GlobalObject)
DeclMarkerImpl(Object, JSObject)
DeclMarkerImpl(Object, JSFunction)
DeclMarkerImpl(Object, RegExpObject)
DeclMarkerImpl(Object, ScopeObject)
DeclMarkerImpl(Script, JSScript)
DeclMarkerImpl(Shape, Shape)

View File

@ -97,7 +97,6 @@ DeclMarker(Object, DebugScopeObject)
DeclMarker(Object, GlobalObject)
DeclMarker(Object, JSObject)
DeclMarker(Object, JSFunction)
DeclMarker(Object, RegExpObject)
DeclMarker(Object, ScopeObject)
DeclMarker(Script, JSScript)
DeclMarker(Shape, Shape)

View File

@ -562,10 +562,8 @@ AutoGCRooter::trace(JSTracer *trc)
}
case REGEXPSTATICS: {
/*
RegExpStatics::AutoRooter *rooter = static_cast<RegExpStatics::AutoRooter *>(this);
rooter->trace(trc);
*/
return;
}
@ -646,15 +644,14 @@ Shape::Range::AutoRooter::trace(JSTracer *trc)
void
RegExpStatics::AutoRooter::trace(JSTracer *trc)
{
if (statics->regexp)
MarkObjectRoot(trc, reinterpret_cast<JSObject**>(&statics->regexp),
"RegExpStatics::AutoRooter regexp");
if (statics->matchesInput)
MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics->matchesInput),
"RegExpStatics::AutoRooter matchesInput");
if (statics->pendingInput)
MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics->pendingInput),
"RegExpStatics::AutoRooter pendingInput");
if (statics->regexp.initialized())
statics->regexp->trace(trc);
}
void

View File

@ -405,12 +405,6 @@ RegExpShared::~RegExpShared()
js_delete<BytecodePattern>(bytecode);
}
void
RegExpShared::trace(JSTracer *trc)
{
MarkStringUnbarriered(trc, &source, "regexpshared source");
}
void
RegExpShared::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error)
{
@ -651,6 +645,18 @@ RegExpCompartment::RegExpCompartment(JSRuntime *rt)
RegExpCompartment::~RegExpCompartment()
{
JS_ASSERT(map_.empty());
/*
* RegExpStatics may have prevented a single RegExpShared from
* being collected during RegExpCompartment::sweep().
*/
if (!inUse_.empty()) {
PendingSet::Enum e(inUse_);
RegExpShared *shared = e.front();
JS_ASSERT(shared->activeUseCount == 0);
js_delete(shared);
e.removeFront();
}
JS_ASSERT(inUse_.empty());
}

View File

@ -14,6 +14,8 @@
#include "jscntxt.h"
#include "jsobj.h"
#include "gc/Barrier.h"
#include "gc/Marking.h"
#include "js/TemplateLib.h"
#include "vm/MatchPairs.h"
@ -120,11 +122,12 @@ class RegExpShared
#endif
/*
* Source to the RegExp. The RegExpShared must either be protected by a
* RegExpGuard, which handles rooting for stacky RegExpShareds,
* or trace() must be explicitly called during marking.
* Source to the RegExp, for lazy compilation.
* The source must be rooted while activeUseCount is non-zero
* via RegExpGuard, RegExpHeapGuard, or explicit calls to trace().
*/
JSAtom * source;
RegExpFlag flags;
unsigned parenCount;
@ -149,7 +152,10 @@ class RegExpShared
RegExpShared(JSRuntime *rt, JSAtom *source, RegExpFlag flags);
~RegExpShared();
void trace(JSTracer *trc);
/* Explicit trace function for use by the RegExpStatics and JITs. */
void trace(JSTracer *trc) {
MarkStringUnbarriered(trc, &source, "regexpshared source");
}
/* Static functions to expose some Yarr logic. */
static inline bool isJITRuntimeEnabled(JSContext *cx);
@ -222,17 +228,58 @@ class RegExpGuard
re_->incRef();
}
~RegExpGuard() {
if (re_)
re_->decRef();
}
~RegExpGuard() { release(); }
public:
void init(RegExpShared &re) {
JS_ASSERT(!re_);
JS_ASSERT(!initialized());
re_ = &re;
re_->incRef();
source_ = re.source;
source_ = re_->source;
}
void release() {
if (re_) {
re_->decRef();
re_ = NULL;
source_ = NULL;
}
}
bool initialized() const { return !!re_; }
RegExpShared *re() const { JS_ASSERT(initialized()); return re_; }
RegExpShared *operator->() { return re(); }
RegExpShared &operator*() { return *re(); }
};
/* Equivalent of RegExpGuard, heap-allocated, with explicit tracing. */
class RegExpHeapGuard
{
RegExpShared *re_;
RegExpHeapGuard(const RegExpGuard &) MOZ_DELETE;
void operator=(const RegExpHeapGuard &) MOZ_DELETE;
public:
RegExpHeapGuard() : re_(NULL) { }
RegExpHeapGuard(RegExpShared &re) { init(re); }
~RegExpHeapGuard() { release(); }
public:
void init(RegExpShared &re) {
JS_ASSERT(!initialized());
re_ = &re;
re_->incRef();
}
void release() {
if (re_) {
re_->decRef();
re_ = NULL;
}
}
void trace(JSTracer *trc) {
if (initialized())
re_->trace(trc);
}
bool initialized() const { return !!re_; }
@ -246,9 +293,12 @@ class RegExpCompartment
struct Key {
JSAtom *atom;
uint16_t flag;
Key() {}
Key(JSAtom *atom, RegExpFlag flag)
: atom(atom), flag(flag) {}
: atom(atom), flag(flag)
{ }
typedef Key Lookup;
static HashNumber hash(const Lookup &l) {
return DefaultHasher<JSAtom *>::hash(l.atom) ^ (l.flag << 1);

View File

@ -27,15 +27,6 @@ SizeOfRegExpStaticsData(const JSObject *obj, JSMallocSizeOfFun mallocSizeOf)
return mallocSizeOf(obj->getPrivate());
}
inline
RegExpStatics::RegExpStatics()
: pendingLazyEvaluation(false),
bufferLink(NULL),
copied(false)
{
clear();
}
inline bool
RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, Value *out)
{
@ -231,15 +222,20 @@ RegExpStatics::copyTo(RegExpStatics &dst)
if (!pendingLazyEvaluation)
dst.matches.initArrayFrom(matches);
if (regexp.initialized())
dst.regexp.init(*regexp);
else
dst.regexp.release();
dst.matchesInput = matchesInput;
dst.regexp = regexp;
dst.lastIndex = lastIndex;
dst.pendingInput = pendingInput;
dst.flags = flags;
dst.pendingLazyEvaluation = pendingLazyEvaluation;
JS_ASSERT_IF(pendingLazyEvaluation, regexp);
JS_ASSERT_IF(pendingLazyEvaluation, regexp.initialized());
JS_ASSERT_IF(pendingLazyEvaluation, matchesInput);
JS_ASSERT(regexp.initialized() == dst.regexp.initialized());
}
inline void
@ -261,17 +257,20 @@ RegExpStatics::restore()
inline void
RegExpStatics::updateLazily(JSContext *cx, JSLinearString *input,
RegExpObject *regexp, size_t lastIndex)
RegExpShared *shared, size_t lastIndex)
{
JS_ASSERT(input && regexp);
JS_ASSERT(input && shared);
aboutToWrite();
BarrieredSetPair<JSString, JSLinearString>(cx->compartment,
pendingInput, input,
matchesInput, input);
pendingLazyEvaluation = true;
this->regexp = regexp;
if (regexp.initialized())
regexp.release();
regexp.init(*shared);
this->lastIndex = lastIndex;
pendingLazyEvaluation = true;
}
inline bool
@ -282,7 +281,7 @@ RegExpStatics::updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchP
/* Unset all lazy state. */
pendingLazyEvaluation = false;
this->regexp = NULL;
this->regexp.release();
this->lastIndex = size_t(-1);
BarrieredSetPair<JSString, JSLinearString>(cx->compartment,
@ -301,11 +300,14 @@ inline void
RegExpStatics::clear()
{
aboutToWrite();
flags = RegExpFlag(0);
pendingInput = NULL;
pendingLazyEvaluation = false;
matchesInput = NULL;
matches.forgetArray();
matchesInput = NULL;
regexp.release();
lastIndex = size_t(-1);
pendingInput = NULL;
flags = RegExpFlag(0);
pendingLazyEvaluation = false;
}
inline void
@ -363,8 +365,9 @@ RegExpStatics::checkInvariants()
{
#ifdef DEBUG
if (pendingLazyEvaluation) {
JS_ASSERT(regexp);
JS_ASSERT(regexp.initialized());
JS_ASSERT(pendingInput);
JS_ASSERT(lastIndex != size_t(-1));
return;
}

View File

@ -74,7 +74,7 @@ RegExpStatics::executeLazy(JSContext *cx)
if (!pendingLazyEvaluation)
return true;
JS_ASSERT(regexp);
JS_ASSERT(regexp.initialized());
JS_ASSERT(matchesInput);
JS_ASSERT(lastIndex != size_t(-1));
@ -87,17 +87,14 @@ RegExpStatics::executeLazy(JSContext *cx)
StableCharPtr chars(matchesInput->chars(), length);
/* Execute the full regular expression. */
RegExpGuard shared(cx);
if (!regexp->getShared(cx, &shared))
return false;
RegExpRunStatus status = shared->execute(cx, chars, length, &this->lastIndex, this->matches);
RegExpRunStatus status = regexp->execute(cx, chars, length, &this->lastIndex, this->matches);
if (status == RegExpRunStatus_Error)
return false;
/* Unset lazy state and remove rooted values that now have no use. */
pendingLazyEvaluation = false;
regexp = NULL;
regexp.release();
lastIndex = size_t(-1);
return true;
}

View File

@ -17,6 +17,7 @@
#include "js/Vector.h"
#include "vm/MatchPairs.h"
#include "vm/RegExpObject.h"
namespace js {
@ -27,7 +28,7 @@ class RegExpStatics
HeapPtr<JSLinearString> matchesInput;
/* The previous RegExp input, used to resolve lazy state. */
HeapPtr<RegExpObject> regexp;
RegExpHeapGuard regexp;
size_t lastIndex;
/* The latest RegExp input, set before execution. */
@ -44,6 +45,10 @@ class RegExpStatics
RegExpStatics *bufferLink;
bool copied;
public:
RegExpStatics() : bufferLink(NULL), copied(false) { clear(); }
static JSObject *create(JSContext *cx, GlobalObject *parent);
private:
bool executeLazy(JSContext *cx);
@ -80,14 +85,9 @@ class RegExpStatics
friend class PreserveRegExpStatics;
public:
inline RegExpStatics();
static JSObject *create(JSContext *cx, GlobalObject *parent);
/* Mutators. */
inline void updateLazily(JSContext *cx, JSLinearString *input,
RegExpObject *regexp, size_t lastIndex);
RegExpShared *shared, size_t lastIndex);
inline bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs &newPairs);
inline void setMultiline(JSContext *cx, bool enabled);
@ -120,12 +120,16 @@ class RegExpStatics
}
void mark(JSTracer *trc) {
if (regexp)
gc::MarkObject(trc, &regexp, "res->regexp");
/*
* Changes to this function must also be reflected in
* RegExpStatics::AutoRooter::trace().
*/
if (pendingInput)
MarkString(trc, &pendingInput, "res->pendingInput");
if (matchesInput)
MarkString(trc, &matchesInput, "res->matchesInput");
if (regexp.initialized())
regexp->trace(trc);
}
/* Value creators. */