Bug 688069 - remove spurious call to zeroLastIndex, bringing us to parity with jsc/v8. This allows removing RegExpPair and several other cleanups (r=cdleary)

--HG--
extra : rebase_source : ae6497b500e93019ecc06ba2668fbdbe70b15417
This commit is contained in:
Luke Wagner 2012-01-27 15:32:53 -08:00
parent b78ff5f631
commit 1f69b3025f
12 changed files with 277 additions and 352 deletions

View File

@ -159,7 +159,7 @@ ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T *re, JSLinearString *inpu
}
bool
js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpMatcher &matcher, JSLinearString *input,
js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, const RegExpMatcher &matcher, JSLinearString *input,
const jschar *chars, size_t length,
size_t *lastIndex, RegExpExecType type, Value *rval)
{
@ -207,7 +207,7 @@ EscapeNakedForwardSlashes(JSContext *cx, JSLinearString *unescaped)
}
/*
* Compile a new |RegExpPrivate| for the |RegExpObject|.
* Compile a new |RegExpShared| for the |RegExpObject|.
*
* Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
* arguments:
@ -514,15 +514,17 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
if (!obj)
return ok;
RegExpObject *reobj = &obj->asRegExp();
RegExpObject &reobj = obj->asRegExp();
RegExpMatcher matcher(cx);
if (reobj->startsWithAtomizedGreedyStar()) {
if (!matcher.resetWithTestOptimized(reobj))
if (reobj.startsWithAtomizedGreedyStar()) {
if (!matcher.initWithTestOptimized(reobj))
return false;
} else {
if (!matcher.reset(reobj))
RegExpShared *shared = reobj.getShared(cx);
if (!shared)
return false;
matcher.init(NeedsIncRef<RegExpShared>(shared));
}
RegExpStatics *res = cx->regExpStatics();
@ -540,7 +542,7 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
size_t length = input->length();
/* Step 4. */
const Value &lastIndex = reobj->getLastIndex();
const Value &lastIndex = reobj.getLastIndex();
/* Step 5. */
jsdouble i;
@ -553,7 +555,7 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
/* Step 9a. */
if (i < 0 || i > length) {
reobj->zeroLastIndex();
reobj.zeroLastIndex();
args.rval() = NullValue();
return true;
}
@ -569,9 +571,9 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
/* Step 11 (with sticky extension). */
if (matcher.global() || (!args.rval().isNull() && matcher.sticky())) {
if (args.rval().isNull())
reobj->zeroLastIndex();
reobj.zeroLastIndex();
else
reobj->setLastIndex(lastIndexInt);
reobj.setLastIndex(lastIndexInt);
}
return true;

View File

@ -59,13 +59,13 @@ namespace js {
* |chars| and |length|.
*/
bool
ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj, JSLinearString *input,
const jschar *chars, size_t length,
ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj,
JSLinearString *input, const jschar *chars, size_t length,
size_t *lastIndex, RegExpExecType type, Value *rval);
bool
ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpMatcher &matcher, JSLinearString *input,
const jschar *chars, size_t length,
ExecuteRegExp(JSContext *cx, RegExpStatics *res, const RegExpMatcher &matcher,
JSLinearString *input, const jschar *chars, size_t length,
size_t *lastIndex, RegExpExecType type, Value *rval);
extern JSBool

View File

@ -701,7 +701,7 @@ JSRuntime::JSRuntime()
tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
execAlloc_(NULL),
bumpAlloc_(NULL),
repCache_(NULL),
reCache_(NULL),
nativeStackBase(0),
nativeStackQuota(0),
interpreterFrames(NULL),
@ -849,7 +849,7 @@ JSRuntime::~JSRuntime()
delete_<JSC::ExecutableAllocator>(execAlloc_);
delete_<WTF::BumpPointerAllocator>(bumpAlloc_);
JS_ASSERT(!repCache_);
JS_ASSERT(!reCache_);
#ifdef DEBUG
/* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */

View File

@ -148,22 +148,21 @@ JSRuntime::createBumpPointerAllocator(JSContext *cx)
return bumpAlloc_;
}
RegExpPrivateCache *
JSRuntime::createRegExpPrivateCache(JSContext *cx)
RegExpCache *
JSRuntime::createRegExpCache(JSContext *cx)
{
JS_ASSERT(!repCache_);
JS_ASSERT(!reCache_);
JS_ASSERT(cx->runtime == this);
RegExpPrivateCache *newCache = new_<RegExpPrivateCache>(this);
RegExpCache *newCache = new_<RegExpCache>(this);
if (!newCache || !newCache->init()) {
js_ReportOutOfMemory(cx);
delete_<RegExpPrivateCache>(newCache);
delete_<RegExpCache>(newCache);
return NULL;
}
repCache_ = newCache;
return repCache_;
reCache_ = newCache;
return reCache_;
}
JSScript *
@ -1173,8 +1172,8 @@ JSRuntime::purge(JSContext *cx)
/* FIXME: bug 506341 */
propertyCache.purge(cx);
delete_<RegExpPrivateCache>(repCache_);
repCache_ = NULL;
delete_<RegExpCache>(reCache_);
reCache_ = NULL;
}
void

View File

