From 391bba5d44f4c06d6a8b4fcaa336fed55f51760d Mon Sep 17 00:00:00 2001 From: Chris Leary Date: Wed, 1 Dec 2010 16:34:10 -0800 Subject: [PATCH] Make paren indexing uniform. (r=dmandelin, a=blocker, b=605754) --- js/src/jsregexp.cpp | 18 ++++++------- js/src/jsregexp.h | 69 +++++++++++++++++++++++++++++++----------------- js/src/jsregexpinlines.h | 15 ++++++----- js/src/jsstr.cpp | 27 ++++++++++--------- 4 files changed, 76 insertions(+), 53 deletions(-) diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index cd6e86d2b5..ce5344d70f 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -401,15 +401,15 @@ DEFINE_STATIC_GETTER(static_lastParen_getter, return res->createLastParen(cx, DEFINE_STATIC_GETTER(static_leftContext_getter, return res->createLeftContext(cx, Valueify(vp))) DEFINE_STATIC_GETTER(static_rightContext_getter, return res->createRightContext(cx, Valueify(vp))) -DEFINE_STATIC_GETTER(static_paren1_getter, return res->createParen(cx, 0, Valueify(vp))) -DEFINE_STATIC_GETTER(static_paren2_getter, return res->createParen(cx, 1, Valueify(vp))) -DEFINE_STATIC_GETTER(static_paren3_getter, return res->createParen(cx, 2, Valueify(vp))) -DEFINE_STATIC_GETTER(static_paren4_getter, return res->createParen(cx, 3, Valueify(vp))) -DEFINE_STATIC_GETTER(static_paren5_getter, return res->createParen(cx, 4, Valueify(vp))) -DEFINE_STATIC_GETTER(static_paren6_getter, return res->createParen(cx, 5, Valueify(vp))) -DEFINE_STATIC_GETTER(static_paren7_getter, return res->createParen(cx, 6, Valueify(vp))) -DEFINE_STATIC_GETTER(static_paren8_getter, return res->createParen(cx, 7, Valueify(vp))) -DEFINE_STATIC_GETTER(static_paren9_getter, return res->createParen(cx, 8, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren1_getter, return res->createParen(cx, 1, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren2_getter, return res->createParen(cx, 2, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren3_getter, return res->createParen(cx, 3, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren4_getter, return res->createParen(cx, 4, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren5_getter, return res->createParen(cx, 5, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren6_getter, return res->createParen(cx, 6, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren7_getter, return res->createParen(cx, 7, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren8_getter, return res->createParen(cx, 8, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren9_getter, return res->createParen(cx, 9, Valueify(vp))) #define DEFINE_STATIC_SETTER(name, code) \ static JSBool \ diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index 0fdcab4b5f..7f6fae0243 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -70,16 +70,6 @@ class RegExpStatics bool createDependent(JSContext *cx, size_t start, size_t end, Value *out) const; - size_t pairCount() const { - JS_ASSERT(matchPairs.length() % 2 == 0); - return matchPairs.length() / 2; - } - - size_t pairCountCrash() const { - JS_CRASH_UNLESS(matchPairs.length() % 2 == 0); - return pairCount(); - } - void copyTo(RegExpStatics &dst) { dst.matchPairs.clear(); /* 'save' has already reserved space in matchPairs */ @@ -139,15 +129,22 @@ class RegExpStatics #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_CRASH_UNLESS(1 <= pairNum); + JS_CRASH_UNLESS(pairNum < pairCount()); + } + 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; + size_t getParenLength(size_t pairNum) const { + checkParenNum(pairNum); JS_CRASH_UNLESS(pairIsPresent(pairNum)); return getCrash(pairNum, 1) - getCrash(pairNum, 0); } @@ -231,6 +228,31 @@ class RegExpStatics /* 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; + } + + size_t pairCountCrash() const { + JS_CRASH_UNLESS(matchPairs.length() % 2 == 0); + return pairCount(); + } + + public: + size_t parenCount() const { + size_t pc = pairCount(); + JS_CRASH_UNLESS(pc); + return pc - 1; + } + JSString *getPendingInput() const { return pendingInput; } uintN getFlags() const { return flags; } bool multiline() const { return flags & JSREG_MULTILINE; } @@ -254,12 +276,6 @@ class RegExpStatics 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; - } - void mark(JSTracer *trc) const { if (pendingInput) JS_CALL_STRING_TRACER(trc, pendingInput, "res->pendingInput"); @@ -275,14 +291,19 @@ class RegExpStatics bool createLeftContext(JSContext *cx, Value *out) const; bool createRightContext(JSContext *cx, Value *out) const; - bool createParen(JSContext *cx, size_t parenNum, Value *out) const { - return makeMatch(cx, (parenNum + 1) * 2, parenNum + 1, out); + /* @param pairNum Any number >= 1. */ + bool createParen(JSContext *cx, size_t pairNum, Value *out) const { + JS_CRASH_UNLESS(pairNum >= 1); + if (pairNum >= pairCount()) { + out->setString(cx->runtime->emptyString); + return true; + } + return makeMatch(cx, pairNum * 2, pairNum, out); } /* 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 pairNum, JSSubString *out) const; void getLastMatch(JSSubString *out) const; void getLastParen(JSSubString *out) const; void getLeftContext(JSSubString *out) const; diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h index 5c0f650fc8..ff015c7fbc 100644 --- a/js/src/jsregexpinlines.h +++ b/js/src/jsregexpinlines.h @@ -244,7 +244,7 @@ RegExp::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemC /* * Create the result array for a match. Array contents: * 0: matched string - * 1..parenCount: paren matches + * 1..pairCount-1: paren matches */ JSObject *array = js_NewSlowArrayObject(cx); if (!array) @@ -618,15 +618,15 @@ RegExpStatics::createRightContext(JSContext *cx, Value *out) const } inline void -RegExpStatics::getParen(size_t num, JSSubString *out) const +RegExpStatics::getParen(size_t pairNum, JSSubString *out) const { - size_t pairNum = num + 1; + checkParenNum(pairNum); if (!pairIsPresent(pairNum)) { *out = js_EmptySubString; return; } out->chars = matchPairsInput->chars() + getCrash(pairNum, 0); - out->length = getParenLength(num); + out->length = getParenLength(pairNum); } inline void @@ -645,12 +645,13 @@ RegExpStatics::getLastMatch(JSSubString *out) const inline void RegExpStatics::getLastParen(JSSubString *out) const { - size_t parenCount = getParenCount(); - if (!parenCount) { + size_t pairCount = pairCountCrash(); + /* Note: the first pair is the whole match. */ + if (pairCount <= 1) { *out = js_EmptySubString; return; } - getParen(parenCount - 1, out); + getParen(pairCount - 1, out); } inline void diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index baf8a9f044..7f72461c20 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2003,13 +2003,13 @@ InterpretDollar(JSContext *cx, RegExpStatics *res, jschar *dp, jschar *ep, Repla if (JS7_ISDEC(dc)) { /* ECMA-262 Edition 3: 1-9 or 01-99 */ uintN num = JS7_UNDEC(dc); - if (num > res->getParenCount()) + if (num > res->parenCount()) return false; jschar *cp = dp + 2; if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { uintN tmp = 10 * num + JS7_UNDEC(dc); - if (tmp <= res->getParenCount()) { + if (tmp <= res->parenCount()) { cp++; num = tmp; } @@ -2017,13 +2017,14 @@ InterpretDollar(JSContext *cx, RegExpStatics *res, jschar *dp, jschar *ep, Repla if (num == 0) return false; - /* Adjust num from 1 $n-origin to 0 array-index-origin. */ - num--; *skip = cp - dp; - if (num < res->getParenCount()) - res->getParen(num, out); - else - *out = js_EmptySubString; + + JS_CRASH_UNLESS(num <= res->parenCount()); + /* + * Note: we index to get the paren with the (1-indexed) pair + * number, as opposed to a (0-indexed) paren number. + */ + res->getParen(num, out); return true; } @@ -2120,7 +2121,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. */ - uintN p = res->getParenCount(); + uintN p = res->parenCount(); uintN argc = 1 + p + 2; InvokeSessionGuard &session = rdata.session; @@ -2139,8 +2140,8 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t if (!res->createLastMatch(cx, &session[argi++])) return false; - for (size_t i = 0; i < res->getParenCount(); ++i) { - if (!res->createParen(cx, i, &session[argi++])) + for (size_t i = 0; i < res->parenCount(); ++i) { + if (!res->createParen(cx, i + 1, &session[argi++])) return false; } @@ -2758,11 +2759,11 @@ str_split(JSContext *cx, uintN argc, Value *vp) * substring that was delimited. */ if (re && sep->chars) { - for (uintN num = 0; num < res->getParenCount(); num++) { + for (uintN num = 0; num < res->parenCount(); num++) { if (limited && len >= limit) break; JSSubString parsub; - res->getParen(num, &parsub); + res->getParen(num + 1, &parsub); sub = js_NewStringCopyN(cx, parsub.chars, parsub.length); if (!sub || !splits.append(StringValue(sub))) return false; -- 2.11.4.GIT