Bug 808245, Part 5/6 - Use MatchPairs for RegExp output. r=dvander

This commit is contained in:
Sean Stangl 2012-12-12 17:42:02 -08:00
parent c6da174664
commit 492a7dd84f
13 changed files with 571 additions and 425 deletions

View File

@ -50,9 +50,9 @@ class RegExpMatchBuilder
}
};
static bool
CreateRegExpMatchResult(JSContext *cx, JSString *input_, StableCharPtr chars, size_t length,
MatchPairs *matchPairs, Value *rval)
bool
js::CreateRegExpMatchResult(JSContext *cx, JSString *input_, StableCharPtr chars, size_t length,
MatchPairs &matches, Value *rval)
{
RootedString input(cx, input_);
@ -78,8 +78,11 @@ CreateRegExpMatchResult(JSContext *cx, JSString *input_, StableCharPtr chars, si
RegExpMatchBuilder builder(cx, array);
RootedValue undefinedValue(cx, UndefinedValue());
for (size_t i = 0; i < matchPairs->pairCount(); ++i) {
MatchPair pair = matchPairs->pair(i);
size_t numPairs = matches.length();
JS_ASSERT(numPairs > 0);
for (size_t i = 0; i < numPairs; ++i) {
const MatchPair &pair = matches[i];
JSString *captured;
if (pair.isUndefined()) {
@ -94,61 +97,72 @@ CreateRegExpMatchResult(JSContext *cx, JSString *input_, StableCharPtr chars, si
}
}
if (!builder.setIndex(matchPairs->pair(0).start) || !builder.setInput(input))
if (!builder.setIndex(matches[0].start) || !builder.setInput(input))
return false;
*rval = ObjectValue(*array);
return true;
}
template <class T>
bool
ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T &re, JSLinearString *input,
StableCharPtr chars, size_t length,
size_t *lastIndex, RegExpExecType type, Value *rval)
js::CreateRegExpMatchResult(JSContext *cx, HandleString string, MatchPairs &matches, Value *rval)
{
LifoAllocScope allocScope(&cx->tempLifoAlloc());
MatchPairs *matchPairs = NULL;
RegExpRunStatus status = re.execute(cx, chars, length, lastIndex, &matchPairs);
switch (status) {
case RegExpRunStatus_Error:
Rooted<JSStableString*> input(cx, string->ensureStable(cx));
if (!input)
return false;
case RegExpRunStatus_Success_NotFound:
*rval = NullValue();
return true;
default:
JS_ASSERT(status == RegExpRunStatus_Success);
JS_ASSERT(matchPairs);
}
return CreateRegExpMatchResult(cx, input, input->chars(), input->length(), matches, rval);
}
if (res)
res->updateFromMatchPairs(cx, input, matchPairs);
RegExpRunStatus
ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re, RegExpObject &regexp,
JSLinearString *input, StableCharPtr chars, size_t length,
size_t *lastIndex, MatchConduit &matches)
{
RegExpRunStatus status;
/* Ahem, not handled in this patch. But it was a pain to rip out. */
JS_ASSERT(!matches.isPair);
*lastIndex = matchPairs->pair(0).limit;
/* Vector of MatchPairs provided: execute full regexp. */
status = re.execute(cx, chars, length, lastIndex, *matches.u.pairs);
if (status == RegExpRunStatus_Success && res)
res->updateFromMatchPairs(cx, input, *matches.u.pairs);
if (type == RegExpTest) {
*rval = BooleanValue(true);
return status;
}
/* Legacy ExecuteRegExp behavior is baked into the JSAPI. */
bool
js::ExecuteRegExpLegacy(JSContext *cx, RegExpStatics *res, RegExpObject &reobj,
Handle<JSStableString*> input, StableCharPtr chars, size_t length,
size_t *lastIndex, JSBool test, jsval *rval)
{
RegExpGuard shared;
if (!reobj.getShared(cx, &shared))
return false;
ScopedMatchPairs matches(&cx->tempLifoAlloc());
MatchConduit conduit(&matches);
RegExpRunStatus status =
ExecuteRegExpImpl(cx, res, *shared, reobj, input, chars, length, lastIndex, conduit);
if (status == RegExpRunStatus_Error)
return false;
if (status == RegExpRunStatus_Success_NotFound) {
/* ExecuteRegExp() previously returned an array or null. */
rval->setNull();
return true;
}
return CreateRegExpMatchResult(cx, input, chars, length, matchPairs, rval);
}
if (test) {
/* Forbid an array, as an optimization. */
rval->setBoolean(true);
return true;
}
bool
js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpShared &shared,
Handle<JSStableString*> input, StableCharPtr chars, size_t length,
size_t *lastIndex, RegExpExecType type, Value *rval)
{
return ExecuteRegExpImpl(cx, res, shared, input, chars, length, lastIndex, type, rval);
}
bool
js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject &reobj,
Handle<JSStableString*> input, StableCharPtr chars, size_t length,
size_t *lastIndex, RegExpExecType type, Value *rval)
{
return ExecuteRegExpImpl(cx, res, reobj, input, chars, length, lastIndex, type, rval);
return CreateRegExpMatchResult(cx, input, chars, length, matches, rval);
}
/* Note: returns the original if no escaping need be performed. */
@ -530,23 +544,22 @@ js_InitRegExpClass(JSContext *cx, HandleObject obj)
return proto;
}
bool
js::ExecuteRegExp(JSContext *cx, RegExpExecType execType, HandleObject regexp,
HandleString string, MutableHandleValue rval)
RegExpRunStatus
js::ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string, MatchConduit &matches)
{
/* Step 1 (b) was performed by CallNonGenericMethod. */
Rooted<RegExpObject*> reobj(cx, &regexp->asRegExp());
RegExpGuard re;
if (!reobj->getShared(cx, &re))
return false;
return RegExpRunStatus_Error;
RegExpStatics *res = cx->regExpStatics();
/* Step 3. */
Rooted<JSStableString*> stableInput(cx, string->ensureStable(cx));
if (!stableInput)
return false;
return RegExpRunStatus_Error;
/* Step 4. */
Value lastIndex = reobj->getLastIndex();
@ -554,7 +567,7 @@ js::ExecuteRegExp(JSContext *cx, RegExpExecType execType, HandleObject regexp,
/* Step 5. */
double i;
if (!ToInteger(cx, lastIndex, &i))
return false;
return RegExpRunStatus_Error;
/* Steps 6-7 (with sticky extension). */
if (!re->global() && !re->sticky())
@ -566,36 +579,31 @@ js::ExecuteRegExp(JSContext *cx, RegExpExecType execType, HandleObject regexp,
/* Step 9a. */
if (i < 0 || i > length) {
reobj->zeroLastIndex();
rval.setNull();
return true;
return RegExpRunStatus_Success_NotFound;
}
/* Steps 8-21. */
size_t lastIndexInt(i);
if (!ExecuteRegExp(cx, res, *re, stableInput, chars, length, &lastIndexInt, execType,
rval.address())) {
return false;
}
RegExpRunStatus status =
ExecuteRegExpImpl(cx, res, *re, *reobj, stableInput, chars, length, &lastIndexInt, matches);
if (status == RegExpRunStatus_Error)
return RegExpRunStatus_Error;
/* Step 11 (with sticky extension). */
if (re->global() || (!rval.isNull() && re->sticky())) {
if (rval.isNull())
if (re->global() || (status == RegExpRunStatus_Success && re->sticky())) {
if (status == RegExpRunStatus_Success_NotFound)
reobj->zeroLastIndex();
else
reobj->setLastIndex(lastIndexInt);
}
return true;
return status;
}
/*
* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2).
*
* RegExp.prototype.test doesn't need to create a results array, and we use
* |execType| to perform this optimization.
*/
static bool
ExecuteRegExp(JSContext *cx, RegExpExecType execType, CallArgs args)
/* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2). */
static RegExpRunStatus
ExecuteRegExp(JSContext *cx, CallArgs args, MatchConduit &matches)
{
/* Step 1 (a) was performed by CallNonGenericMethod. */
RootedObject regexp(cx, &args.thisv().toObject());
@ -603,16 +611,39 @@ ExecuteRegExp(JSContext *cx, RegExpExecType execType, CallArgs args)
/* Step 2. */
RootedString string(cx, ToString(cx, (args.length() > 0) ? args[0] : UndefinedValue()));
if (!string)
return false;
return RegExpRunStatus_Error;
return ExecuteRegExp(cx, execType, regexp, string, args.rval());
return ExecuteRegExp(cx, regexp, string, matches);
}
/* ES5 15.10.6.2. */
static bool
regexp_exec_impl(JSContext *cx, CallArgs args)
{
return ExecuteRegExp(cx, RegExpExec, args);
/* Execute regular expression and gather matches. */
ScopedMatchPairs matches(&cx->tempLifoAlloc());
MatchConduit conduit(&matches);
/*
* Extract arguments to share between ExecuteRegExp()
* and CreateRegExpMatchResult().
*/
RootedObject regexp(cx, &args.thisv().toObject());
RootedString string(cx, ToString(cx, (args.length() > 0) ? args[0] : UndefinedValue()));
if (!string)
return false;
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit);
if (status == RegExpRunStatus_Error)
return false;
if (status == RegExpRunStatus_Success_NotFound) {
args.rval().setNull();
return true;
}
return CreateRegExpMatchResult(cx, string, matches, args.rval().address());
}
JSBool
@ -626,11 +657,22 @@ js::regexp_exec(JSContext *cx, unsigned argc, Value *vp)
static bool
regexp_test_impl(JSContext *cx, CallArgs args)
{
if (!ExecuteRegExp(cx, RegExpTest, args))
return false;
if (!args.rval().isTrue())
args.rval().setBoolean(false);
return true;
MatchPair match;
MatchConduit conduit(&match);
RegExpRunStatus status = ExecuteRegExp(cx, args, conduit);
args.rval().setBoolean(status == RegExpRunStatus_Success);
return (status != RegExpRunStatus_Error);
}
/* Separate interface for use by IonMonkey. */
bool
js::regexp_test_raw(JSContext *cx, HandleObject regexp, HandleString input, JSBool *result)
{
MatchPair match;
MatchConduit conduit(&match);
RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, conduit);
*result = (status == RegExpRunStatus_Success);
return (status != RegExpRunStatus_Error);
}
JSBool

View File

@ -10,6 +10,9 @@
#include "jsprvtd.h"
#include "vm/MatchPairs.h"
#include "vm/RegExpObject.h"
JSObject *
js_InitRegExpClass(JSContext *cx, js::HandleObject obj);
@ -20,31 +23,39 @@ js_InitRegExpClass(JSContext *cx, js::HandleObject obj);
namespace js {
RegExpRunStatus
ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string,
MatchConduit &matches);
/*
* |res| may be null if the |RegExpStatics| are not to be updated.
* |input| may be null if there is no |JSString| corresponding to
* Legacy behavior of ExecuteRegExp(), which is baked into the JSAPI.
*
* |res| may be NULL if the RegExpStatics are not to be updated.
* |input| may be NULL if there is no JSString corresponding to
* |chars| and |length|.
*/
bool
ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject &reobj,
Handle<JSStableString*> input, StableCharPtr chars, size_t length,
size_t *lastIndex, RegExpExecType type, Value *rval);
ExecuteRegExpLegacy(JSContext *cx, RegExpStatics *res, RegExpObject &reobj,
Handle<JSStableString*> input, StableCharPtr chars, size_t length,
size_t *lastIndex, JSBool test, jsval *rval);
/* Translation from MatchPairs to a JS array in regexp_exec()'s output format. */
bool
CreateRegExpMatchResult(JSContext *cx, HandleString string, MatchPairs &matches, Value *rval);
bool
ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpShared &shared,
Handle<JSStableString*> input, StableCharPtr chars, size_t length,
size_t *lastIndex, RegExpExecType type, Value *rval);
bool
ExecuteRegExp(JSContext *cx, RegExpExecType execType, HandleObject regexp,
HandleString string, MutableHandleValue rval);
CreateRegExpMatchResult(JSContext *cx, JSString *input_, StableCharPtr chars, size_t length,
MatchPairs &matches, Value *rval);
extern JSBool
regexp_exec(JSContext *cx, unsigned argc, Value *vp);
bool
regexp_test_raw(JSContext *cx, HandleObject regexp, HandleString input, JSBool *result);
extern JSBool
regexp_test(JSContext *cx, unsigned argc, Value *vp);
} /* namespace js */
#endif
#endif /* RegExp_h__ */