@ -95,6 +95,11 @@ class InterpreterFrames;
class ScriptOpcodeCounts;
struct ScriptOpcodeCountsPair;
typedef HashMap<JSAtom *,
detail::RegExpCacheValue,
DefaultHasher<JSAtom *>,
RuntimeAllocPolicy> RegExpCache;
/*
* GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
* given pc in a script. We use the script->code pointer to tag the cache,
@ -214,11 +219,11 @@ struct JSRuntime : js::RuntimeFriendFields
*/
JSC::ExecutableAllocator *execAlloc_;
WTF::BumpPointerAllocator *bumpAlloc_;
js::RegExpPrivateCache *repCache_;
js::RegExpCache *reCache_;
JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
js::RegExpPrivateCache *createRegExpPrivateCache(JSContext *cx);
js::RegExpCache *createRegExpCache(JSContext *cx);
public:
JSC::ExecutableAllocator *getExecutableAllocator(JSContext *cx) {
@ -227,11 +232,11 @@ struct JSRuntime : js::RuntimeFriendFields
WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) {
return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx);
}
js::RegExpPrivateCache *maybeRegExpPrivateCache() {
return repCache_;
js::RegExpCache *maybeRegExpCache() {
return reCache_;
}
js::RegExpPrivateCache *getRegExpPrivateCache(JSContext *cx) {
return repCache_ ? repCache_ : createRegExpPrivateCache(cx);
js::RegExpCache *getRegExpCache(JSContext *cx) {
return reCache_ ? reCache_ : createRegExpCache(cx);
}
/* Base address of the native stack for the current thread. */

View File

@ -126,14 +126,14 @@ struct Class;
class RegExpObject;
class RegExpMatcher;
class RegExpObjectBuilder;
class RegExpShared;
class RegExpStatics;
class MatchPairs;
namespace detail {
class RegExpPrivate;
class RegExpPrivateCode;
class RegExpPrivateCacheValue;
class RegExpCode;
class RegExpCacheValue;
} /* namespace detail */
@ -221,12 +221,6 @@ class BreakpointSite;
class Debugger;
class WatchpointMap;
typedef HashMap<JSAtom *,
detail::RegExpPrivateCacheValue,
DefaultHasher<JSAtom *>,
RuntimeAllocPolicy>
RegExpPrivateCache;
/*
* Env is the type of what ES5 calls "lexical environments" (runtime
* activations of lexical scopes). This is currently just JSObject, and is

View File

@ -1315,43 +1315,6 @@ class FlatMatch
int32_t 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;
mutable RegExpMatcher matcher_;
RegExpObject *reobj_;
explicit RegExpPair(RegExpPair &) MOZ_DELETE;
void operator=(const RegExpPair &) MOZ_DELETE;
public:
explicit RegExpPair(JSContext *cx) : cx(cx), matcher_(cx) {}
bool reset(RegExpObject *reobj) {
reobj_ = reobj;
return matcher_.reset(reobj);
}
bool reset(JSLinearString *patstr, JSString *opt) {
reobj_ = NULL;
return matcher_.reset(patstr, opt);
}
bool null() const { return matcher_.null(); }
RegExpObject *reobj() const { return reobj_; }
RegExpMatcher &matcher() const {
JS_ASSERT(!null());
return matcher_;
}
};
/*
* RegExpGuard factors logic out of String regexp operations.
*
@ -1363,9 +1326,9 @@ class RegExpGuard
RegExpGuard(const RegExpGuard &) MOZ_DELETE;
void operator=(const RegExpGuard &) MOZ_DELETE;
JSContext *cx;
RegExpPair rep;
FlatMatch fm;
JSContext *cx;
RegExpMatcher matcher;
FlatMatch fm;
/*
* Upper bound on the number of characters we are willing to potentially
@ -1394,7 +1357,7 @@ class RegExpGuard
}
public:
explicit RegExpGuard(JSContext *cx) : cx(cx), rep(cx) {}
explicit RegExpGuard(JSContext *cx) : cx(cx), matcher(cx) {}
~RegExpGuard() {}
/* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
@ -1402,8 +1365,11 @@ class RegExpGuard
init(uintN argc, Value *vp, bool convertVoid = false)
{
if (argc != 0 && ValueIsRegExp(vp[2])) {
if (!rep.reset(&vp[2].toObject().asRegExp()))
RegExpObject &reobj = vp[2].toObject().asRegExp();
RegExpShared *shared = reobj.getShared(cx);
if (!shared)
return false;
matcher.init(NeedsIncRef<RegExpShared>(shared));
} else {
if (convertVoid && (argc == 0 || vp[2].isUndefined())) {
fm.patstr = cx->runtime->emptyString;
@ -1431,7 +1397,7 @@ class RegExpGuard
tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc,
bool checkMetaChars = true)
{
if (!rep.null())
if (matcher.initialized())
return NULL;
fm.pat = fm.patstr->chars();
@ -1461,11 +1427,11 @@ class RegExpGuard
}
/* If the pattern is not already a regular expression, make it so. */
const RegExpPair *
const RegExpMatcher *
normalizeRegExp(bool flat, uintN optarg, uintN argc, Value *vp)
{
if (!rep.null())
return &rep;
if (matcher.initialized())
return &matcher;
/* Build RegExp from pattern string. */
JSString *opt;
@ -1487,14 +1453,14 @@ class RegExpGuard
}
JS_ASSERT(patstr);
if (!rep.reset(patstr, opt))
if (!matcher.init(patstr, opt))
return NULL;
return &rep;
return &matcher;
}
#if DEBUG
bool hasRegExpPair() const { return !rep.null(); }
bool matcherInitialized() const { return matcher.initialized(); }
#endif
};
@ -1523,22 +1489,18 @@ enum MatchControlFlags {
/* Factor out looping and matching logic. */
static bool
DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &regExpPair,
DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpMatcher &matcher,
DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
{
RegExpMatcher &matcher = regExpPair.matcher();
JSLinearString *linearStr = str->ensureLinear(cx);
if (!linearStr)
return false;
const jschar *chars = linearStr->chars();
size_t length = linearStr->length();
if (matcher.global()) {
/* global matching ('g') */
RegExpExecType type = (flags & TEST_GLOBAL_BIT) ? RegExpTest : RegExpExec;
if (RegExpObject *reobj = regExpPair.reobj())
reobj->zeroLastIndex();
for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
if (!ExecuteRegExp(cx, res, matcher, linearStr, chars, length, &i, type, rval))
return false;
@ -1550,7 +1512,6 @@ DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &regE
++i;
}
} else {
/* single match */
RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec;
bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
size_t i = 0;
@ -1618,18 +1579,18 @@ js::str_match(JSContext *cx, uintN argc, Value *vp)
if (cx->isExceptionPending()) /* from tryFlatMatch */
return false;
const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
if (!rep)
const RegExpMatcher *matcher = g.normalizeRegExp(false, 1, argc, vp);
if (!matcher)
return false;
JSObject *array = NULL;
MatchArgType arg = &array;
RegExpStatics *res = cx->regExpStatics();
Value rval;
if (!DoMatch(cx, res, str, *rep, MatchCallback, arg, MATCH_ARGS, &rval))
if (!DoMatch(cx, res, str, *matcher, MatchCallback, arg, MATCH_ARGS, &rval))
return false;
if (rep->matcher().global())
if (matcher->global())
vp->setObjectOrNull(array);
else
*vp = rval;
@ -1650,22 +1611,25 @@ js::str_search(JSContext *cx, uintN argc, Value *vp)
vp->setInt32(fm->match());
return true;
}
if (cx->isExceptionPending()) /* from tryFlatMatch */
return false;
const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
if (!rep)
const RegExpMatcher *matcher = g.normalizeRegExp(false, 1, argc, vp);
if (!matcher)
return false;
JSLinearString *linearStr = str->ensureLinear(cx);
if (!linearStr)
return false;
const jschar *chars = linearStr->chars();
size_t length = linearStr->length();
RegExpStatics *res = cx->regExpStatics();
/* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
size_t i = 0;
if (!ExecuteRegExp(cx, res, rep->matcher(), linearStr, chars, length, &i, RegExpTest, vp))
if (!ExecuteRegExp(cx, res, *matcher, linearStr, chars, length, &i, RegExpTest, vp))
return false;
if (vp->isTrue())
@ -2106,8 +2070,8 @@ BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *reps
static inline bool
str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata)
{
const RegExpPair *rep = rdata.g.normalizeRegExp(true, 2, argc, vp);
if (!rep)
const RegExpMatcher *matcher = rdata.g.normalizeRegExp(true, 2, argc, vp);
if (!matcher)
return false;
rdata.leftIndex = 0;
@ -2115,7 +2079,7 @@ str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata)
RegExpStatics *res = cx->regExpStatics();
Value tmp;
if (!DoMatch(cx, res, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
if (!DoMatch(cx, res, rdata.str, *matcher, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
return false;
if (!rdata.calledBack) {
@ -2270,7 +2234,7 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp)
if (!fm) {
if (cx->isExceptionPending()) /* oom in RopeMatch in tryFlatMatch */
return false;
JS_ASSERT_IF(!rdata.g.hasRegExpPair(), argc > optarg);
JS_ASSERT_IF(!rdata.g.matcherInitialized(), argc > optarg);
return str_replace_regexp(cx, argc, vp, rdata);
}
@ -2461,11 +2425,7 @@ class SplitRegExpMatcher {
inline bool operator()(JSContext *cx, JSLinearString *str, size_t index,
SplitMatchResult *result) {
Value rval
#ifdef __GNUC__ /* quell GCC overwarning */
= UndefinedValue()
#endif
;
Value rval = UndefinedValue();
const jschar *chars = str->chars();
size_t length = str->length();
if (!ExecuteRegExp(cx, res, matcher, str, chars, length, &index, RegExpTest, &rval))
@ -2537,8 +2497,11 @@ js::str_split(JSContext *cx, uintN argc, Value *vp)
bool sepUndefined = (argc == 0 || vp[2].isUndefined());
if (!sepUndefined) {
if (ValueIsRegExp(vp[2])) {
if (!matcher.reset(&vp[2].toObject().asRegExp()))
RegExpObject &reobj = vp[2].toObject().asRegExp();
RegExpShared *shared = reobj.getShared(cx);
if (!shared)
return false;
matcher.init(NeedsIncRef<RegExpShared>(shared));
} else {
JSString *sep = ToString(cx, vp[2]);
if (!sep)
@ -2577,7 +2540,7 @@ js::str_split(JSContext *cx, uintN argc, Value *vp)
/* Steps 11-15. */
JSObject *aobj;
if (matcher.null()) {
if (!matcher.initialized()) {
// NB: sepstr is anchored through its storage in vp[2].
aobj = SplitHelper(cx, strlin, limit, SplitStringMatcher(sepstr), type);
} else {

View File

@ -6944,7 +6944,7 @@ mjit::Compiler::jsop_regexp()
* JIT code is discarded on every GC, which permits us to burn in
* the pointer to the RegExpPrivate refcount.
*/
if (!reobj->makePrivateNow(cx))
if (!reobj->getShared(cx))
return false;
RegisterID result = frame.allocReg();

View File

@ -93,7 +93,7 @@ class MatchPairs
int *buffer() { return buffer_; }
friend class detail::RegExpPrivate;
friend class RegExpShared;
public:
/*

View File

@ -109,14 +109,7 @@ RegExpObject::startsWithAtomizedGreedyStar() const
inline size_t *
RegExpObject::addressOfPrivateRefCount() const
{
JS_ASSERT(getPrivate());
return getPrivate()->addressOfRefCount();
}
inline void
RegExpObject::setPrivate(RegExpPrivate *rep)
{
JSObject::setPrivate(rep);
return shared().addressOfRefCount();
}
inline RegExpObject *
@ -142,7 +135,7 @@ inline RegExpObject *
RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags,
TokenStream *tokenStream)
{
if (!RegExpPrivateCode::checkSyntax(cx, tokenStream, source))
if (!RegExpCode::checkSyntax(cx, tokenStream, source))
return NULL;
RegExpObjectBuilder builder(cx);
@ -152,9 +145,9 @@ RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags,
inline void
RegExpObject::purge(JSContext *cx)
{
if (RegExpPrivate *rep = getPrivate()) {
rep->decref(cx);
setPrivate(NULL);
if (RegExpShared *shared = maybeShared()) {
shared->decref(cx);
JSObject::setPrivate(NULL);
}
}
@ -163,7 +156,7 @@ RegExpObject::finalize(JSContext *cx)
{
purge(cx);
#ifdef DEBUG
setPrivate((RegExpPrivate *) 0x1); /* Non-null but still in the zero page. */
JSObject::setPrivate((void *) 0x1); /* Non-null but still in the zero page. */
#endif
}
@ -193,7 +186,7 @@ RegExpObject::init(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());
JS_ASSERT(!maybeShared());
zeroLastIndex();
setSource(source);
setGlobal(flags & GlobalFlag);
@ -203,14 +196,19 @@ RegExpObject::init(JSContext *cx, JSLinearString *source, RegExpFlag flags)
return true;
}
inline bool
RegExpMatcher::reset(JSLinearString *patstr, JSString *opt)
inline void
RegExpMatcher::init(NeedsIncRef<RegExpShared> shared)
{
AlreadyIncRefed<RegExpPrivate> priv = RegExpPrivate::create(cx, patstr, opt, NULL);
if (!priv)
return false;
arc.reset(priv);
return true;
JS_ASSERT(!shared_);
shared_.reset(shared);
}
inline bool
RegExpMatcher::init(JSLinearString *patstr, JSString *opt)
{
JS_ASSERT(!shared_);
shared_.reset(RegExpShared::create(cx_, patstr, opt, NULL));
return !!shared_;
}
inline void
@ -261,23 +259,22 @@ RegExpObject::setSticky(bool enabled)
setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled));
}
/* RegExpPrivate inlines. */
/* RegExpShared inlines. */
inline bool
detail::RegExpPrivate::cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
RegExpPrivateCacheKind targetKind,
AlreadyIncRefed<RegExpPrivate> *result)
RegExpShared::cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
RegExpCacheKind targetKind,
AlreadyIncRefed<RegExpShared> *result)
{
RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx);
RegExpCache *cache = cx->runtime->getRegExpCache(cx);
if (!cache)
return false;
if (RegExpPrivateCache::Ptr p = cache->lookup(atom)) {
RegExpPrivateCacheValue &cacheValue = p->value;
if (cacheValue.kind() == targetKind && cacheValue.rep()->getFlags() == flags) {
NeedsIncRef<RegExpPrivate> cached(cacheValue.rep());
cached->incref(cx);
*result = AlreadyIncRefed<RegExpPrivate>(cached.get());
if (RegExpCache::Ptr p = cache->lookup(atom)) {
RegExpCacheValue &cacheValue = p->value;
if (cacheValue.kind() == targetKind && cacheValue.shared().getFlags() == flags) {
cacheValue.shared().incref(cx);
*result = AlreadyIncRefed<RegExpShared>(&cacheValue.shared());
return true;
}
}
@ -287,27 +284,25 @@ detail::RegExpPrivate::cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags
}
inline bool
detail::RegExpPrivate::cacheInsert(JSContext *cx, JSAtom *atom, RegExpPrivateCacheKind kind,
RegExpPrivate *priv)
RegExpShared::cacheInsert(JSContext *cx, JSAtom *atom, RegExpCacheKind kind,
RegExpShared &shared)
{
JS_ASSERT(priv);
/*
* Note: allocation performed since lookup may cause a garbage collection,
* so we have to re-lookup the cache (and inside the cache) after the
* allocation is performed.
*/
RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx);
RegExpCache *cache = cx->runtime->getRegExpCache(cx);
if (!cache)
return false;
if (RegExpPrivateCache::AddPtr addPtr = cache->lookupForAdd(atom)) {
if (RegExpCache::AddPtr addPtr = cache->lookupForAdd(atom)) {
/* We clobber existing entries with the same source (but different flags or kind). */
JS_ASSERT(addPtr->value.rep()->getFlags() != priv->getFlags() ||
JS_ASSERT(addPtr->value.shared().getFlags() != shared.getFlags() ||
addPtr->value.kind() != kind);
addPtr->value.reset(priv, kind);
addPtr->value.reset(shared, kind);
} else {
if (!cache->add(addPtr, atom, RegExpPrivateCacheValue(priv, kind))) {
if (!cache->add(addPtr, atom, RegExpCacheValue(shared, kind))) {
js_ReportOutOfMemory(cx);
return false;
}
@ -316,50 +311,48 @@ detail::RegExpPrivate::cacheInsert(JSContext *cx, JSAtom *atom, RegExpPrivateCac
return true;
}
inline AlreadyIncRefed<detail::RegExpPrivate>
detail::RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags,
TokenStream *ts)
inline AlreadyIncRefed<RegExpShared>
RegExpShared::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts)
{
typedef AlreadyIncRefed<RegExpPrivate> RetType;
typedef AlreadyIncRefed<RegExpShared> RetType;
/*
* We choose to only cache |RegExpPrivate|s who have atoms as
* We choose to only cache |RegExpShared|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();
if (!cacheable)
return RetType(RegExpPrivate::createUncached(cx, source, flags, ts));
return RetType(RegExpShared::createUncached(cx, source, flags, ts));
/*
* 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.
* Refcount note: not all |RegExpShared|s are cached so we need to keep a
* refcount. The cache holds a "weak ref", where the |RegExpShared|'s
* deallocation decref will first cause it to remove itself from the cache.
*/
JSAtom *sourceAtom = &source->asAtom();
AlreadyIncRefed<RegExpPrivate> cached;
if (!cacheLookup(cx, sourceAtom, flags, RegExpPrivateCache_ExecCapable, &cached))
AlreadyIncRefed<RegExpShared> cached;
if (!cacheLookup(cx, sourceAtom, flags, detail::RegExpCache_ExecCapable, &cached))
return RetType(NULL);
if (cached)
return cached;
RegExpPrivate *priv = RegExpPrivate::createUncached(cx, source, flags, ts);
if (!priv)
RegExpShared *shared = RegExpShared::createUncached(cx, source, flags, ts);
if (!shared)
return RetType(NULL);
if (!cacheInsert(cx, sourceAtom, RegExpPrivateCache_ExecCapable, priv))
if (!cacheInsert(cx, sourceAtom, detail::RegExpCache_ExecCapable, *shared))
return RetType(NULL);
return RetType(priv);
return RetType(shared);
}
/* This function should be deleted once bad Android platforms phase out. See bug 604774. */
inline bool
detail::RegExpPrivateCode::isJITRuntimeEnabled(JSContext *cx)
detail::RegExpCode::isJITRuntimeEnabled(JSContext *cx)
{
#if defined(ANDROID) && defined(JS_METHODJIT)
return cx->methodJitEnabled;
@ -369,8 +362,8 @@ detail::RegExpPrivateCode::isJITRuntimeEnabled(JSContext *cx)
}
inline bool
detail::RegExpPrivateCode::compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts,
uintN *parenCount, RegExpFlag flags)
detail::RegExpCode::compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts,
uintN *parenCount, RegExpFlag flags)
{
#if ENABLE_YARR_JIT
/* Parse the pattern. */
@ -428,7 +421,7 @@ detail::RegExpPrivateCode::compile(JSContext *cx, JSLinearString &pattern, Token
}
inline bool
detail::RegExpPrivate::compile(JSContext *cx, TokenStream *ts)
RegExpShared::compile(JSContext *cx, TokenStream *ts)
{
if (!sticky())
return code.compile(cx, *source, ts, &parenCount, getFlags());
@ -455,8 +448,8 @@ detail::RegExpPrivate::compile(JSContext *cx, TokenStream *ts)
}
inline RegExpRunStatus
detail::RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
int *output, size_t outputCount)
detail::RegExpCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
int *output, size_t outputCount)
{
int result;
#if ENABLE_YARR_JIT
@ -484,33 +477,28 @@ detail::RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t le
}
inline void
detail::RegExpPrivate::incref(JSContext *cx)
RegExpShared::incref(JSContext *cx)
{
++refCount;
}
inline void
detail::RegExpPrivate::decref(JSContext *cx)
RegExpShared::decref(JSContext *cx)
{
#ifdef JS_THREADSAFE
JS_OPT_ASSERT_IF(cx->runtime->gcHelperThread.getThread(),
PR_GetCurrentThread() != cx->runtime->gcHelperThread.getThread());
#endif
if (--refCount != 0)
return;
if (RegExpPrivateCache *cache = cx->runtime->maybeRegExpPrivateCache()) {
if (RegExpCache *cache = cx->runtime->maybeRegExpCache()) {
if (source->isAtom()) {
if (RegExpPrivateCache::Ptr p = cache->lookup(&source->asAtom())) {
if (p->value.rep() == this)
if (RegExpCache::Ptr p = cache->lookup(&source->asAtom())) {
if (&p->value.shared() == this)
cache->remove(p);
}
}
}
#ifdef DEBUG
this->~RegExpPrivate();
this->~RegExpShared();
memset(this, 0xcd, sizeof(*this));
cx->free_(this);
#else

View File

@ -49,8 +49,7 @@
#include "vm/RegExpStatics-inl.h"
using namespace js;
using js::detail::RegExpPrivate;
using js::detail::RegExpPrivateCode;
using js::detail::RegExpCode;
JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
@ -60,29 +59,23 @@ JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
/* RegExpMatcher */
bool
RegExpMatcher::resetWithTestOptimized(RegExpObject *reobj)
RegExpMatcher::initWithTestOptimized(RegExpObject &reobj)
{
JS_ASSERT(reobj->startsWithAtomizedGreedyStar());
JS_ASSERT(reobj.startsWithAtomizedGreedyStar());
JS_ASSERT(!shared_);
JSAtom *source = &reobj->getSource()->asAtom();
AlreadyIncRefed<RegExpPrivate> priv =
RegExpPrivate::createTestOptimized(cx, source, reobj->getFlags());
if (!priv)
JSAtom *source = &reobj.getSource()->asAtom();
shared_.reset(RegExpShared::createTestOptimized(cx_, source, reobj.getFlags()));
if (!shared_)
return false;
/*
* Create a dummy RegExpObject to persist this RegExpPrivate until the next GC.
* Create a dummy RegExpObject to persist this RegExpShared until the next GC.
* Note that we give the ref we have to this new object.
*/
RegExpObjectBuilder builder(cx);
RegExpObject *dummy = builder.build(priv);
if (!dummy) {
priv->decref(cx);
return false;
}
arc.reset(NeedsIncRef<RegExpPrivate>(priv.get()));
return true;
RegExpObjectBuilder builder(cx_);
shared_->incref(cx_);
return !!builder.build(AlreadyIncRefed<RegExpShared>(shared_.get()));
}
/* RegExpObjectBuilder */
@ -117,19 +110,19 @@ RegExpObjectBuilder::getOrCreateClone(RegExpObject *proto)
}
RegExpObject *
RegExpObjectBuilder::build(AlreadyIncRefed<RegExpPrivate> rep)
RegExpObjectBuilder::build(AlreadyIncRefed<RegExpShared> shared)
{
if (!getOrCreate()) {
rep->decref(cx);
shared->decref(cx);
return NULL;
}
reobj_->purge(cx);
if (!reobj_->init(cx, rep->getSource(), rep->getFlags())) {
rep->decref(cx);
if (!reobj_->init(cx, shared->getSource(), shared->getFlags())) {
shared->decref(cx);
return NULL;
}
reobj_->setPrivate(rep.get());
reobj_->setPrivate(shared.get());
return reobj_;
}
@ -147,13 +140,13 @@ RegExpObjectBuilder::build(JSLinearString *source, RegExpFlag flags)
RegExpObject *
RegExpObjectBuilder::build(RegExpObject *other)
{
RegExpPrivate *rep = other->getOrCreatePrivate(cx);
if (!rep)
RegExpShared *shared = other->getShared(cx);
if (!shared)
return NULL;
/* Now, incref it for the RegExpObject being built. */
rep->incref(cx);
return build(AlreadyIncRefed<RegExpPrivate>(rep));
shared->incref(cx);
return build(AlreadyIncRefed<RegExpShared>(shared));
}
RegExpObject *
@ -163,9 +156,9 @@ RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto)
return NULL;
/*
* Check that the RegExpPrivate for the original is okay to use in
* Check that the RegExpShared for the original is okay to use in
* the clone -- if the |RegExpStatics| provides more flags we'll
* need a different |RegExpPrivate|.
* need a different |RegExpShared|.
*/
RegExpStatics *res = cx->regExpStatics();
RegExpFlag origFlags = other->getFlags();
@ -175,12 +168,12 @@ RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto)
return build(other->getSource(), newFlags);
}
RegExpPrivate *toShare = other->getOrCreatePrivate(cx);
RegExpShared *toShare = other->getShared(cx);
if (!toShare)
return NULL;
toShare->incref(cx);
return build(AlreadyIncRefed<RegExpPrivate>(toShare));
return build(AlreadyIncRefed<RegExpShared>(toShare));
}
/* MatchPairs */
@ -210,11 +203,11 @@ MatchPairs::checkAgainst(size_t inputLength)
}
RegExpRunStatus
RegExpPrivate::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
LifoAllocScope &allocScope, MatchPairs **output)
RegExpShared::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
LifoAllocScope &allocScope, MatchPairs **output)
{
const size_t origLength = length;
size_t backingPairCount = RegExpPrivateCode::getOutputSize(pairCount());
size_t backingPairCount = RegExpCode::getOutputSize(pairCount());
MatchPairs *matchPairs = MatchPairs::create(allocScope.alloc(), pairCount(), backingPairCount);
if (!matchPairs)
@ -256,26 +249,26 @@ RegExpPrivate::execute(JSContext *cx, const jschar *chars, size_t length, size_t
return RegExpRunStatus_Success;
}
RegExpPrivate *
RegExpObject::makePrivate(JSContext *cx)
RegExpShared *
RegExpObject::createShared(JSContext *cx)
{
JS_ASSERT(!getPrivate());
AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, getSource(), getFlags(), NULL);
if (!rep)
JS_ASSERT(!maybeShared());
AlreadyIncRefed<RegExpShared> shared = RegExpShared::create(cx, getSource(), getFlags(), NULL);
if (!shared)
return NULL;
setPrivate(rep.get());
return rep.get();
setPrivate(shared.get());
return shared.get();
}
RegExpRunStatus
RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
LifoAllocScope &allocScope, MatchPairs **output)
{
if (!getPrivate() && !makePrivate(cx))
RegExpShared *shared = getShared(cx);
if (!shared)
return RegExpRunStatus_Error;
return getPrivate()->execute(cx, chars, length, lastIndex, allocScope, output);
return shared->execute(cx, chars, length, lastIndex, allocScope, output);
}
Shape *
@ -394,7 +387,7 @@ Class js::RegExpClass = {
#if ENABLE_YARR_JIT
void
RegExpPrivateCode::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error)
RegExpCode::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error)
{
switch (error) {
case JSC::Yarr::NoError:
@ -427,7 +420,7 @@ RegExpPrivateCode::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode err
#else /* !ENABLE_YARR_JIT */
void
RegExpPrivateCode::reportPCREError(JSContext *cx, int error)
RegExpCode::reportPCREError(JSContext *cx, int error)
{
#define REPORT(msg_) \
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, msg_); \
@ -497,35 +490,36 @@ js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut)
return true;
}
RegExpPrivate *
RegExpPrivate::createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
TokenStream *tokenStream)
RegExpShared *
RegExpShared::createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
TokenStream *tokenStream)
{
RegExpPrivate *priv = cx->new_<RegExpPrivate>(source, flags);
if (!priv)
RegExpShared *shared = cx->new_<RegExpShared>(source, flags);
if (!shared)
return NULL;
if (!priv->compile(cx, tokenStream)) {
Foreground::delete_(priv);
if (!shared->compile(cx, tokenStream)) {
Foreground::delete_(shared);
return NULL;
}
return priv;
return shared;
}
AlreadyIncRefed<RegExpPrivate>
RegExpPrivate::createTestOptimized(JSContext *cx, JSAtom *cacheKey, RegExpFlag flags)
AlreadyIncRefed<RegExpShared>
RegExpShared::createTestOptimized(JSContext *cx, JSAtom *cacheKey, RegExpFlag flags)
{
typedef AlreadyIncRefed<RegExpPrivate> RetType;
using namespace detail;
typedef AlreadyIncRefed<RegExpShared> RetType;
RetType cached;
if (!cacheLookup(cx, cacheKey, flags, RegExpPrivateCache_TestOptimized, &cached))
if (!cacheLookup(cx, cacheKey, flags, RegExpCache_TestOptimized, &cached))
return RetType(NULL);
if (cached)
return cached;
/* Strip off the greedy star characters, create a new RegExpPrivate, and cache. */
/* Strip off the greedy star characters, create a new RegExpShared, and cache. */
JS_ASSERT(cacheKey->length() > JS_ARRAY_LENGTH(GreedyStarChars));
JSDependentString *stripped =
JSDependentString::new_(cx, cacheKey, cacheKey->chars() + JS_ARRAY_LENGTH(GreedyStarChars),
@ -533,27 +527,27 @@ RegExpPrivate::createTestOptimized(JSContext *cx, JSAtom *cacheKey, RegExpFlag f
if (!stripped)
return RetType(NULL);
RegExpPrivate *priv = createUncached(cx, cacheKey, flags, NULL);
if (!priv)
RegExpShared *shared = createUncached(cx, cacheKey, flags, NULL);
if (!shared)
return RetType(NULL);
if (!cacheInsert(cx, cacheKey, RegExpPrivateCache_TestOptimized, priv)) {
priv->decref(cx);
if (!cacheInsert(cx, cacheKey, RegExpCache_TestOptimized, *shared)) {
shared->decref(cx);
return RetType(NULL);
}
return RetType(priv);
return RetType(shared);
}
AlreadyIncRefed<RegExpPrivate>
RegExpPrivate::create(JSContext *cx, JSLinearString *str, JSString *opt, TokenStream *ts)
AlreadyIncRefed<RegExpShared>
RegExpShared::create(JSContext *cx, JSLinearString *str, JSString *opt, TokenStream *ts)
{
if (!opt)
return create(cx, str, RegExpFlag(0), ts);
RegExpFlag flags = RegExpFlag(0);
if (!ParseRegExpFlags(cx, opt, &flags))
return AlreadyIncRefed<RegExpPrivate>(NULL);
return AlreadyIncRefed<RegExpShared>(NULL);
return create(cx, str, flags, ts);
}

View File

@ -67,8 +67,7 @@ enum RegExpRunStatus
class RegExpObject : public JSObject
{
typedef detail::RegExpPrivate RegExpPrivate;
typedef detail::RegExpPrivateCode RegExpPrivateCode;
typedef detail::RegExpCode RegExpCode;
static const uintN LAST_INDEX_SLOT = 0;
static const uintN SOURCE_SLOT = 1;
@ -155,15 +154,22 @@ class RegExpObject : public JSObject
inline void finalize(JSContext *cx);
/* Clear out lazy |RegExpPrivate|. */
/* Clear out lazy |RegExpShared|. */
inline void purge(JSContext *x);
/*
* Trigger an eager creation of the associated RegExpPrivate. Note
* that a GC may purge it away.
*/
bool makePrivateNow(JSContext *cx) {
return getPrivate() ? true : !!makePrivate(cx);
RegExpShared &shared() const {
JS_ASSERT(JSObject::getPrivate() != NULL);
return *static_cast<RegExpShared *>(JSObject::getPrivate());
}
RegExpShared *maybeShared() {
return static_cast<RegExpShared *>(JSObject::getPrivate());
}
RegExpShared *getShared(JSContext *cx) {
if (RegExpShared *shared = maybeShared())
return shared;
return createShared(cx);
}
private:
@ -172,28 +178,14 @@ class RegExpObject : public JSObject
inline bool init(JSContext *cx, JSLinearString *source, RegExpFlag flags);
RegExpPrivate *getOrCreatePrivate(JSContext *cx) {
if (RegExpPrivate *rep = getPrivate())
return rep;
return makePrivate(cx);
}
/* The |RegExpPrivate| is lazily created at the time of use. */
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.
*/
RegExpPrivate *makePrivate(JSContext *cx);
RegExpShared *createShared(JSContext *cx);
friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSLinearString *, RegExpFlag);
friend bool ResetRegExpObject(JSContext *, RegExpObject *, AlreadyIncRefed<RegExpPrivate>);
friend bool ResetRegExpObject(JSContext *, RegExpObject *, AlreadyIncRefed<RegExpShared>);
/*
* Compute the initial shape to associate with fresh RegExp objects,
@ -209,15 +201,13 @@ class RegExpObject : public JSObject
/* Either builds a new RegExpObject or re-initializes an existing one. */
class RegExpObjectBuilder
{
typedef detail::RegExpPrivate RegExpPrivate;
JSContext *cx;
RegExpObject *reobj_;
bool getOrCreate();
bool getOrCreateClone(RegExpObject *proto);
RegExpObject *build(AlreadyIncRefed<RegExpPrivate> rep);
RegExpObject *build(AlreadyIncRefed<RegExpShared> shared);
friend class RegExpMatcher;
@ -239,8 +229,8 @@ namespace detail {
static const jschar GreedyStarChars[] = {'.', '*'};
/* Abstracts away the gross |RegExpPrivate| backend details. */
class RegExpPrivateCode
/* Abstracts away the gross |RegExpShared| backend details. */
class RegExpCode
{
#if ENABLE_YARR_JIT
typedef JSC::Yarr::BytecodePattern BytecodePattern;
@ -257,7 +247,7 @@ class RegExpPrivateCode
#endif
public:
RegExpPrivateCode()
RegExpCode()
:
#if ENABLE_YARR_JIT
codeBlock(),
@ -267,7 +257,7 @@ class RegExpPrivateCode
#endif
{ }
~RegExpPrivateCode() {
~RegExpCode() {
#if ENABLE_YARR_JIT
codeBlock.release();
if (byteCode)
@ -314,68 +304,70 @@ class RegExpPrivateCode
int *output, size_t outputCount);
};
enum RegExpPrivateCacheKind
enum RegExpCacheKind
{
RegExpPrivateCache_TestOptimized,
RegExpPrivateCache_ExecCapable
RegExpCache_TestOptimized,
RegExpCache_ExecCapable
};
class RegExpPrivateCacheValue
class RegExpCacheValue
{
union {
RegExpPrivate *rep_;
RegExpShared *shared_;
uintptr_t bits;
};
public:
RegExpPrivateCacheValue() : rep_(NULL) {}
RegExpCacheValue() : shared_(NULL) {}
RegExpPrivateCacheValue(RegExpPrivate *rep, RegExpPrivateCacheKind kind) {
reset(rep, kind);
RegExpCacheValue(RegExpShared &shared, RegExpCacheKind kind) {
reset(shared, kind);
}
RegExpPrivateCacheKind kind() const {
RegExpCacheKind kind() const {
return (bits & 0x1)
? RegExpPrivateCache_TestOptimized
: RegExpPrivateCache_ExecCapable;
? RegExpCache_TestOptimized
: RegExpCache_ExecCapable;
}
RegExpPrivate *rep() {
return reinterpret_cast<RegExpPrivate *>(bits & ~uintptr_t(1));
RegExpShared &shared() {
return *reinterpret_cast<RegExpShared *>(bits & ~uintptr_t(1));
}
void reset(RegExpPrivate *rep, RegExpPrivateCacheKind kind) {
rep_ = rep;
if (kind == RegExpPrivateCache_TestOptimized)
void reset(RegExpShared &shared, RegExpCacheKind kind) {
shared_ = &shared;
if (kind == RegExpCache_TestOptimized)
bits |= 0x1;
JS_ASSERT(this->kind() == kind);
}
};
} /* namespace detail */
/*
* The "meat" of the builtin regular expression objects: it contains the
* mini-program that represents the source of the regular expression.
* Excepting refcounts, this is an immutable datastructure after
* compilation.
*
* Non-atomic refcounting is used, so single-thread invariants must be
* 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
* memory.
*/
class RegExpPrivate
class RegExpShared
{
RegExpPrivateCode code;
JSLinearString *source;
size_t refCount;
uintN parenCount;
RegExpFlag flags;
typedef detail::RegExpCode RegExpCode;
typedef detail::RegExpCacheKind RegExpCacheKind;
typedef detail::RegExpCacheValue RegExpCacheValue;
RegExpCode code;
JSLinearString *source;
size_t refCount;
uintN parenCount;
RegExpFlag flags;
private:
RegExpPrivate(JSLinearString *source, RegExpFlag flags)
RegExpShared(JSLinearString *source, RegExpFlag flags)
: source(source), refCount(1), parenCount(0), flags(flags)
{ }
@ -384,23 +376,23 @@ class RegExpPrivate
bool compile(JSContext *cx, TokenStream *ts);
static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
static RegExpPrivate *
static RegExpShared *
createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
TokenStream *tokenStream);
static bool cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
RegExpPrivateCacheKind kind, AlreadyIncRefed<RegExpPrivate> *result);
RegExpCacheKind kind, AlreadyIncRefed<RegExpShared> *result);
static bool cacheInsert(JSContext *cx, JSAtom *atom,
RegExpPrivateCacheKind kind, RegExpPrivate *priv);
RegExpCacheKind kind, RegExpShared &shared);
public:
static AlreadyIncRefed<RegExpPrivate>
static AlreadyIncRefed<RegExpShared>
create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts);
static AlreadyIncRefed<RegExpPrivate>
static AlreadyIncRefed<RegExpShared>
create(JSContext *cx, JSLinearString *source, JSString *flags, TokenStream *ts);
static AlreadyIncRefed<RegExpPrivate>
static AlreadyIncRefed<RegExpShared>
createTestOptimized(JSContext *cx, JSAtom *originalSource, RegExpFlag flags);
RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
@ -429,54 +421,42 @@ class RegExpPrivate
bool sticky() const { return flags & StickyFlag; }
};
} /* namespace detail */
/*
* Wraps the RegExpObject's internals in a recount-safe interface for
* use in RegExp execution. This is used in situations where we'd like
* to avoid creating a full-fledged RegExpObject. This interface is
* provided in lieu of exposing the RegExpPrivate directly.
* provided in lieu of exposing the RegExpShared directly.
*
* Note: this exposes precisely the execute interface of a RegExpObject.
*/
class RegExpMatcher
{
typedef detail::RegExpPrivate RegExpPrivate;
JSContext *cx;
AutoRefCount<RegExpPrivate> arc;
JSContext *cx_;
AutoRefCount<RegExpShared> shared_;
public:
explicit RegExpMatcher(JSContext *cx)
: cx(cx), arc(cx)
{ }
explicit RegExpMatcher(JSContext *cx) : cx_(cx), shared_(cx) {
JS_ASSERT(!initialized());
}
bool null() const {
return arc.null();
bool initialized() const {
return !shared_.null();
}
bool global() const {
return arc.get()->global();
return shared_->global();
}
bool sticky() const {
return arc.get()->sticky();
return shared_->sticky();
}
bool reset(RegExpObject *reobj) {
RegExpPrivate *priv = reobj->getOrCreatePrivate(cx);
if (!priv)
return false;
arc.reset(NeedsIncRef<RegExpPrivate>(priv));
return true;
}
inline bool reset(JSLinearString *patstr, JSString *opt);
bool resetWithTestOptimized(RegExpObject *reobj);
inline void init(NeedsIncRef<RegExpShared> shared);
inline bool init(JSLinearString *patstr, JSString *opt);
bool initWithTestOptimized(RegExpObject &reobj);
RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
LifoAllocScope &allocScope, MatchPairs **output) {
JS_ASSERT(!arc.null());
return arc.get()->execute(cx, chars, length, lastIndex, allocScope, output);
LifoAllocScope &allocScope, MatchPairs **output) const {
JS_ASSERT(initialized());
return shared_->execute(cx, chars, length, lastIndex, allocScope, output);
}
};
@ -498,7 +478,7 @@ IsRegExpMetaChar(jschar c);
inline bool
CheckRegExpSyntax(JSContext *cx, JSLinearString *str)
{
return detail::RegExpPrivateCode::checkSyntax(cx, NULL, str);
return detail::RegExpCode::checkSyntax(cx, NULL, str);
}
} /* namespace js */