mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
b78ff5f631
commit
1f69b3025f
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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! */
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -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
|
||||
|
111
js/src/jsstr.cpp
111
js/src/jsstr.cpp
@ -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 ®ExpPair,
|
||||
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 ®E
|
||||
++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 {
|
||||
|
@ -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();
|
||||
|
@ -93,7 +93,7 @@ class MatchPairs
|
||||
|
||||
int *buffer() { return buffer_; }
|
||||
|
||||
friend class detail::RegExpPrivate;
|
||||
friend class RegExpShared;
|
||||
|
||||
public:
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user