From de67205f5f4a33f51647e81acd34bfff74dbdd05 Mon Sep 17 00:00:00 2001 From: Chris Leary Date: Wed, 1 Dec 2010 16:33:49 -0800 Subject: [PATCH] Fix regexp match pair end-index == -1 assumption. (r=dmandelin, a=blocker b=605754) --HG-- extra : rebase_source : a4a73855736671e4dae8264522ce26a3505ab800 --- .../jit-test/tests/basic/bug605754-regexp.js | 2 ++ js/src/jsregexp.h | 31 ++++++++++++------- js/src/jsregexpinlines.h | 18 ++++++----- 3 files changed, 31 insertions(+), 20 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug605754-regexp.js diff --git a/js/src/jit-test/tests/basic/bug605754-regexp.js b/js/src/jit-test/tests/basic/bug605754-regexp.js new file mode 100644 index 00000000000..9acd3143779 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug605754-regexp.js @@ -0,0 +1,2 @@ +var result = "foobarbaz".replace(/foo(bar)?bar/, "$1"); +assertEq(result, "baz"); diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index ba0653cb567..0fdcab4b5f6 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -124,7 +124,9 @@ class RegExpStatics 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) { @@ -137,6 +139,19 @@ class RegExpStatics #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 { JS_ASSERT(pairNum < pairCount()); return matchPairs[2 * pairNum + which]; @@ -168,10 +183,6 @@ class RegExpStatics /* 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) { aboutToWrite(); pendingInput = input; @@ -204,8 +215,6 @@ class RegExpStatics matchPairs.clear(); } - bool pairIsPresent(size_t pairNum) { return get(0, 0) != -1; } - /* Corresponds to JSAPI functionality to set the pending RegExp input. */ void reset(JSString *newInput, bool newMultiline) { aboutToWrite(); @@ -238,12 +247,15 @@ class RegExpStatics 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; } size_t getParenCount() const { + /* The first pair is the whole match. */ JS_ASSERT(pairCount() > 0); return pairCount() - 1; } @@ -255,12 +267,6 @@ class RegExpStatics 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. */ bool createPendingInput(JSContext *cx, Value *out) const; @@ -275,6 +281,7 @@ class RegExpStatics /* Substring creators. */ + /* @param num Zero-indexed paren number (i.e. $1 is 0). */ void getParen(size_t num, JSSubString *out) const; void getLastMatch(JSSubString *out) const; void getLastParen(JSSubString *out) const; diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h index 17cb1e5c809..5c0f650fc84 100644 --- a/js/src/jsregexpinlines.h +++ b/js/src/jsregexpinlines.h @@ -265,7 +265,6 @@ RegExp::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemC } else { /* Missing parenthesized match. */ 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())) return NULL; } @@ -582,11 +581,11 @@ RegExpStatics::createLastParen(JSContext *cx, Value *out) const int start = get(num, 0); int end = get(num, 1); if (start == -1) { - JS_ASSERT(end == -1); out->setString(cx->runtime->emptyString); return true; } JS_ASSERT(start >= 0 && end >= 0); + JS_ASSERT(end >= start); return createDependent(cx, start, end, out); } @@ -621,7 +620,12 @@ RegExpStatics::createRightContext(JSContext *cx, Value *out) const inline void 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); } @@ -641,14 +645,12 @@ RegExpStatics::getLastMatch(JSSubString *out) const inline void RegExpStatics::getLastParen(JSSubString *out) const { - if (!pairCountCrash()) { + size_t parenCount = getParenCount(); + if (!parenCount) { *out = js_EmptySubString; return; } - size_t num = pairCount() - 1; - out->chars = matchPairsInput->chars() + getCrash(num, 0); - JS_CRASH_UNLESS(getCrash(num, 1) >= get(num, 0)); - out->length = get(num, 1) - get(num, 0); + getParen(parenCount - 1, out); } inline void