Fix regexp match pair end-index == -1 assumption. (r=dmandelin, b=605754)

This commit is contained in:
Chris Leary 2010-12-01 16:33:49 -08:00
parent bb4d273591
commit 71dc219790
3 changed files with 31 additions and 20 deletions

View File

@ -0,0 +1,2 @@
var result = "foobarbaz".replace(/foo(bar)?bar/, "$1");
assertEq(result, "baz");

View File

@ -124,7 +124,9 @@ class RegExpStatics
JS_ASSERT(matchPairsInput); JS_ASSERT(matchPairsInput);
size_t mpiLen = matchPairsInput->length(); size_t mpiLen = matchPairsInput->length();
/* Both members of the first pair must be non-negative. */
JS_ASSERT(pairIsPresent(0)); JS_ASSERT(pairIsPresent(0));
JS_ASSERT(get(0, 1) >= 0);
/* Present pairs must be valid. */ /* Present pairs must be valid. */
for (size_t i = 0; i < pairCount(); ++i) { for (size_t i = 0; i < pairCount(); ++i) {
@ -137,6 +139,19 @@ class RegExpStatics
#endif #endif
} }
bool pairIsPresent(size_t pairNum) const {
return getCrash(pairNum, 0) >= 0;
}
/* Precondition: paren is present. */
size_t getParenLength(size_t parenNum) const {
size_t pairNum = parenNum + 1;
if (pairCountCrash() <= pairNum)
return 0;
JS_CRASH_UNLESS(pairIsPresent(pairNum));
return getCrash(pairNum, 1) - getCrash(pairNum, 0);
}
int get(size_t pairNum, bool which) const { int get(size_t pairNum, bool which) const {
JS_ASSERT(pairNum < pairCount()); JS_ASSERT(pairNum < pairCount());
return matchPairs[2 * pairNum + which]; return matchPairs[2 * pairNum + which];
@ -168,10 +183,6 @@ class RegExpStatics
/* Mutators. */ /* Mutators. */
/*
* The inputOffset parameter is added to the present (i.e. non-negative) match items to emulate
* sticky mode.
*/
bool updateFromMatch(JSContext *cx, JSString *input, int *buf, size_t matchItemCount) { bool updateFromMatch(JSContext *cx, JSString *input, int *buf, size_t matchItemCount) {
aboutToWrite(); aboutToWrite();
pendingInput = input; pendingInput = input;
@ -204,8 +215,6 @@ class RegExpStatics
matchPairs.clear(); matchPairs.clear();
} }
bool pairIsPresent(size_t pairNum) { return get(0, 0) != -1; }
/* Corresponds to JSAPI functionality to set the pending RegExp input. */ /* Corresponds to JSAPI functionality to set the pending RegExp input. */
void reset(JSString *newInput, bool newMultiline) { void reset(JSString *newInput, bool newMultiline) {
aboutToWrite(); aboutToWrite();
@ -238,12 +247,15 @@ class RegExpStatics
return size_t(limit); return size_t(limit);
} }
/* Returns whether results for a non-empty match are present. */
bool matched() const { bool matched() const {
JS_ASSERT(pairCount() > 0); JS_ASSERT(pairCount() > 0);
JS_ASSERT_IF(get(0, 1) == -1, get(1, 1) == -1);
return get(0, 1) - get(0, 0) > 0; return get(0, 1) - get(0, 0) > 0;
} }
size_t getParenCount() const { size_t getParenCount() const {
/* The first pair is the whole match. */
JS_ASSERT(pairCount() > 0); JS_ASSERT(pairCount() > 0);
return pairCount() - 1; return pairCount() - 1;
} }
@ -255,12 +267,6 @@ class RegExpStatics
JS_CALL_STRING_TRACER(trc, matchPairsInput, "res->matchPairsInput"); JS_CALL_STRING_TRACER(trc, matchPairsInput, "res->matchPairsInput");
} }
size_t getParenLength(size_t parenNum) const {
if (pairCountCrash() <= parenNum + 1)
return 0;
return getCrash(parenNum + 1, 1) - getCrash(parenNum + 1, 0);
}
/* Value creators. */ /* Value creators. */
bool createPendingInput(JSContext *cx, Value *out) const; bool createPendingInput(JSContext *cx, Value *out) const;
@ -275,6 +281,7 @@ class RegExpStatics
/* Substring creators. */ /* Substring creators. */
/* @param num Zero-indexed paren number (i.e. $1 is 0). */
void getParen(size_t num, JSSubString *out) const; void getParen(size_t num, JSSubString *out) const;
void getLastMatch(JSSubString *out) const; void getLastMatch(JSSubString *out) const;
void getLastParen(JSSubString *out) const; void getLastParen(JSSubString *out) const;

View File

@ -265,7 +265,6 @@ RegExp::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemC
} else { } else {
/* Missing parenthesized match. */ /* Missing parenthesized match. */
JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */ JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
JS_ASSERT(start == end && end == -1);
if (!builder.append(INT_TO_JSID(i / 2), UndefinedValue())) if (!builder.append(INT_TO_JSID(i / 2), UndefinedValue()))
return NULL; return NULL;
} }
@ -582,11 +581,11 @@ RegExpStatics::createLastParen(JSContext *cx, Value *out) const
int start = get(num, 0); int start = get(num, 0);
int end = get(num, 1); int end = get(num, 1);
if (start == -1) { if (start == -1) {
JS_ASSERT(end == -1);
out->setString(cx->runtime->emptyString); out->setString(cx->runtime->emptyString);
return true; return true;
} }
JS_ASSERT(start >= 0 && end >= 0); JS_ASSERT(start >= 0 && end >= 0);
JS_ASSERT(end >= start);
return createDependent(cx, start, end, out); return createDependent(cx, start, end, out);
} }
@ -621,7 +620,12 @@ RegExpStatics::createRightContext(JSContext *cx, Value *out) const
inline void inline void
RegExpStatics::getParen(size_t num, JSSubString *out) const RegExpStatics::getParen(size_t num, JSSubString *out) const
{ {
out->chars = matchPairsInput->chars() + getCrash(num + 1, 0); size_t pairNum = num + 1;
if (!pairIsPresent(pairNum)) {
*out = js_EmptySubString;
return;
}
out->chars = matchPairsInput->chars() + getCrash(pairNum, 0);
out->length = getParenLength(num); out->length = getParenLength(num);
} }
@ -641,14 +645,12 @@ RegExpStatics::getLastMatch(JSSubString *out) const
inline void inline void
RegExpStatics::getLastParen(JSSubString *out) const RegExpStatics::getLastParen(JSSubString *out) const
{ {
if (!pairCountCrash()) { size_t parenCount = getParenCount();
if (!parenCount) {
*out = js_EmptySubString; *out = js_EmptySubString;
return; return;
} }
size_t num = pairCount() - 1; getParen(parenCount - 1, out);
out->chars = matchPairsInput->chars() + getCrash(num, 0);
JS_CRASH_UNLESS(getCrash(num, 1) >= get(num, 0));
out->length = get(num, 1) - get(num, 0);
} }
inline void inline void