View File

@ -674,9 +674,9 @@ Shape::Range::AutoRooter::trace(JSTracer *trc)
void
RegExpStatics::AutoRooter::trace(JSTracer *trc)
{
if (statics->matchPairsInput)
MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics->matchPairsInput),
"RegExpStatics::AutoRooter matchPairsInput");
if (statics->matchesInput)
MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics->matchesInput),
"RegExpStatics::AutoRooter matchesInput");
if (statics->pendingInput)
MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics->pendingInput),
"RegExpStatics::AutoRooter pendingInput");

View File

@ -245,28 +245,16 @@ CodeGenerator::visitRegExp(LRegExp *lir)
return callVM(CloneRegExpObjectInfo, lir);
}
typedef bool (*ExecuteRegExpFn)(JSContext *cx, RegExpExecType type, HandleObject regexp,
HandleString string, MutableHandleValue rval);
static const VMFunction ExecuteRegExpInfo = FunctionInfo<ExecuteRegExpFn>(ExecuteRegExp);
typedef bool (*RegExpTestRawFn)(JSContext *cx, HandleObject regexp,
HandleString input, JSBool *result);
static const VMFunction RegExpTestRawInfo = FunctionInfo<RegExpTestRawFn>(regexp_test_raw);
bool
CodeGenerator::visitRegExpTest(LRegExpTest *lir)
{
pushArg(ToRegister(lir->string()));
pushArg(ToRegister(lir->regexp()));
pushArg(Imm32(RegExpTest));
if (!callVM(ExecuteRegExpInfo, lir))
return false;
Register output = ToRegister(lir->output());
Label notBool, end;
masm.branchTestBoolean(Assembler::NotEqual, JSReturnOperand, &notBool);
masm.unboxBoolean(JSReturnOperand, output);
masm.jump(&end);
masm.bind(&notBool);
masm.mov(Imm32(0), output);
masm.bind(&end);
return true;
return callVM(RegExpTestRawInfo, lir);
}
typedef JSObject *(*LambdaFn)(JSContext *, HandleFunction, HandleObject);

