This commit is contained in:
Andreas Gal 2010-04-26 19:07:10 -07:00
Родитель f30b02e603 50f747b529
Коммит 46cc50ab96
6 изменённых файлов: 130 добавлений и 65 удалений

Просмотреть файл

@ -5522,8 +5522,19 @@ JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline)
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
JS_ClearRegExpStatics(JSContext *cx) JS_ClearRegExpStatics(JSContext *cx)
{ {
JSRegExpStatics *res;
/* No locking required, cx is thread-private and input must be live. */ /* No locking required, cx is thread-private and input must be live. */
cx->regExpStatics.clear(); res = &cx->regExpStatics;
res->input = NULL;
res->multiline = JS_FALSE;
res->parenCount = 0;
res->lastMatch = res->lastParen = js_EmptySubString;
res->leftContext = res->rightContext = js_EmptySubString;
if (res->moreParens) {
cx->free(res->moreParens);
res->moreParens = NULL;
}
cx->runtime->gcPoke = JS_TRUE; cx->runtime->gcPoke = JS_TRUE;
} }

Просмотреть файл

@ -1180,42 +1180,9 @@ namespace js {
class AutoGCRooter; class AutoGCRooter;
} }
struct JSRegExpStatics {
JSString *input; /* input string to match (perl $_, GC root) */
JSBool multiline; /* whether input contains newlines (perl $*) */
JSSubString lastMatch; /* last string matched (perl $&) */
JSSubString lastParen; /* last paren matched (perl $+) */
JSSubString leftContext; /* input to left of last match (perl $`) */
JSSubString rightContext; /* input to right of last match (perl $') */
js::Vector<JSSubString> parens; /* last set of parens matched (perl $1, $2) */
JSRegExpStatics(JSContext *cx) : parens(cx) {}
bool copy(const JSRegExpStatics& other) {
input = other.input;
multiline = other.multiline;
lastMatch = other.lastMatch;
lastParen = other.lastParen;
leftContext = other.leftContext;
rightContext = other.rightContext;
if (!parens.resize(other.parens.length()))
return false;
memcpy(parens.begin(), other.parens.begin(), sizeof(JSSubString) * parens.length());
return true;
}
void clear() {
input = NULL;
multiline = false;
lastMatch = lastParen = leftContext = rightContext = js_EmptySubString;
parens.clear();
}
};
struct JSContext struct JSContext
{ {
explicit JSContext(JSRuntime *rt) : explicit JSContext(JSRuntime *rt) : runtime(rt), busyArrays(this) {}
runtime(rt), regExpStatics(this), busyArrays(this) {}
/* /*
* If this flag is set, we were asked to call back the operation callback * If this flag is set, we were asked to call back the operation callback
@ -1301,7 +1268,7 @@ struct JSContext
/* Storage to root recently allocated GC things and script result. */ /* Storage to root recently allocated GC things and script result. */
JSWeakRoots weakRoots; JSWeakRoots weakRoots;
/* Regular expression class statics. */ /* Regular expression class statics (XXX not shared globally). */
JSRegExpStatics regExpStatics; JSRegExpStatics regExpStatics;
/* State for object and array toSource conversion. */ /* State for object and array toSource conversion. */

Просмотреть файл

@ -4865,10 +4865,11 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
const jschar *cp, *ep; const jschar *cp, *ep;
size_t i, length, start; size_t i, length, start;
JSSubString *morepar;
JSBool ok; JSBool ok;
JSRegExpStatics *res; JSRegExpStatics *res;
ptrdiff_t matchlen; ptrdiff_t matchlen;
uintN num; uintN num, morenum;
JSString *parstr, *matchstr; JSString *parstr, *matchstr;
JSObject *obj; JSObject *obj;
@ -4972,22 +4973,45 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
res = &cx->regExpStatics; res = &cx->regExpStatics;
res->input = str; res->input = str;
if (!res->parens.resize(re->parenCount)) { res->parenCount = uint16(re->parenCount);
ok = JS_FALSE;
goto out;
}
if (re->parenCount == 0) { if (re->parenCount == 0) {
res->lastParen = js_EmptySubString; res->lastParen = js_EmptySubString;
} else { } else {
for (num = 0; num < re->parenCount; num++) { for (num = 0; num < re->parenCount; num++) {
JSSubString *sub = &res->parens[num];
parsub = &result->parens[num]; parsub = &result->parens[num];
if (parsub->index == -1) { if (num < 9) {
sub->chars = NULL; if (parsub->index == -1) {
sub->length = 0; res->parens[num].chars = NULL;
res->parens[num].length = 0;
} else {
res->parens[num].chars = gData.cpbegin + parsub->index;
res->parens[num].length = parsub->length;
}
} else { } else {
sub->chars = gData.cpbegin + parsub->index; morenum = num - 9;
sub->length = parsub->length; morepar = res->moreParens;
if (!morepar) {
res->moreLength = 10;
morepar = (JSSubString*)
cx->malloc(10 * sizeof(JSSubString));
} else if (morenum >= res->moreLength) {
res->moreLength += 10;
morepar = (JSSubString*)
cx->realloc(morepar,
res->moreLength * sizeof(JSSubString));
}
if (!morepar) {
ok = JS_FALSE;
goto out;
}
res->moreParens = morepar;
if (parsub->index == -1) {
morepar[morenum].chars = NULL;
morepar[morenum].length = 0;
} else {
morepar[morenum].chars = gData.cpbegin + parsub->index;
morepar[morenum].length = parsub->length;
}
} }
if (test) if (test)
continue; continue;
@ -5185,9 +5209,14 @@ JS_FRIEND_API(void)
js_SaveAndClearRegExpStatics(JSContext *cx, JSRegExpStatics *statics, js_SaveAndClearRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
AutoValueRooter *tvr) AutoValueRooter *tvr)
{ {
statics->copy(cx->regExpStatics); *statics = cx->regExpStatics;
if (statics->input) if (statics->input)
tvr->setString(statics->input); tvr->setString(statics->input);
/*
* Prevent JS_ClearRegExpStatics from freeing moreParens, since we've only
* moved it elsewhere (into statics->moreParens).
*/
cx->regExpStatics.moreParens = NULL;
JS_ClearRegExpStatics(cx); JS_ClearRegExpStatics(cx);
} }
@ -5196,7 +5225,8 @@ js_RestoreRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
AutoValueRooter *tvr) AutoValueRooter *tvr)
{ {
/* Clear/free any new JSRegExpStatics data before clobbering. */ /* Clear/free any new JSRegExpStatics data before clobbering. */
cx->regExpStatics.copy(*statics); JS_ClearRegExpStatics(cx);
cx->regExpStatics = *statics;
} }
void void
@ -5248,7 +5278,7 @@ regexp_static_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
sub = &res->rightContext; sub = &res->rightContext;
break; break;
default: default:
sub = (size_t(slot) < res->parens.length()) ? &res->parens[slot] : &js_EmptySubString; sub = REGEXP_PAREN_SUBSTRING(res, slot);
break; break;
} }
str = js_NewStringCopyN(cx, sub->chars, sub->length); str = js_NewStringCopyN(cx, sub->chars, sub->length);

Просмотреть файл

@ -52,6 +52,19 @@
JS_BEGIN_EXTERN_C JS_BEGIN_EXTERN_C
struct JSRegExpStatics {
JSString *input; /* input string to match (perl $_, GC root) */
JSBool multiline; /* whether input contains newlines (perl $*) */
uint16 parenCount; /* number of valid elements in parens[] */
uint16 moreLength; /* number of allocated elements in moreParens */
JSSubString parens[9]; /* last set of parens matched (perl $1, $2) */
JSSubString *moreParens; /* null or realloc'd vector for $10, etc. */
JSSubString lastMatch; /* last string matched (perl $&) */
JSSubString lastParen; /* last paren matched (perl $+) */
JSSubString leftContext; /* input to left of last match (perl $`) */
JSSubString rightContext; /* input to right of last match (perl $') */
};
namespace js { class AutoValueRooter; } namespace js { class AutoValueRooter; }
extern JS_FRIEND_API(void) extern JS_FRIEND_API(void)
@ -83,6 +96,17 @@ typedef struct RECharSet {
} u; } u;
} RECharSet; } RECharSet;
/*
* This macro is safe because moreParens is guaranteed to be allocated and big
* enough to hold parenCount, or else be null when parenCount is 0.
*/
#define REGEXP_PAREN_SUBSTRING(res, num) \
(((jsuint)(num) < (jsuint)(res)->parenCount) \
? ((jsuint)(num) < 9) \
? &(res)->parens[num] \
: &(res)->moreParens[(num) - 9] \
: &js_EmptySubString)
typedef struct RENode RENode; typedef struct RENode RENode;
struct JSRegExp { struct JSRegExp {

Просмотреть файл

@ -1719,13 +1719,13 @@ InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata,
if (JS7_ISDEC(dc)) { if (JS7_ISDEC(dc)) {
/* ECMA-262 Edition 3: 1-9 or 01-99 */ /* ECMA-262 Edition 3: 1-9 or 01-99 */
num = JS7_UNDEC(dc); num = JS7_UNDEC(dc);
if (num > res->parens.length()) if (num > res->parenCount)
return NULL; return NULL;
cp = dp + 2; cp = dp + 2;
if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
tmp = 10 * num + JS7_UNDEC(dc); tmp = 10 * num + JS7_UNDEC(dc);
if (tmp <= res->parens.length()) { if (tmp <= res->parenCount) {
cp++; cp++;
num = tmp; num = tmp;
} }
@ -1736,7 +1736,7 @@ InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata,
/* Adjust num from 1 $n-origin to 0 array-index-origin. */ /* Adjust num from 1 $n-origin to 0 array-index-origin. */
num--; num--;
*skip = cp - dp; *skip = cp - dp;
return (num < res->parens.length()) ? &res->parens[num] : &js_EmptySubString; return REGEXP_PAREN_SUBSTRING(res, num);
} }
*skip = 2; *skip = 2;
@ -1780,6 +1780,8 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
lambda = rdata.lambda; lambda = rdata.lambda;
if (lambda) { if (lambda) {
uintN i, m, n;
LeaveTrace(cx); LeaveTrace(cx);
/* /*
@ -1800,6 +1802,18 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
} }
jsval* invokevp = rdata.invokevp; jsval* invokevp = rdata.invokevp;
MUST_FLOW_THROUGH("lambda_out");
bool ok = false;
bool freeMoreParens = false;
/*
* Save the regExpStatics from the current regexp, since they may be
* clobbered by a RegExp usage in the lambda function. Note that all
* members of JSRegExpStatics are JSSubStrings, so not GC roots, save
* input, which is rooted otherwise via vp[1] in str_replace.
*/
JSRegExpStatics save = cx->regExpStatics;
/* Push lambda and its 'this' parameter. */ /* Push lambda and its 'this' parameter. */
jsval *sp = invokevp; jsval *sp = invokevp;
*sp++ = OBJECT_TO_JSVAL(lambda); *sp++ = OBJECT_TO_JSVAL(lambda);
@ -1807,13 +1821,27 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
/* Push $&, $1, $2, ... */ /* Push $&, $1, $2, ... */
if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp)) if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp))
return false; goto lambda_out;
uintN i = 0; i = 0;
for (uintN n = cx->regExpStatics.parens.length(); i < n; i++) { m = cx->regExpStatics.parenCount;
if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[i], sp)) n = JS_MIN(m, 9);
return false; for (uintN j = 0; i < n; i++, j++) {
if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[j], sp))
goto lambda_out;
} }
for (uintN j = 0; i < m; i++, j++) {
if (!PushRegExpSubstr(cx, cx->regExpStatics.moreParens[j], sp))
goto lambda_out;
}
/*
* We need to clear moreParens in the top-of-stack cx->regExpStatics
* so it won't be possibly realloc'ed, leaving the bottom-of-stack
* moreParens pointing to freed memory.
*/
cx->regExpStatics.moreParens = NULL;
freeMoreParens = true;
/* Make sure to push undefined for any unmatched parens. */ /* Make sure to push undefined for any unmatched parens. */
for (; i < p; i++) for (; i < p; i++)
@ -1824,7 +1852,7 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
*sp++ = STRING_TO_JSVAL(rdata.str); *sp++ = STRING_TO_JSVAL(rdata.str);
if (!js_Invoke(cx, argc, invokevp, 0)) if (!js_Invoke(cx, argc, invokevp, 0))
return false; goto lambda_out;
/* /*
* NB: we count on the newborn string root to hold any string * NB: we count on the newborn string root to hold any string
@ -1833,12 +1861,18 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
*/ */
repstr = js_ValueToString(cx, *invokevp); repstr = js_ValueToString(cx, *invokevp);
if (!repstr) if (!repstr)
return false; goto lambda_out;
rdata.repstr = repstr; rdata.repstr = repstr;
*sizep = repstr->length(); *sizep = repstr->length();
return true; ok = true;
lambda_out:
if (freeMoreParens)
cx->free(cx->regExpStatics.moreParens);
cx->regExpStatics = save;
return ok;
} }
repstr = rdata.repstr; repstr = rdata.repstr;
@ -2178,11 +2212,10 @@ str_split(JSContext *cx, uintN argc, jsval *vp)
* substring that was delimited. * substring that was delimited.
*/ */
if (re && sep->chars) { if (re && sep->chars) {
JSRegExpStatics *res = &cx->regExpStatics; for (uintN num = 0; num < cx->regExpStatics.parenCount; num++) {
for (uintN num = 0; num < res->parens.length(); num++) {
if (limited && len >= limit) if (limited && len >= limit)
break; break;
JSSubString *parsub = &res->parens[num]; JSSubString *parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num);
sub = js_NewStringCopyN(cx, parsub->chars, parsub->length); sub = js_NewStringCopyN(cx, parsub->chars, parsub->length);
if (!sub || !splits.push(sub)) if (!sub || !splits.push(sub))
return false; return false;

Просмотреть файл

@ -534,7 +534,7 @@ XPC_SJOW_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
NS_STACK_CLASS class SafeCallGuard { NS_STACK_CLASS class SafeCallGuard {
public: public:
SafeCallGuard(JSContext *cx, nsIPrincipal *principal) SafeCallGuard(JSContext *cx, nsIPrincipal *principal)
: cx(cx), statics(cx), tvr(cx) { : cx(cx), tvr(cx) {
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
if (ssm) { if (ssm) {
// Note: We pass null as the target frame pointer because we know that // Note: We pass null as the target frame pointer because we know that