mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 634654 - Add RegExpPrivate cache. (r=Waldo)
This commit is contained in:
parent
033bc59a4b
commit
5559fb1d83
@ -206,14 +206,6 @@ EscapeNakedForwardSlashes(JSContext *cx, JSLinearString *unescaped)
|
||||
return sb.empty() ? unescaped : sb.finishString();
|
||||
}
|
||||
|
||||
static bool
|
||||
ResetRegExpObjectWithStatics(JSContext *cx, RegExpObject *reobj,
|
||||
JSLinearString *str, RegExpFlag flags = RegExpFlag(0))
|
||||
{
|
||||
flags = RegExpFlag(flags | cx->regExpStatics()->getFlags());
|
||||
return reobj->reset(cx, str, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile a new |RegExpPrivate| for the |RegExpObject|.
|
||||
*
|
||||
@ -226,12 +218,15 @@ ResetRegExpObjectWithStatics(JSContext *cx, RegExpObject *reobj,
|
||||
* flags := ToString(flags) if defined(flags) else ''
|
||||
*/
|
||||
static bool
|
||||
CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, Value *rval)
|
||||
CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder,
|
||||
uintN argc, Value *argv, Value *rval)
|
||||
{
|
||||
if (argc == 0) {
|
||||
if (!ResetRegExpObjectWithStatics(cx, obj, cx->runtime->emptyString))
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags());
|
||||
if (!reobj)
|
||||
return false;
|
||||
*rval = ObjectValue(*obj);
|
||||
*rval = ObjectValue(*reobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -249,9 +244,10 @@ CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, V
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!obj->reset(cx, sourceObj.asRegExp()))
|
||||
RegExpObject *reobj = builder.build(sourceObj.asRegExp());
|
||||
if (!reobj)
|
||||
return false;
|
||||
*rval = ObjectValue(*obj);
|
||||
*rval = ObjectValue(*reobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -285,9 +281,12 @@ CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, V
|
||||
if (!RegExpPrivateCode::checkSyntax(cx, NULL, escapedSourceStr))
|
||||
return false;
|
||||
|
||||
if (!ResetRegExpObjectWithStatics(cx, obj, escapedSourceStr, flags))
|
||||
return false;
|
||||
*rval = ObjectValue(*obj);
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
|
||||
if (!reobj)
|
||||
return NULL;
|
||||
|
||||
*rval = ObjectValue(*reobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -301,8 +300,8 @@ regexp_compile(JSContext *cx, uintN argc, Value *vp)
|
||||
if (!obj)
|
||||
return ok;
|
||||
|
||||
RegExpObject *reobj = obj->asRegExp();
|
||||
return CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval());
|
||||
RegExpObjectBuilder builder(cx, obj->asRegExp());
|
||||
return CompileRegExpObject(cx, builder, args.length(), args.array(), &args.rval());
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -322,14 +321,11 @@ regexp_construct(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
}
|
||||
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
|
||||
if (!obj)
|
||||
RegExpObjectBuilder builder(cx);
|
||||
if (!CompileRegExpObject(cx, builder, argc, argv, &JS_RVAL(cx, vp)))
|
||||
return false;
|
||||
|
||||
if (!CompileRegExpObject(cx, obj->asRegExp(), argc, argv, &JS_RVAL(cx, vp)))
|
||||
return false;
|
||||
|
||||
*vp = ObjectValue(*obj);
|
||||
*vp = ObjectValue(*builder.reobj());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -462,9 +458,11 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj)
|
||||
JSObject *proto = global->createBlankPrototype(cx, &RegExpClass);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
proto->setPrivate(NULL);
|
||||
|
||||
RegExpObject *reproto = proto->asRegExp();
|
||||
if (!reproto->reset(cx, cx->runtime->emptyString, RegExpFlag(0)))
|
||||
RegExpObjectBuilder builder(cx, reproto);
|
||||
if (!builder.build(cx->runtime->emptyString, RegExpFlag(0)))
|
||||
return NULL;
|
||||
|
||||
if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods))
|
||||
@ -526,6 +524,7 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
|
||||
* have to take a defensive refcount here.
|
||||
*/
|
||||
AutoRefCount<RegExpPrivate> arc(cx, NeedsIncRef<RegExpPrivate>(rep));
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
|
||||
/* Step 2. */
|
||||
|
@ -111,6 +111,7 @@ ThreadData::ThreadData()
|
||||
#endif
|
||||
waiveGCQuota(false),
|
||||
tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
repCache(NULL),
|
||||
dtoaState(NULL),
|
||||
nativeStackBase(GetNativeStackBase()),
|
||||
pendingProxyOperation(NULL),
|
||||
@ -153,6 +154,31 @@ ThreadData::triggerOperationCallback(JSRuntime *rt)
|
||||
#endif
|
||||
}
|
||||
|
||||
RegExpPrivateCache *
|
||||
ThreadData::createRegExpPrivateCache(JSRuntime *rt)
|
||||
{
|
||||
JS_ASSERT(!repCache);
|
||||
RegExpPrivateCache *newCache = rt->new_<RegExpPrivateCache>(rt);
|
||||
|
||||
if (!newCache || !newCache->init()) {
|
||||
rt->delete_<RegExpPrivateCache>(newCache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
repCache = newCache;
|
||||
return repCache;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadData::purgeRegExpPrivateCache(JSRuntime *rt)
|
||||
{
|
||||
if (!repCache)
|
||||
return;
|
||||
|
||||
rt->delete_<RegExpPrivateCache>(repCache);
|
||||
repCache = NULL;
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
JSScript *
|
||||
@ -315,6 +341,24 @@ js_PurgeThreads(JSContext *cx)
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
js_PurgeThreads_PostGlobalSweep(JSContext *cx)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
for (JSThread::Map::Enum e(cx->runtime->threads);
|
||||
!e.empty();
|
||||
e.popFront())
|
||||
{
|
||||
JSThread *thread = e.front().value;
|
||||
|
||||
JS_ASSERT(!JS_CLIST_IS_EMPTY(&thread->contextList));
|
||||
thread->data.purgeRegExpPrivateCache(cx->runtime);
|
||||
}
|
||||
#else
|
||||
cx->runtime->threadData.purgeRegExpPrivateCache(cx->runtime);
|
||||
#endif
|
||||
}
|
||||
|
||||
JSContext *
|
||||
js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||
{
|
||||
|
@ -197,6 +197,25 @@ struct ThreadData {
|
||||
static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
|
||||
LifoAlloc tempLifoAlloc;
|
||||
|
||||
private:
|
||||
js::RegExpPrivateCache *repCache;
|
||||
|
||||
js::RegExpPrivateCache *createRegExpPrivateCache(JSRuntime *rt);
|
||||
|
||||
public:
|
||||
js::RegExpPrivateCache *getRegExpPrivateCache() { return repCache; }
|
||||
|
||||
/* N.B. caller is responsible for reporting OOM. */
|
||||
js::RegExpPrivateCache *getOrCreateRegExpPrivateCache(JSRuntime *rt) {
|
||||
if (repCache)
|
||||
return repCache;
|
||||
|
||||
return createRegExpPrivateCache(rt);
|
||||
}
|
||||
|
||||
/* Called at the end of the global GC sweep phase to deallocate repCache memory. */
|
||||
void purgeRegExpPrivateCache(JSRuntime *rt);
|
||||
|
||||
/*
|
||||
* The GSN cache is per thread since even multi-cx-per-thread embeddings
|
||||
* do not interleave js_GetSrcNote calls.
|
||||
@ -1214,6 +1233,8 @@ struct JSContext
|
||||
js::GCHelperThread *gcBackgroundFree;
|
||||
#endif
|
||||
|
||||
js::ThreadData *threadData() { return JS_THREAD_DATA(this); }
|
||||
|
||||
inline void* malloc_(size_t bytes) {
|
||||
return runtime->malloc_(bytes, this);
|
||||
}
|
||||
@ -1890,6 +1911,9 @@ js_FinishThreads(JSRuntime *rt);
|
||||
extern void
|
||||
js_PurgeThreads(JSContext *cx);
|
||||
|
||||
extern void
|
||||
js_PurgeThreads_PostGlobalSweep(JSContext *cx);
|
||||
|
||||
namespace js {
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
@ -2910,6 +2910,9 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
||||
|
||||
MarkAndSweep(cx, gckind);
|
||||
|
||||
if (!comp)
|
||||
js_PurgeThreads_PostGlobalSweep(cx);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
|
||||
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
|
||||
|
@ -124,6 +124,7 @@ struct Class;
|
||||
|
||||
class RegExpObject;
|
||||
class RegExpPrivate;
|
||||
class RegExpObjectBuilder;
|
||||
class RegExpStatics;
|
||||
class MatchPairs;
|
||||
|
||||
@ -232,6 +233,9 @@ typedef HashMap<jsbytecode *, BreakpointSite *, DefaultHasher<jsbytecode *>, Run
|
||||
class Debugger;
|
||||
class WatchpointMap;
|
||||
|
||||
typedef HashMap<JSAtom *, RegExpPrivate *, DefaultHasher<JSAtom *>, RuntimeAllocPolicy>
|
||||
RegExpPrivateCache;
|
||||
|
||||
typedef JSNative Native;
|
||||
typedef JSPropertyOp PropertyOp;
|
||||
typedef JSStrictPropertyOp StrictPropertyOp;
|
||||
|
@ -1264,8 +1264,14 @@ class FlatMatch
|
||||
int32 match() const { return match_; }
|
||||
};
|
||||
|
||||
/*
|
||||
* Some string methods operate on a RegExpObject, if it is present, but if it
|
||||
* is absent create an internal regular expression matcher. This unifies the
|
||||
* interface.
|
||||
*/
|
||||
class RegExpPair
|
||||
{
|
||||
JSContext *cx;
|
||||
AutoRefCount<RegExpPrivate> rep_;
|
||||
RegExpObject *reobj_;
|
||||
|
||||
@ -1273,9 +1279,10 @@ class RegExpPair
|
||||
void operator=(const RegExpPair &);
|
||||
|
||||
public:
|
||||
explicit RegExpPair(JSContext *cx) : rep_(cx) {}
|
||||
explicit RegExpPair(JSContext *cx) : cx(cx), rep_(cx) {}
|
||||
|
||||
bool resetWithObject(JSContext *cx, RegExpObject *reobj) {
|
||||
JS_ASSERT(cx == this->cx);
|
||||
reobj_ = reobj;
|
||||
RegExpPrivate *rep = reobj_->asRegExp()->getOrCreatePrivate(cx);
|
||||
if (!rep)
|
||||
@ -1302,9 +1309,8 @@ class RegExpPair
|
||||
/*
|
||||
* RegExpGuard factors logic out of String regexp operations.
|
||||
*
|
||||
* @param optarg Indicates in which argument position RegExp
|
||||
* flags will be found, if present. This is a Mozilla
|
||||
* extension and not part of any ECMA spec.
|
||||
* |optarg| indicates in which argument position RegExp flags will be found, if
|
||||
* present. This is a Mozilla extension and not part of any ECMA spec.
|
||||
*/
|
||||
class RegExpGuard
|
||||
{
|
||||
@ -1368,8 +1374,9 @@ class RegExpGuard
|
||||
* Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
|
||||
* pattern string, or a lengthy pattern string can thwart this process.
|
||||
*
|
||||
* @param checkMetaChars Look for regexp metachars in the pattern string.
|
||||
* @return Whether flat matching could be used.
|
||||
* |checkMetaChars| looks for regexp metachars in the pattern string.
|
||||
*
|
||||
* Return whether flat matching could be used.
|
||||
*
|
||||
* N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending().
|
||||
*/
|
||||
|
@ -89,6 +89,12 @@ HasRegExpMetaChars(const jschar *chars, size_t length)
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void
|
||||
RegExpObject::setPrivate(RegExpPrivate *rep)
|
||||
{
|
||||
JSObject::setPrivate(rep);
|
||||
}
|
||||
|
||||
inline RegExpObject *
|
||||
RegExpObject::create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
|
||||
RegExpFlag flags, TokenStream *tokenStream)
|
||||
@ -101,57 +107,44 @@ inline RegExpObject *
|
||||
RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length,
|
||||
RegExpFlag flags, TokenStream *tokenStream)
|
||||
{
|
||||
JSLinearString *source = js_NewStringCopyN(cx, chars, length);
|
||||
JSAtom *source = js_AtomizeChars(cx, chars, length);
|
||||
if (!source)
|
||||
return NULL;
|
||||
|
||||
/* |NewBuiltinClassInstance| can GC. */
|
||||
JS::Anchor<JSString *> anchor(source);
|
||||
return createNoStatics(cx, source, flags, tokenStream);
|
||||
}
|
||||
|
||||
inline RegExpObject *
|
||||
RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags,
|
||||
TokenStream *tokenStream)
|
||||
{
|
||||
if (!RegExpPrivateCode::checkSyntax(cx, tokenStream, source))
|
||||
return NULL;
|
||||
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
RegExpObjectBuilder builder(cx);
|
||||
return builder.build(source, flags);
|
||||
}
|
||||
|
||||
RegExpObject *reobj = obj->asRegExp();
|
||||
return reobj->reset(cx, source, flags) ? reobj : NULL;
|
||||
inline void
|
||||
RegExpObject::purge(JSContext *cx)
|
||||
{
|
||||
if (RegExpPrivate *rep = getPrivate()) {
|
||||
rep->decref(cx);
|
||||
setPrivate(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
RegExpObject::finalize(JSContext *cx)
|
||||
{
|
||||
if (RegExpPrivate *rep = getPrivate())
|
||||
rep->decref(cx);
|
||||
purge(cx);
|
||||
#ifdef DEBUG
|
||||
setPrivate((void *) 0x1); /* Non-null but still in the zero page. */
|
||||
setPrivate((RegExpPrivate *) 0x1); /* Non-null but still in the zero page. */
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool
|
||||
RegExpObject::reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep)
|
||||
{
|
||||
if (!reset(cx, rep->getSource(), rep->getFlags()))
|
||||
return false;
|
||||
|
||||
setPrivate(rep.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
RegExpObject::reset(JSContext *cx, RegExpObject *other)
|
||||
{
|
||||
if (RegExpPrivate *rep = other->getPrivate()) {
|
||||
rep->incref(cx);
|
||||
return reset(cx, AlreadyIncRefed<RegExpPrivate>(rep));
|
||||
}
|
||||
|
||||
return reset(cx, other->getSource(), other->getFlags());
|
||||
}
|
||||
|
||||
inline bool
|
||||
RegExpObject::reset(JSContext *cx, JSLinearString *source, RegExpFlag flags)
|
||||
RegExpObject::init(JSContext *cx, JSLinearString *source, RegExpFlag flags)
|
||||
{
|
||||
if (nativeEmpty()) {
|
||||
const js::Shape **shapep = &cx->compartment->initialRegExpShape;
|
||||
@ -174,8 +167,8 @@ RegExpObject::reset(JSContext *cx, JSLinearString *source, RegExpFlag flags)
|
||||
MULTILINE_FLAG_SLOT);
|
||||
JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot == STICKY_FLAG_SLOT);
|
||||
|
||||
JS_ASSERT(!getPrivate());
|
||||
zeroLastIndex();
|
||||
setPrivate(NULL);
|
||||
setSource(source);
|
||||
setGlobal(flags & GlobalFlag);
|
||||
setIgnoreCase(flags & IgnoreCaseFlag);
|
||||
@ -191,11 +184,46 @@ RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, T
|
||||
{
|
||||
typedef AlreadyIncRefed<RegExpPrivate> RetType;
|
||||
|
||||
/*
|
||||
* We choose to only cache |RegExpPrivate|s who have atoms as
|
||||
* sources, under the unverified premise that non-atoms will have a
|
||||
* low hit rate (both hit ratio and absolute number of hits).
|
||||
*/
|
||||
bool cacheable = source->isAtom();
|
||||
|
||||
/*
|
||||
* Refcount note: not all |RegExpPrivate|s are cached so we need to
|
||||
* keep a refcount. The cache holds a "weak ref", where the
|
||||
* |RegExpPrivate|'s deallocation decref will first cause it to
|
||||
* remove itself from the cache.
|
||||
*/
|
||||
|
||||
JSRuntime *rt = cx->runtime;
|
||||
RegExpPrivateCache *cache = NULL; /* Quell "may be used uninitialized". */
|
||||
RegExpPrivateCache::AddPtr addPtr;
|
||||
if (cacheable) {
|
||||
cache = cx->threadData()->getOrCreateRegExpPrivateCache(rt);
|
||||
if (!cache) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return RetType(NULL);
|
||||
}
|
||||
|
||||
addPtr = cache->lookupForAdd(&source->asAtom());
|
||||
if (addPtr) {
|
||||
RegExpPrivate *cached = addPtr->value;
|
||||
if (cached->getFlags() == flags) {
|
||||
cached->incref(cx);
|
||||
return RetType(cached);
|
||||
}
|
||||
/* Note: on flag mismatch, we clobber the existing entry. */
|
||||
}
|
||||
}
|
||||
|
||||
JSLinearString *flatSource = source->ensureLinear(cx);
|
||||
if (!flatSource)
|
||||
return RetType(NULL);
|
||||
|
||||
RegExpPrivate *self = cx->new_<RegExpPrivate>(flatSource, flags, cx->compartment);
|
||||
RegExpPrivate *self = cx->new_<RegExpPrivate>(flatSource, flags);
|
||||
if (!self)
|
||||
return RetType(NULL);
|
||||
|
||||
@ -204,6 +232,18 @@ RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, T
|
||||
return RetType(NULL);
|
||||
}
|
||||
|
||||
if (cacheable) {
|
||||
if (addPtr) {
|
||||
JS_ASSERT(addPtr->key == &self->getSource()->asAtom());
|
||||
addPtr->value = self;
|
||||
} else {
|
||||
if (!cache->add(addPtr, &source->asAtom(), self)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return RetType(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RetType(self);
|
||||
}
|
||||
|
||||
@ -331,40 +371,23 @@ RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t length, si
|
||||
inline void
|
||||
RegExpPrivate::incref(JSContext *cx)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
assertSameCompartment(cx, compartment);
|
||||
#endif
|
||||
++refCount;
|
||||
}
|
||||
|
||||
inline void
|
||||
RegExpPrivate::decref(JSContext *cx)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
assertSameCompartment(cx, compartment);
|
||||
#endif
|
||||
if (--refCount == 0)
|
||||
cx->delete_(this);
|
||||
}
|
||||
if (--refCount != 0)
|
||||
return;
|
||||
|
||||
inline RegExpPrivate *
|
||||
RegExpObject::getOrCreatePrivate(JSContext *cx)
|
||||
{
|
||||
if (RegExpPrivate *rep = getPrivate())
|
||||
return rep;
|
||||
RegExpPrivateCache *cache;
|
||||
if (source->isAtom() && (cache = cx->threadData()->getRegExpPrivateCache())) {
|
||||
RegExpPrivateCache::Ptr ptr = cache->lookup(&source->asAtom());
|
||||
if (ptr && ptr->value == this)
|
||||
cache->remove(ptr);
|
||||
}
|
||||
|
||||
return makePrivate(cx) ? getPrivate() : NULL;
|
||||
}
|
||||
|
||||
inline RegExpPrivate *
|
||||
RegExpObject::getPrivate() const
|
||||
{
|
||||
RegExpPrivate *rep = static_cast<RegExpPrivate *>(JSObject::getPrivate());
|
||||
#ifdef DEBUG
|
||||
if (rep)
|
||||
CompartmentChecker::check(compartment(), rep->compartment);
|
||||
#endif
|
||||
return rep;
|
||||
cx->delete_(this);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
@ -59,6 +59,106 @@ JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
|
||||
JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
|
||||
JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
|
||||
|
||||
/* RegExpObjectBuilder */
|
||||
|
||||
bool
|
||||
RegExpObjectBuilder::getOrCreate()
|
||||
{
|
||||
if (reobj_)
|
||||
return true;
|
||||
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
|
||||
if (!obj)
|
||||
return false;
|
||||
obj->setPrivate(NULL);
|
||||
|
||||
reobj_ = obj->asRegExp();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpObjectBuilder::getOrCreateClone(RegExpObject *proto)
|
||||
{
|
||||
JS_ASSERT(!reobj_);
|
||||
|
||||
JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent());
|
||||
if (!clone)
|
||||
return false;
|
||||
clone->setPrivate(NULL);
|
||||
|
||||
reobj_ = clone->asRegExp();
|
||||
return true;
|
||||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObjectBuilder::build(AlreadyIncRefed<RegExpPrivate> rep)
|
||||
{
|
||||
if (!getOrCreate()) {
|
||||
rep->decref(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
reobj_->purge(cx);
|
||||
if (!reobj_->init(cx, rep->getSource(), rep->getFlags())) {
|
||||
rep->decref(cx);
|
||||
return NULL;
|
||||
}
|
||||
reobj_->setPrivate(rep.get());
|
||||
|
||||
return reobj_;
|
||||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObjectBuilder::build(JSLinearString *source, RegExpFlag flags)
|
||||
{
|
||||
if (!getOrCreate())
|
||||
return NULL;
|
||||
|
||||
reobj_->purge(cx);
|
||||
return reobj_->init(cx, source, flags) ? reobj_ : NULL;
|
||||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObjectBuilder::build(RegExpObject *other)
|
||||
{
|
||||
RegExpPrivate *rep = other->getOrCreatePrivate(cx);
|
||||
if (!rep)
|
||||
return NULL;
|
||||
|
||||
/* Now, incref it for the RegExpObject being built. */
|
||||
rep->incref(cx);
|
||||
return build(AlreadyIncRefed<RegExpPrivate>(rep));
|
||||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto)
|
||||
{
|
||||
if (!getOrCreateClone(proto))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Check that the RegExpPrivate for the original is okay to use in
|
||||
* the clone -- if the |RegExpStatics| provides more flags we'll
|
||||
* need a different |RegExpPrivate|.
|
||||
*/
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpFlag origFlags = other->getFlags();
|
||||
RegExpFlag staticsFlags = res->getFlags();
|
||||
if ((origFlags & staticsFlags) != staticsFlags) {
|
||||
RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
|
||||
return build(other->getSource(), newFlags);
|
||||
}
|
||||
|
||||
RegExpPrivate *toShare = other->getOrCreatePrivate(cx);
|
||||
if (!toShare)
|
||||
return NULL;
|
||||
|
||||
toShare->incref(cx);
|
||||
return build(AlreadyIncRefed<RegExpPrivate>(toShare));
|
||||
}
|
||||
|
||||
/* MatchPairs */
|
||||
|
||||
MatchPairs *
|
||||
MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount)
|
||||
{
|
||||
@ -130,16 +230,16 @@ RegExpPrivate::execute(JSContext *cx, const jschar *chars, size_t length, size_t
|
||||
return RegExpRunStatus_Success;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpPrivate *
|
||||
RegExpObject::makePrivate(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(!getPrivate());
|
||||
AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, getSource(), getFlags(), NULL);
|
||||
if (!rep)
|
||||
return false;
|
||||
return NULL;
|
||||
|
||||
setPrivate(rep.get());
|
||||
return true;
|
||||
return rep.get();
|
||||
}
|
||||
|
||||
RegExpRunStatus
|
||||
@ -209,13 +309,11 @@ js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp)
|
||||
if (!JS_XDRString(xdr, &source) || !JS_XDRUint32(xdr, &flagsword))
|
||||
return false;
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
JS::Anchor<JSString *> anchor(source);
|
||||
const jschar *chars = source->getChars(xdr->cx);
|
||||
if (!chars)
|
||||
JSAtom *atom = js_AtomizeString(xdr->cx, source);
|
||||
if (!atom)
|
||||
return false;
|
||||
size_t len = source->length();
|
||||
RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, chars, len,
|
||||
RegExpFlag(flagsword), NULL);
|
||||
RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, atom, RegExpFlag(flagsword),
|
||||
NULL);
|
||||
if (!reobj)
|
||||
return false;
|
||||
|
||||
@ -238,6 +336,12 @@ regexp_finalize(JSContext *cx, JSObject *obj)
|
||||
obj->asRegExp()->finalize(cx);
|
||||
}
|
||||
|
||||
static void
|
||||
regexp_trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
obj->asRegExp()->purge(trc->context);
|
||||
}
|
||||
|
||||
Class js::RegExpClass = {
|
||||
js_RegExp_str,
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
@ -257,7 +361,7 @@ Class js::RegExpClass = {
|
||||
NULL, /* construct */
|
||||
js_XDRRegExpObject,
|
||||
NULL, /* hasInstance */
|
||||
NULL /* trace */
|
||||
regexp_trace
|
||||
};
|
||||
|
||||
#if ENABLE_YARR_JIT
|
||||
@ -378,54 +482,14 @@ RegExpPrivate::create(JSContext *cx, JSLinearString *str, JSString *opt, TokenSt
|
||||
return create(cx, str, flags, ts);
|
||||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObject::clone(JSContext *cx, RegExpObject *reobj, RegExpObject *proto)
|
||||
{
|
||||
JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent());
|
||||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* This clone functionality does not duplicate the JIT'd code blob,
|
||||
* which is necessary for cross-compartment cloning functionality.
|
||||
*/
|
||||
assertSameCompartment(cx, reobj, clone);
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpObject *reclone = clone->asRegExp();
|
||||
|
||||
/*
|
||||
* Check that the RegExpPrivate for the original is okay to use in
|
||||
* the clone -- if the |RegExpStatics| provides more flags we'll
|
||||
* need a different |RegExpPrivate|.
|
||||
*/
|
||||
RegExpFlag origFlags = reobj->getFlags();
|
||||
RegExpFlag staticsFlags = res->getFlags();
|
||||
if ((origFlags & staticsFlags) != staticsFlags) {
|
||||
RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
|
||||
return reclone->reset(cx, reobj->getSource(), newFlags) ? reclone : NULL;
|
||||
}
|
||||
|
||||
RegExpPrivate *toShare = reobj->getOrCreatePrivate(cx);
|
||||
if (!toShare)
|
||||
return NULL;
|
||||
|
||||
toShare->incref(cx);
|
||||
if (!reclone->reset(cx, AlreadyIncRefed<RegExpPrivate>(toShare))) {
|
||||
toShare->decref(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return reclone;
|
||||
}
|
||||
|
||||
JSObject * JS_FASTCALL
|
||||
js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
|
||||
{
|
||||
JS_ASSERT(obj->isRegExp());
|
||||
JS_ASSERT(proto->isRegExp());
|
||||
|
||||
return RegExpObject::clone(cx, obj->asRegExp(), proto->asRegExp());
|
||||
RegExpObjectBuilder builder(cx);
|
||||
return builder.clone(obj->asRegExp(), proto->asRegExp());
|
||||
}
|
||||
|
||||
#ifdef JS_TRACER
|
||||
|
@ -82,13 +82,14 @@ class RegExpObject : public ::JSObject
|
||||
*/
|
||||
static inline RegExpObject *
|
||||
create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length, RegExpFlag flags,
|
||||
TokenStream *ts);
|
||||
TokenStream *tokenStream);
|
||||
|
||||
static inline RegExpObject *
|
||||
createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
|
||||
TokenStream *ts);
|
||||
TokenStream *tokenStream);
|
||||
|
||||
static RegExpObject *clone(JSContext *cx, RegExpObject *obj, RegExpObject *proto);
|
||||
static inline RegExpObject *
|
||||
createNoStatics(JSContext *cx, JSAtom *atom, RegExpFlag flags, TokenStream *tokenStream);
|
||||
|
||||
/* Note: fallible. */
|
||||
JSFlatString *toString(JSContext *cx) const;
|
||||
@ -148,27 +149,35 @@ class RegExpObject : public ::JSObject
|
||||
bool multiline() const { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
|
||||
bool sticky() const { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
|
||||
|
||||
/*
|
||||
* N.B. |RegExpObject|s can be mutated in place because of |RegExp.prototype.compile|, hence
|
||||
* |reset| for re-initialization.
|
||||
*/
|
||||
|
||||
inline bool reset(JSContext *cx, RegExpObject *other);
|
||||
inline bool reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
|
||||
inline bool reset(JSContext *cx, JSLinearString *source, RegExpFlag flags);
|
||||
|
||||
inline RegExpPrivate *getOrCreatePrivate(JSContext *cx);
|
||||
inline void finalize(JSContext *cx);
|
||||
|
||||
/* Clear out lazy |RegExpPrivate|. */
|
||||
inline void purge(JSContext *x);
|
||||
|
||||
RegExpPrivate *getOrCreatePrivate(JSContext *cx) {
|
||||
if (RegExpPrivate *rep = getPrivate())
|
||||
return rep;
|
||||
|
||||
return makePrivate(cx);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class RegExpObjectBuilder;
|
||||
|
||||
inline bool init(JSContext *cx, JSLinearString *source, RegExpFlag flags);
|
||||
|
||||
/* The |RegExpPrivate| is lazily created at the time of use. */
|
||||
inline RegExpPrivate *getPrivate() const;
|
||||
RegExpPrivate *getPrivate() const {
|
||||
return static_cast<RegExpPrivate *>(JSObject::getPrivate());
|
||||
}
|
||||
|
||||
inline void setPrivate(RegExpPrivate *rep);
|
||||
|
||||
/*
|
||||
* Precondition: the syntax for |source| has already been validated.
|
||||
* Side effect: sets the private field.
|
||||
*/
|
||||
bool makePrivate(JSContext *cx);
|
||||
RegExpPrivate *makePrivate(JSContext *cx);
|
||||
|
||||
friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSLinearString *, RegExpFlag);
|
||||
friend bool ResetRegExpObject(JSContext *, RegExpObject *, AlreadyIncRefed<RegExpPrivate>);
|
||||
@ -184,6 +193,32 @@ class RegExpObject : public ::JSObject
|
||||
RegExpObject &operator=(const RegExpObject &reo);
|
||||
}; /* class RegExpObject */
|
||||
|
||||
/* Either builds a new RegExpObject or re-initializes an existing one. */
|
||||
class RegExpObjectBuilder
|
||||
{
|
||||
JSContext *cx;
|
||||
RegExpObject *reobj_;
|
||||
|
||||
bool getOrCreate();
|
||||
bool getOrCreateClone(RegExpObject *proto);
|
||||
|
||||
public:
|
||||
RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj = NULL)
|
||||
: cx(cx), reobj_(reobj)
|
||||
{ }
|
||||
|
||||
RegExpObject *reobj() { return reobj_; }
|
||||
|
||||
/* Note: In case of failure, |rep| will be decrefed. */
|
||||
RegExpObject *build(AlreadyIncRefed<RegExpPrivate> rep);
|
||||
|
||||
RegExpObject *build(JSLinearString *str, RegExpFlag flags);
|
||||
RegExpObject *build(RegExpObject *other);
|
||||
|
||||
/* Perform a VM-internal clone. */
|
||||
RegExpObject *clone(RegExpObject *other, RegExpObject *proto);
|
||||
};
|
||||
|
||||
/* Abstracts away the gross |RegExpPrivate| backend details. */
|
||||
class RegExpPrivateCode
|
||||
{
|
||||
@ -243,12 +278,6 @@ class RegExpPrivateCode
|
||||
static void reportPCREError(JSContext *cx, int error);
|
||||
#endif
|
||||
|
||||
inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
|
||||
RegExpFlag flags);
|
||||
|
||||
inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
|
||||
int *output, size_t outputCount);
|
||||
|
||||
static size_t getOutputSize(size_t pairCount) {
|
||||
#if ENABLE_YARR_JIT
|
||||
return pairCount * 2;
|
||||
@ -256,6 +285,13 @@ class RegExpPrivateCode
|
||||
return pairCount * 3; /* Should be x2, but PCRE has... needs. */
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
|
||||
RegExpFlag flags);
|
||||
|
||||
|
||||
inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
|
||||
int *output, size_t outputCount);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -265,8 +301,8 @@ class RegExpPrivateCode
|
||||
* compilation.
|
||||
*
|
||||
* Non-atomic refcounting is used, so single-thread invariants must be
|
||||
* maintained: we check regexp operations are performed in a single
|
||||
* compartment.
|
||||
* maintained. |RegExpPrivate|s are currently shared within a single
|
||||
* |ThreadData|.
|
||||
*
|
||||
* Note: refCount cannot overflow because that would require more
|
||||
* referring regexp objects than there is space for in addressable
|
||||
@ -280,12 +316,9 @@ class RegExpPrivate
|
||||
uintN parenCount;
|
||||
RegExpFlag flags;
|
||||
|
||||
public:
|
||||
DebugOnly<JSCompartment *> compartment;
|
||||
|
||||
private:
|
||||
RegExpPrivate(JSLinearString *source, RegExpFlag flags, JSCompartment *compartment)
|
||||
: source(source), refCount(1), parenCount(0), flags(flags), compartment(compartment)
|
||||
RegExpPrivate(JSLinearString *source, RegExpFlag flags)
|
||||
: source(source), refCount(1), parenCount(0), flags(flags)
|
||||
{ }
|
||||
|
||||
JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
|
||||
|
Loading…
Reference in New Issue
Block a user