View File

@ -6739,8 +6739,10 @@ JS_ExecuteRegExp(JSContext *cx, JSObject *objArg, JSObject *reobjArg, jschar *ch
CHECK_REQUEST(cx);
RegExpStatics *res = obj->asGlobal().getRegExpStatics();
return ExecuteRegExp(cx, res, reobj->asRegExp(), NullPtr(), StableCharPtr(chars, length),
length, indexp, test ? RegExpTest : RegExpExec, rval);
StableCharPtr charPtr(chars, length);
return ExecuteRegExpLegacy(cx, res, reobj->asRegExp(), NullPtr(),
charPtr, length, indexp, test, rval);
}
JS_PUBLIC_API(JSObject *)
@ -6774,8 +6776,9 @@ JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *objArg, jschar *chars, size_t
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
return ExecuteRegExp(cx, NULL, obj->asRegExp(), NullPtr(), StableCharPtr(chars, length),
length, indexp, test ? RegExpTest : RegExpExec, rval);
StableCharPtr charPtr(chars, length);
return ExecuteRegExpLegacy(cx, NULL, obj->asRegExp(), NullPtr(),
charPtr, length, indexp, test, rval);
}
JS_PUBLIC_API(JSBool)

View File

@ -105,12 +105,6 @@ enum RegExpFlag
AllFlags = 0x0f
};
enum RegExpExecType
{
RegExpExec,
RegExpTest
};
class ExecuteArgsGuard;
class InvokeFrameGuard;
class InvokeArgsGuard;

View File

@ -1648,13 +1648,6 @@ class StringRegExpGuard
RegExpShared &regExp() { return *re_; }
};
/* ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
static JS_ALWAYS_INLINE bool
Matched(RegExpExecType type, const Value &v)
{
return (type == RegExpTest) ? v.isTrue() : !v.isNull();
}
typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data);
/*
@ -1680,34 +1673,60 @@ DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, RegExpShared &re,
if (!stableStr)
return false;
StableCharPtr chars = stableStr->chars();
size_t charsLen = stableStr->length();
ScopedMatchPairs matches(&cx->tempLifoAlloc());
if (re.global()) {
RegExpExecType type = (flags & TEST_GLOBAL_BIT) ? RegExpTest : RegExpExec;
bool isTest = bool(flags & TEST_GLOBAL_BIT);
for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
if (!JS_CHECK_OPERATION_LIMIT(cx))
return false;
StableCharPtr chars = stableStr->chars();
size_t charsLen = stableStr->length();
if (!ExecuteRegExp(cx, res, re, stableStr, chars, charsLen, &i, type, rval))
RegExpRunStatus status = re.execute(cx, chars, charsLen, &i, matches);
if (status == RegExpRunStatus_Error)
return false;
if (!Matched(type, *rval))
if (status == RegExpRunStatus_Success_NotFound) {
rval->setNull();
break;
}
res->updateFromMatchPairs(cx, stableStr, matches);
if (!isTest && !CreateRegExpMatchResult(cx, stableStr, matches, rval))
return false;
if (!callback(cx, res, count, data))
return false;
if (!res->matched())
++i;
}
} else {
StableCharPtr chars = stableStr->chars();
size_t charsLen = stableStr->length();
RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec;
bool isTest = bool(flags & TEST_SINGLE_BIT);
bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
size_t i = 0;
if (!ExecuteRegExp(cx, res, re, stableStr, chars, charsLen, &i, type, rval))
RegExpRunStatus status = re.execute(cx, chars, charsLen, &i, matches);
if (status == RegExpRunStatus_Error)
return false;
if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data))
/* Emulate ExecuteRegExpLegacy() behavior. */
if (status == RegExpRunStatus_Success_NotFound) {
rval->setNull();
return true;
}
res->updateFromMatchPairs(cx, stableStr, matches);
if (isTest) {
rval->setBoolean(true);
} else {
if (!CreateRegExpMatchResult(cx, stableStr, matches, rval))
return false;
}
if (callbackOnSingle && !callback(cx, res, 0, data))
return false;
}
return true;
@ -1832,14 +1851,19 @@ js::str_search(JSContext *cx, unsigned argc, Value *vp)
/* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
size_t i = 0;
Value result;
if (!ExecuteRegExp(cx, res, g.regExp(), stableStr, chars, length, &i, RegExpTest, &result))
ScopedMatchPairs matches(&cx->tempLifoAlloc());
RegExpRunStatus status = g.regExp().execute(cx, chars, length, &i, matches);
if (status == RegExpRunStatus_Error)
return false;
if (result.isTrue())
args.rval().setInt32(res->matchStart());
else
if (status == RegExpRunStatus_Success) {
res->updateFromMatchPairs(cx, stableStr, matches);
args.rval().setInt32(matches[0].start);
} else {
args.rval().setInt32(-1);
}
return true;
}
@ -1882,13 +1906,13 @@ InterpretDollar(RegExpStatics *res, const jschar *dp, const jschar *ep,
if (JS7_ISDEC(dc)) {
/* ECMA-262 Edition 3: 1-9 or 01-99 */
unsigned num = JS7_UNDEC(dc);
if (num > res->parenCount())
if (num > res->getMatches().parenCount())
return false;
const jschar *cp = dp + 2;
if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
unsigned tmp = 10 * num + JS7_UNDEC(dc);
if (tmp <= res->parenCount()) {
if (tmp <= res->getMatches().parenCount()) {
cp++;
num = tmp;
}
@ -1898,7 +1922,7 @@ InterpretDollar(RegExpStatics *res, const jschar *dp, const jschar *ep,
*skip = cp - dp;
JS_ASSERT(num <= res->parenCount());
JS_ASSERT(num <= res->getMatches().parenCount());
/*
* Note: we index to get the paren with the (1-indexed) pair
@ -1989,7 +2013,7 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
* For $&, etc., we must create string jsvals from cx->regExpStatics.
* We grab up stack space to keep the newborn strings GC-rooted.
*/
unsigned p = res->parenCount();
unsigned p = res->getMatches().parenCount();
unsigned argc = 1 + p + 2;
InvokeArgsGuard &args = rdata.fig.args();
@ -2004,13 +2028,13 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
if (!res->createLastMatch(cx, &args[argi++]))
return false;
for (size_t i = 0; i < res->parenCount(); ++i) {
for (size_t i = 0; i < res->getMatches().parenCount(); ++i) {
if (!res->createParen(cx, i + 1, &args[argi++]))
return false;
}
/* Push match index and input string. */
args[argi++].setInt32(res->matchStart());
args[argi++].setInt32(res->getMatches()[0].start);
args[argi].setString(rdata.str);
if (!rdata.fig.invoke(cx))
@ -2091,10 +2115,14 @@ ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
{
ReplaceData &rdata = *static_cast<ReplaceData *>(p);
const MatchPair &match = res->getMatches()[0];
JS_ASSERT(!match.isUndefined());
JS_ASSERT(match.limit >= match.start && match.limit >= 0);
rdata.calledBack = true;
size_t leftoff = rdata.leftIndex;
size_t leftlen = res->matchStart() - leftoff;
rdata.leftIndex = res->matchLimit();
size_t leftlen = match.start - leftoff;
rdata.leftIndex = match.limit;
size_t replen = 0; /* silence 'unused' warning */
if (!FindReplaceLength(cx, res, rdata, &replen))
@ -2623,9 +2651,10 @@ SplitHelper(JSContext *cx, Handle<JSStableString*> str, uint32_t limit, const Ma
/* Step 13(c)(iii)(6-7). */
if (Matcher::returnsCaptures) {
RegExpStatics *res = cx->regExpStatics();
for (size_t i = 0; i < res->parenCount(); i++) {
const MatchPairs &matches = res->getMatches();
for (size_t i = 0; i < matches.parenCount(); i++) {
/* Steps 13(c)(iii)(7)(a-c). */
if (res->pairIsPresent(i + 1)) {
if (!matches[i + 1].isUndefined()) {
JSSubString parsub;
res->getParen(i + 1, &parsub);
sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
@ -2678,15 +2707,21 @@ class SplitRegExpMatcher
SplitMatchResult *result) const
{
AssertCanGC();
Value rval = UndefinedValue();
StableCharPtr chars = str->chars();
size_t length = str->length();
if (!ExecuteRegExp(cx, res, re, str, chars, length, &index, RegExpTest, &rval))
ScopedMatchPairs matches(&cx->tempLifoAlloc());
RegExpRunStatus status = re.execute(cx, chars, length, &index, matches);
if (status == RegExpRunStatus_Error)
return false;
if (!rval.isTrue()) {
if (status == RegExpRunStatus_Success_NotFound) {
result->setFailure();
return true;
}
res->updateFromMatchPairs(cx, str, matches);
JSSubString sep;
res->getLastMatch(&sep);

View File

@ -23,70 +23,144 @@ struct MatchPair
int start;
int limit;
MatchPair(int start, int limit) : start(start), limit(limit) {}
MatchPair()
: start(-1), limit(-1)
{ }
size_t length() const {
JS_ASSERT(!isUndefined());
return limit - start;
MatchPair(int start, int limit)
: start(start), limit(limit)
{ }
size_t length() const { JS_ASSERT(!isUndefined()); return limit - start; }
bool isEmpty() const { return length() == 0; }
bool isUndefined() const { return start < 0; }
void displace(size_t amount) {
start += (start < 0) ? 0 : amount;
limit += (limit < 0) ? 0 : amount;
}
bool isUndefined() const {
return start == -1;
}
void check() const {
inline bool check() const {
JS_ASSERT(limit >= start);
JS_ASSERT_IF(!isUndefined(), start >= 0);
JS_ASSERT_IF(start < 0, start == -1);
JS_ASSERT_IF(limit < 0, limit == -1);
return true;
}
};
/* Base class for RegExp execution output. */
class MatchPairs
{
size_t pairCount_;
int buffer_[1];
protected:
size_t pairCount_; /* Length of pairs_. */
MatchPair *pairs_; /* Raw pointer into an allocated MatchPair buffer. */
explicit MatchPairs(size_t pairCount) : pairCount_(pairCount) {
initPairValues();
}
void initPairValues() {
for (int *it = buffer_; it < buffer_ + 2 * pairCount_; ++it)
*it = -1;
}
static size_t calculateSize(size_t backingPairCount) {
return sizeof(MatchPairs) - sizeof(int) + sizeof(int) * backingPairCount;
}
int *buffer() { return buffer_; }
protected:
/* Not used directly: use ScopedMatchPairs or VectorMatchPairs. */
MatchPairs()
: pairCount_(0), pairs_(NULL)
{ }
protected:
/* Functions used by friend classes. */
friend class RegExpShared;
friend class RegExpStatics;
/* MatchPair buffer allocator: set pairs_ and pairCount_. */
virtual bool allocOrExpandArray(size_t pairCount) = 0;
bool initArray(size_t pairCount);
bool initArrayFrom(MatchPairs &copyFrom);
void forgetArray() { pairs_ = NULL; }
void displace(size_t disp);
inline void checkAgainst(size_t length);
public:
/*
* |backingPairCount| is necessary because PCRE uses extra space
* after the actual results in the buffer.
*/
static MatchPairs *create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount);
/* Querying functions in the style of RegExpStatics. */
bool empty() const { return pairCount_ == 0; }
size_t pairCount() const { JS_ASSERT(pairCount_ > 0); return pairCount_; }
size_t parenCount() const { return pairCount_ - 1; }
size_t pairCount() const { return pairCount_; }
public:
unsigned *rawBuf() const { return reinterpret_cast<unsigned *>(pairs_); }
size_t length() const { return pairCount_; }
MatchPair pair(size_t i) {
JS_ASSERT(i < pairCount());
return MatchPair(buffer_[2 * i], buffer_[2 * i + 1]);
/* Pair accessors. */
const MatchPair &pair(size_t i) const {
JS_ASSERT(pairCount_ && i < pairCount_);
JS_ASSERT(pairs_);
return pairs_[i];
}
void displace(size_t amount) {
if (!amount)
return;
const MatchPair &operator[](size_t i) const { return pair(i); }
};
for (int *it = buffer_; it < buffer_ + 2 * pairCount_; ++it)
*it = (*it < 0) ? -1 : *it + amount;
/* MatchPairs allocated into temporary storage, removed when out of scope. */
class ScopedMatchPairs : public MatchPairs
{
LifoAlloc *lifoAlloc_;
void *mark_; /* Saved original position in bump allocator. */
public:
/* Constructs an implicit LifoAllocScope. */
ScopedMatchPairs(LifoAlloc *lifoAlloc)
: lifoAlloc_(lifoAlloc),
mark_(lifoAlloc->mark())
{ }
~ScopedMatchPairs() {
lifoAlloc_->release(mark_);
}
inline void checkAgainst(size_t length);
const MatchPair &operator[](size_t i) const { return pair(i); }
protected:
bool allocOrExpandArray(size_t pairCount);
};
/*
* MatchPairs allocated into permanent storage, for RegExpStatics.
* The Vector of MatchPairs is reusable by Vector expansion.
*/
class VectorMatchPairs : public MatchPairs
{
Vector<MatchPair, 10, SystemAllocPolicy> vec_;
public:
VectorMatchPairs() {
vec_.clear();
}
const MatchPair &operator[](size_t i) const { return pair(i); }
protected:
friend class RegExpStatics;
bool allocOrExpandArray(size_t pairCount);
};
/*
* Passes either MatchPair or MatchPairs through ExecuteRegExp()
* to avoid duplication of generic code.
*/
struct MatchConduit
{
union {
MatchPair *pair;
MatchPairs *pairs;
} u;
bool isPair;
explicit MatchConduit(MatchPair *pair) {
isPair = true;
u.pair = pair;
}
explicit MatchConduit(MatchPairs *pairs) {
isPair = false;
u.pairs = pairs;
}
};
} /* namespace js */
#endif
#endif /* MatchPairs_h__ */

View File

@ -111,23 +111,61 @@ RegExpObjectBuilder::clone(Handle<RegExpObject *> other, Handle<RegExpObject *>
/* MatchPairs */
MatchPairs *
MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount)
bool
MatchPairs::initArray(size_t pairCount)
{
void *mem = alloc.alloc(calculateSize(backingPairCount));
if (!mem)
return NULL;
JS_ASSERT(pairCount > 0);
return new (mem) MatchPairs(pairCount);
/* Guarantee adequate space in buffer. */
if (!allocOrExpandArray(pairCount))
return false;
/* Initialize all MatchPair objects to invalid locations. */
for (size_t i = 0; i < pairCount; i++) {
pairs_[i].start = size_t(-1);
pairs_[i].limit = size_t(-1);
}
return true;
}
bool
MatchPairs::initArrayFrom(MatchPairs &copyFrom)
{
JS_ASSERT(copyFrom.pairCount() > 0);
if (!allocOrExpandArray(copyFrom.pairCount()))
return false;
for (size_t i = 0; i < pairCount_; i++) {
JS_ASSERT(copyFrom[i].check());
pairs_[i].start = copyFrom[i].start;
pairs_[i].limit = copyFrom[i].limit;
}
return true;
}
void
MatchPairs::displace(size_t disp)
{
if (disp == 0)
return;
for (size_t i = 0; i < pairCount_; i++) {
JS_ASSERT(pairs_[i].check());
pairs_[i].start += (pairs_[i].start < 0) ? 0 : disp;
pairs_[i].limit += (pairs_[i].limit < 0) ? 0 : disp;
}
}
inline void
MatchPairs::checkAgainst(size_t inputLength)
{
#if DEBUG
for (size_t i = 0; i < pairCount(); ++i) {
MatchPair p = pair(i);
p.check();
for (size_t i = 0; i < pairCount_; i++) {
const MatchPair &p = pair(i);
JS_ASSERT(p.check());
if (p.isUndefined())
continue;
JS_ASSERT(size_t(p.limit) <= inputLength);
@ -135,6 +173,36 @@ MatchPairs::checkAgainst(size_t inputLength)
#endif
}
bool
ScopedMatchPairs::allocOrExpandArray(size_t pairCount)
{
/* Array expansion is forbidden, but array reuse is acceptable. */
if (pairCount_) {
JS_ASSERT(pairs_);
JS_ASSERT(pairCount_ == pairCount);
return true;
}
JS_ASSERT(!pairs_);
pairs_ = (MatchPair *)lifoAlloc_->alloc(sizeof(MatchPair) * pairCount);
if (!pairs_)
return false;
pairCount_ = pairCount;
return true;
}
bool
VectorMatchPairs::allocOrExpandArray(size_t pairCount)
{
if (!vec_.resizeUninitialized(sizeof(MatchPair) * pairCount))
return false;
pairs_ = &vec_[0];
pairCount_ = pairCount;
return true;
}
/* RegExpObject */
static void
@ -291,16 +359,6 @@ RegExpObject::init(JSContext *cx, HandleAtom source, RegExpFlag flags)
return true;
}
RegExpRunStatus
RegExpObject::execute(JSContext *cx, StableCharPtr chars, size_t length, size_t *lastIndex,
MatchPairs **output)
{
RegExpGuard g;
if (!getShared(cx, &g))
return RegExpRunStatus_Error;
return g->execute(cx, chars, length, lastIndex, output);
}
JSFlatString *
RegExpObject::toString(JSContext *cx) const
{
@ -466,36 +524,33 @@ RegExpShared::compileIfNecessary(JSContext *cx)
}
RegExpRunStatus
RegExpShared::execute(JSContext *cx, StableCharPtr chars, size_t length, size_t *lastIndex,
MatchPairs **output)
RegExpShared::execute(JSContext *cx, StableCharPtr chars, size_t length,
size_t *lastIndex, MatchPairs &matches)
{
/* Compile the code at point-of-use. */
if (!compileIfNecessary(cx))
return RegExpRunStatus_Error;
const size_t origLength = length;
size_t backingPairCount = pairCount() * 2;
LifoAlloc &alloc = cx->tempLifoAlloc();
MatchPairs *matchPairs = MatchPairs::create(alloc, pairCount(), backingPairCount);
if (!matchPairs)
/* Ensure sufficient memory for output vector. */
if (!matches.initArray(pairCount()))
return RegExpRunStatus_Error;
/*
* |displacement| emulates sticky mode by matching from this offset
* into the char buffer and subtracting the delta off at the end.
*/
size_t origLength = length;
size_t start = *lastIndex;
size_t displacement = 0;
if (sticky()) {
displacement = *lastIndex;
displacement = start;
chars += displacement;
length -= displacement;
start = 0;
}
unsigned *outputBuf = (unsigned *)matchPairs->buffer();
unsigned *outputBuf = matches.rawBuf();
unsigned result;
#if ENABLE_YARR_JIT
@ -507,14 +562,12 @@ RegExpShared::execute(JSContext *cx, StableCharPtr chars, size_t length, size_t
result = JSC::Yarr::interpret(bytecode, chars.get(), length, start, outputBuf);
#endif
*output = matchPairs;
if (result == JSC::Yarr::offsetNoMatch)
return RegExpRunStatus_Success_NotFound;
matchPairs->displace(displacement);
matchPairs->checkAgainst(origLength);
*lastIndex = matchPairs->pair(0).limit;
matches.displace(displacement);
matches.checkAgainst(origLength);
*lastIndex = matches[0].limit;
return RegExpRunStatus_Success;
}

View File

@ -15,7 +15,9 @@
#include "jsobj.h"
#include "js/TemplateLib.h"
#include "vm/MatchPairs.h"
#include "yarr/MatchResult.h"
#include "yarr/Yarr.h"
#if ENABLE_YARR_JIT
#include "yarr/YarrJIT.h"
@ -156,7 +158,7 @@ class RegExpShared
/* Primary interface: run this regular expression on the given string. */
RegExpRunStatus execute(JSContext *cx, StableCharPtr chars, size_t length,
size_t *lastIndex, MatchPairs **matches);
size_t *lastIndex, MatchPairs &matches);
/* Accessors */
@ -287,21 +289,6 @@ class RegExpObject : public JSObject
static RegExpObject *
createNoStatics(JSContext *cx, HandleAtom atom, RegExpFlag flags, frontend::TokenStream *ts);
/*
* Run the regular expression over the input text.
*
* Results are placed in |output| as integer pairs. For eaxmple,
* |output[0]| and |output[1]| represent the text indices that make
* up the "0" (whole match) pair. Capturing parens will result in
* more output.
*
* N.B. it's the responsibility of the caller to hook the |output|
* into the |RegExpStatics| appropriately, if necessary.
*/
RegExpRunStatus
execute(JSContext *cx, StableCharPtr chars, size_t length, size_t *lastIndex,
MatchPairs **output);
/* Accessors. */
const Value &getLastIndex() const { return getSlot(LAST_INDEX_SLOT); }

View File

@ -36,11 +36,12 @@ RegExpStatics::RegExpStatics()
}
inline bool
RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, Value *out) const
RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, Value *out)
{
JS_ASSERT(start <= end);
JS_ASSERT(end <= matchPairsInput->length());
JSString *str = js_NewDependentString(cx, matchPairsInput, start, end - start);
JS_ASSERT(end <= matchesInput->length());
JSString *str = js_NewDependentString(cx, matchesInput, start, end - start);
if (!str)
return false;
*out = StringValue(str);
@ -48,136 +49,156 @@ RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, Value *o
}
inline bool
RegExpStatics::createPendingInput(JSContext *cx, Value *out) const
RegExpStatics::createPendingInput(JSContext *cx, Value *out)
{
/* Lazy evaluation need not be resolved to return the input. */
out->setString(pendingInput ? pendingInput.get() : cx->runtime->emptyString);
return true;
}
inline bool
RegExpStatics::makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, Value *out) const
RegExpStatics::makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, Value *out)
{
if (checkValidIndex / 2 >= pairCount() || matchPairs[checkValidIndex] < 0) {
bool checkWhich = checkValidIndex % 2;
size_t checkPair = checkValidIndex / 2;
if (matches.empty() || checkPair >= matches.pairCount() ||
(checkWhich ? matches[checkPair].limit : matches[checkPair].start) < 0)
{
out->setString(cx->runtime->emptyString);
return true;
}
return createDependent(cx, get(pairNum, 0), get(pairNum, 1), out);
const MatchPair &pair = matches[pairNum];
return createDependent(cx, pair.start, pair.limit, out);
}
inline bool
RegExpStatics::createLastParen(JSContext *cx, Value *out) const
RegExpStatics::createLastMatch(JSContext *cx, Value *out)
{
if (pairCount() <= 1) {
out->setString(cx->runtime->emptyString);
return true;
}
size_t num = pairCount() - 1;
int start = get(num, 0);
int end = get(num, 1);
if (start == -1) {
out->setString(cx->runtime->emptyString);
return true;
}
JS_ASSERT(start >= 0 && end >= 0);
JS_ASSERT(end >= start);
return createDependent(cx, start, end, out);
return makeMatch(cx, 0, 0, out);
}
inline bool
RegExpStatics::createLeftContext(JSContext *cx, Value *out) const
RegExpStatics::createLastParen(JSContext *cx, Value *out)
{
if (!pairCount()) {
if (matches.empty() || matches.pairCount() == 1) {
out->setString(cx->runtime->emptyString);
return true;
}
if (matchPairs[0] < 0) {
const MatchPair &pair = matches[matches.pairCount() - 1];
if (pair.start == -1) {
out->setString(cx->runtime->emptyString);
return true;
}
JS_ASSERT(pair.start >= 0 && pair.limit >= 0);
JS_ASSERT(pair.limit >= pair.start);
return createDependent(cx, pair.start, pair.limit, out);
}
inline bool
RegExpStatics::createParen(JSContext *cx, size_t pairNum, Value *out)
{
JS_ASSERT(pairNum >= 1);
if (matches.empty() || pairNum >= matches.pairCount()) {
out->setString(cx->runtime->emptyString);
return true;
}
return makeMatch(cx, pairNum * 2, pairNum, out);
}
inline bool
RegExpStatics::createLeftContext(JSContext *cx, Value *out)
{
if (matches.empty()) {
out->setString(cx->runtime->emptyString);
return true;
}
if (matches[0].start < 0) {
*out = UndefinedValue();
return true;
}
return createDependent(cx, 0, matchPairs[0], out);
return createDependent(cx, 0, matches[0].start, out);
}
inline bool
RegExpStatics::createRightContext(JSContext *cx, Value *out) const
RegExpStatics::createRightContext(JSContext *cx, Value *out)
{
if (!pairCount()) {
if (matches.empty()) {
out->setString(cx->runtime->emptyString);
return true;
}
if (matchPairs[1] < 0) {
if (matches[0].limit < 0) {
*out = UndefinedValue();
return true;
}
return createDependent(cx, matchPairs[1], matchPairsInput->length(), out);
return createDependent(cx, matches[0].limit, matchesInput->length(), out);
}
inline void
RegExpStatics::getParen(size_t pairNum, JSSubString *out) const
{
checkParenNum(pairNum);
if (!pairIsPresent(pairNum)) {
JS_ASSERT(pairNum >= 1 && pairNum < matches.pairCount());
const MatchPair &pair = matches[pairNum];
if (pair.isUndefined()) {
*out = js_EmptySubString;
return;
}
out->chars = matchPairsInput->chars() + get(pairNum, 0);
out->length = getParenLength(pairNum);
out->chars = matchesInput->chars() + pair.start;
out->length = pair.length();
}
inline void
RegExpStatics::getLastMatch(JSSubString *out) const
{
if (!pairCount()) {
if (matches.empty()) {
*out = js_EmptySubString;
return;
}
JS_ASSERT(matchPairsInput);
out->chars = matchPairsInput->chars() + get(0, 0);
JS_ASSERT(get(0, 1) >= get(0, 0));
out->length = get(0, 1) - get(0, 0);
JS_ASSERT(matchesInput);
out->chars = matchesInput->chars() + matches[0].start;
JS_ASSERT(matches[0].limit >= matches[0].start);
out->length = matches[0].length();
}
inline void
RegExpStatics::getLastParen(JSSubString *out) const
{
size_t pc = pairCount();
/* Note: the first pair is the whole match. */
if (pc <= 1) {
if (matches.empty() || matches.pairCount() == 1) {
*out = js_EmptySubString;
return;
}
getParen(pc - 1, out);
getParen(matches.parenCount(), out);
}
inline void
RegExpStatics::getLeftContext(JSSubString *out) const
{
if (!pairCount()) {
if (matches.empty()) {
*out = js_EmptySubString;
return;
}
out->chars = matchPairsInput->chars();
out->length = get(0, 0);
out->chars = matchesInput->chars();
out->length = matches[0].start;
}
inline void
RegExpStatics::getRightContext(JSSubString *out) const
{
if (!pairCount()) {
if (matches.empty()) {
*out = js_EmptySubString;
return;
}
out->chars = matchPairsInput->chars() + get(0, 1);
JS_ASSERT(get(0, 1) <= int(matchPairsInput->length()));
out->length = matchPairsInput->length() - get(0, 1);
out->chars = matchesInput->chars() + matches[0].limit;
JS_ASSERT(matches[0].limit <= int(matchesInput->length()));
out->length = matchesInput->length() - matches[0].limit;
}
inline void
RegExpStatics::copyTo(RegExpStatics &dst)
{
dst.matchPairs.clear();
/* 'save' has already reserved space in matchPairs */
dst.matchPairs.infallibleAppend(matchPairs);
dst.matchPairsInput = matchPairsInput;
dst.matches.initArrayFrom(matches);
dst.matchesInput = matchesInput;
dst.pendingInput = pendingInput;
dst.flags = flags;
}
@ -200,24 +221,20 @@ RegExpStatics::restore()
}
inline bool
RegExpStatics::updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs *newPairs)
RegExpStatics::updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs &newPairs)
{
JS_ASSERT(input);
aboutToWrite();
BarrieredSetPair<JSString, JSLinearString>(cx->compartment,
pendingInput, input,
matchPairsInput, input);
matchesInput, input);
if (!matchPairs.resizeUninitialized(2 * newPairs->pairCount())) {
if (!matches.initArrayFrom(newPairs)) {
js_ReportOutOfMemory(cx);
return false;
}
for (size_t i = 0; i < newPairs->pairCount(); ++i) {
matchPairs[2 * i] = newPairs->pair(i).start;
matchPairs[2 * i + 1] = newPairs->pair(i).limit;
}
return true;
}
@ -227,8 +244,8 @@ RegExpStatics::clear()
aboutToWrite();
flags = RegExpFlag(0);
pendingInput = NULL;
matchPairsInput = NULL;
matchPairs.clear();
matchesInput = NULL;
matches.forgetArray();
}
inline void
@ -281,6 +298,33 @@ RegExpStatics::reset(JSContext *cx, JSString *newInput, bool newMultiline)
checkInvariants();
}
inline void
RegExpStatics::checkInvariants()
{
#ifdef DEBUG
if (matches.empty()) {
JS_ASSERT(!matchesInput);
return;
}
/* Pair count is non-zero, so there must be match pairs input. */
JS_ASSERT(matchesInput);
size_t mpiLen = matchesInput->length();
/* Both members of the first pair must be non-negative. */
JS_ASSERT(!matches[0].isUndefined());
JS_ASSERT(matches[0].limit >= 0);
/* Present pairs must be valid. */
for (size_t i = 0; i < matches.pairCount(); i++) {
if (matches[i].isUndefined())
continue;
const MatchPair &pair = matches[i];
JS_ASSERT(mpiLen >= size_t(pair.limit) && pair.limit >= pair.start && pair.start >= 0);
}
#endif /* DEBUG */
}
} /* namespace js */
inline js::RegExpStatics *

View File

@ -9,6 +9,7 @@
#include "jsobjinlines.h"
#include "vm/RegExpObject-inl.h"
#include "vm/RegExpStatics-inl.h"
using namespace js;

View File

@ -20,88 +20,43 @@ namespace js {
class RegExpStatics
{
typedef Vector<int, 20, SystemAllocPolicy> Pairs;
Pairs matchPairs;
/* The input that was used to produce matchPairs. */
HeapPtr<JSLinearString> matchPairsInput;
/* The input last set on the statics. */
/* The latest RegExp output, set after execution. */
VectorMatchPairs matches;
HeapPtr<JSLinearString> matchesInput;
/* The latest RegExp input, set before execution. */
HeapPtr<JSString> pendingInput;
RegExpFlag flags;
/* Linkage for preserving RegExpStatics during nested RegExp execution. */
RegExpStatics *bufferLink;
bool copied;
bool createDependent(JSContext *cx, size_t start, size_t end, Value *out) const;
private:
inline void aboutToWrite();
inline void copyTo(RegExpStatics &dst);
inline void aboutToWrite();
inline void restore();
bool save(JSContext *cx, RegExpStatics *buffer) {
JS_ASSERT(!buffer->copied && !buffer->bufferLink);
buffer->bufferLink = bufferLink;
bufferLink = buffer;
if (!buffer->matchPairs.reserve(matchPairs.length())) {
if (!buffer->matches.allocOrExpandArray(matches.length())) {
js_ReportOutOfMemory(cx);
return false;
}
return true;
}
inline void restore();
void checkInvariants() {
#if DEBUG
if (pairCount() == 0) {
JS_ASSERT(!matchPairsInput);
return;
}
/* Pair count is non-zero, so there must be match pairs input. */
JS_ASSERT(matchPairsInput);
size_t mpiLen = matchPairsInput->length();
/* Both members of the first pair must be non-negative. */
JS_ASSERT(pairIsPresent(0));
JS_ASSERT(get(0, 1) >= 0);
/* Present pairs must be valid. */
for (size_t i = 0; i < pairCount(); ++i) {
if (!pairIsPresent(i))
continue;
int start = get(i, 0);
int limit = get(i, 1);
JS_ASSERT(mpiLen >= size_t(limit) && limit >= start && start >= 0);
}
#endif
}
/*
* Since the first pair indicates the whole match, the paren pair
* numbers have to be in the range [1, pairCount).
*/
void checkParenNum(size_t pairNum) const {
JS_ASSERT(1 <= pairNum);
JS_ASSERT(pairNum < pairCount());
}
/* Precondition: paren is present. */
size_t getParenLength(size_t pairNum) const {
checkParenNum(pairNum);
JS_ASSERT(pairIsPresent(pairNum));
return get(pairNum, 1) - get(pairNum, 0);
}
int get(size_t pairNum, bool which) const {
JS_ASSERT(pairNum < pairCount());
return matchPairs[2 * pairNum + which];
}
inline void checkInvariants();
/*
* Check whether the index at |checkValidIndex| is valid (>= 0).
* If so, construct a string for it and place it in |*out|.
* If not, place undefined in |*out|.
*/
bool makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, Value *out) const;
bool makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, Value *out);
bool createDependent(JSContext *cx, size_t start, size_t end, Value *out);
void markFlagsSet(JSContext *cx);
@ -117,7 +72,7 @@ class RegExpStatics
/* Mutators. */
inline bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs *newPairs);
inline bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs &newPairs);
inline void setMultiline(JSContext *cx, bool enabled);
inline void clear();
@ -127,81 +82,40 @@ class RegExpStatics
inline void setPendingInput(JSString *newInput);
/* Accessors. */
/*
* When there is a match present, the pairCount is at least 1 for the whole
* match. There is one additional pair per parenthesis.
*
* Getting a parenCount requires there to be a match result as a precondition.
*/
private:
size_t pairCount() const {
JS_ASSERT(matchPairs.length() % 2 == 0);
return matchPairs.length() / 2;
}
public:
size_t parenCount() const {
size_t pc = pairCount();
JS_ASSERT(pc);
return pc - 1;
/* Default match accessor. */
const MatchPairs &getMatches() const {
return matches;
}
JSString *getPendingInput() const { return pendingInput; }
RegExpFlag getFlags() const { return flags; }
bool multiline() const { return flags & MultilineFlag; }
size_t matchStart() const {
int start = get(0, 0);
JS_ASSERT(start >= 0);
return size_t(start);
}
size_t matchLimit() const {
int limit = get(0, 1);
JS_ASSERT(size_t(limit) >= matchStart() && limit >= 0);
return size_t(limit);
}
/* Returns whether results for a non-empty match are present. */
bool matched() const {
JS_ASSERT(pairCount() > 0);
JS_ASSERT_IF(get(0, 1) == -1, get(1, 1) == -1);
return get(0, 1) - get(0, 0) > 0;
JS_ASSERT(matches.pairCount() > 0);
return matches[0].limit - matches[0].start > 0;
}
void mark(JSTracer *trc) {
if (pendingInput)
MarkString(trc, &pendingInput, "res->pendingInput");
if (matchPairsInput)
MarkString(trc, &matchPairsInput, "res->matchPairsInput");
}
bool pairIsPresent(size_t pairNum) const {
return get(pairNum, 0) >= 0;
if (matchesInput)
MarkString(trc, &matchesInput, "res->matchesInput");
}
/* Value creators. */
bool createPendingInput(JSContext *cx, Value *out) const;
bool createLastMatch(JSContext *cx, Value *out) const { return makeMatch(cx, 0, 0, out); }
bool createLastParen(JSContext *cx, Value *out) const;
bool createLeftContext(JSContext *cx, Value *out) const;
bool createRightContext(JSContext *cx, Value *out) const;
bool createPendingInput(JSContext *cx, Value *out);
bool createLastMatch(JSContext *cx, Value *out);
bool createLastParen(JSContext *cx, Value *out);
bool createParen(JSContext *cx, size_t pairNum, Value *out);
bool createLeftContext(JSContext *cx, Value *out);
bool createRightContext(JSContext *cx, Value *out);
/* @param pairNum Any number >= 1. */
bool createParen(JSContext *cx, size_t pairNum, Value *out) const {
JS_ASSERT(pairNum >= 1);
if (pairNum >= pairCount()) {
out->setString(cx->runtime->emptyString);
return true;
}
return makeMatch(cx, pairNum * 2, pairNum, out);
}
/* Substring creators. */
/* Infallible substring creators. */
void getParen(size_t pairNum, JSSubString *out) const;
void getLastMatch(JSSubString *out) const;