зеркало из https://github.com/mozilla/pjs.git
Don't save static RegExp state across lambda replace calls (560358, r=brendan).
This commit is contained in:
Родитель
bac98abcd5
Коммит
ccff6a6573
|
@ -5524,19 +5524,8 @@ JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline)
|
|||
JS_PUBLIC_API(void)
|
||||
JS_ClearRegExpStatics(JSContext *cx)
|
||||
{
|
||||
JSRegExpStatics *res;
|
||||
|
||||
/* No locking required, cx is thread-private and input must be live. */
|
||||
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->regExpStatics.clear();
|
||||
cx->runtime->gcPoke = JS_TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1173,9 +1173,42 @@ namespace js {
|
|||
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
|
||||
{
|
||||
explicit JSContext(JSRuntime *rt) : runtime(rt), busyArrays(this) {}
|
||||
explicit JSContext(JSRuntime *rt) :
|
||||
runtime(rt), regExpStatics(this), busyArrays(this) {}
|
||||
|
||||
/*
|
||||
* If this flag is set, we were asked to call back the operation callback
|
||||
|
@ -1261,7 +1294,7 @@ struct JSContext
|
|||
/* Storage to root recently allocated GC things and script result. */
|
||||
JSWeakRoots weakRoots;
|
||||
|
||||
/* Regular expression class statics (XXX not shared globally). */
|
||||
/* Regular expression class statics. */
|
||||
JSRegExpStatics regExpStatics;
|
||||
|
||||
/* State for object and array toSource conversion. */
|
||||
|
|
|
@ -4865,11 +4865,10 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
|
|||
|
||||
const jschar *cp, *ep;
|
||||
size_t i, length, start;
|
||||
JSSubString *morepar;
|
||||
JSBool ok;
|
||||
JSRegExpStatics *res;
|
||||
ptrdiff_t matchlen;
|
||||
uintN num, morenum;
|
||||
uintN num;
|
||||
JSString *parstr, *matchstr;
|
||||
JSObject *obj;
|
||||
|
||||
|
@ -4973,45 +4972,22 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
|
|||
|
||||
res = &cx->regExpStatics;
|
||||
res->input = str;
|
||||
res->parenCount = uint16(re->parenCount);
|
||||
if (!res->parens.resize(re->parenCount)) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
if (re->parenCount == 0) {
|
||||
res->lastParen = js_EmptySubString;
|
||||
} else {
|
||||
for (num = 0; num < re->parenCount; num++) {
|
||||
JSSubString *sub = &res->parens[num];
|
||||
parsub = &result->parens[num];
|
||||
if (num < 9) {
|
||||
if (parsub->index == -1) {
|
||||
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;
|
||||
}
|
||||
if (parsub->index == -1) {
|
||||
sub->chars = NULL;
|
||||
sub->length = 0;
|
||||
} else {
|
||||
morenum = num - 9;
|
||||
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;
|
||||
}
|
||||
sub->chars = gData.cpbegin + parsub->index;
|
||||
sub->length = parsub->length;
|
||||
}
|
||||
if (test)
|
||||
continue;
|
||||
|
@ -5209,14 +5185,9 @@ JS_FRIEND_API(void)
|
|||
js_SaveAndClearRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
|
||||
AutoValueRooter *tvr)
|
||||
{
|
||||
*statics = cx->regExpStatics;
|
||||
statics->copy(cx->regExpStatics);
|
||||
if (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);
|
||||
}
|
||||
|
||||
|
@ -5225,8 +5196,7 @@ js_RestoreRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
|
|||
AutoValueRooter *tvr)
|
||||
{
|
||||
/* Clear/free any new JSRegExpStatics data before clobbering. */
|
||||
JS_ClearRegExpStatics(cx);
|
||||
cx->regExpStatics = *statics;
|
||||
cx->regExpStatics.copy(*statics);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -5278,7 +5248,7 @@ regexp_static_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|||
sub = &res->rightContext;
|
||||
break;
|
||||
default:
|
||||
sub = REGEXP_PAREN_SUBSTRING(res, slot);
|
||||
sub = (size_t(slot) < res->parens.length()) ? &res->parens[slot] : &js_EmptySubString;
|
||||
break;
|
||||
}
|
||||
str = js_NewStringCopyN(cx, sub->chars, sub->length);
|
||||
|
|
|
@ -52,19 +52,6 @@
|
|||
|
||||
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; }
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
|
@ -96,17 +83,6 @@ typedef struct RECharSet {
|
|||
} u;
|
||||
} 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;
|
||||
|
||||
struct JSRegExp {
|
||||
|
|
|
@ -1719,13 +1719,13 @@ InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata,
|
|||
if (JS7_ISDEC(dc)) {
|
||||
/* ECMA-262 Edition 3: 1-9 or 01-99 */
|
||||
num = JS7_UNDEC(dc);
|
||||
if (num > res->parenCount)
|
||||
if (num > res->parens.length())
|
||||
return NULL;
|
||||
|
||||
cp = dp + 2;
|
||||
if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
|
||||
tmp = 10 * num + JS7_UNDEC(dc);
|
||||
if (tmp <= res->parenCount) {
|
||||
if (tmp <= res->parens.length()) {
|
||||
cp++;
|
||||
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. */
|
||||
num--;
|
||||
*skip = cp - dp;
|
||||
return REGEXP_PAREN_SUBSTRING(res, num);
|
||||
return (num < res->parens.length()) ? &res->parens[num] : &js_EmptySubString;
|
||||
}
|
||||
|
||||
*skip = 2;
|
||||
|
@ -1780,8 +1780,6 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
|
|||
|
||||
lambda = rdata.lambda;
|
||||
if (lambda) {
|
||||
uintN i, m, n;
|
||||
|
||||
LeaveTrace(cx);
|
||||
|
||||
/*
|
||||
|
@ -1802,18 +1800,6 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
|
|||
}
|
||||
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. */
|
||||
jsval *sp = invokevp;
|
||||
*sp++ = OBJECT_TO_JSVAL(lambda);
|
||||
|
@ -1821,27 +1807,13 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
|
|||
|
||||
/* Push $&, $1, $2, ... */
|
||||
if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp))
|
||||
goto lambda_out;
|
||||
return false;
|
||||
|
||||
i = 0;
|
||||
m = cx->regExpStatics.parenCount;
|
||||
n = JS_MIN(m, 9);
|
||||
for (uintN j = 0; i < n; i++, j++) {
|
||||
if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[j], sp))
|
||||
goto lambda_out;
|
||||
uintN i = 0;
|
||||
for (uintN n = cx->regExpStatics.parens.length(); i < n; i++) {
|
||||
if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[i], sp))
|
||||
return false;
|
||||
}
|
||||
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. */
|
||||
for (; i < p; i++)
|
||||
|
@ -1852,7 +1824,7 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
|
|||
*sp++ = STRING_TO_JSVAL(rdata.str);
|
||||
|
||||
if (!js_Invoke(cx, argc, invokevp, 0))
|
||||
goto lambda_out;
|
||||
return false;
|
||||
|
||||
/*
|
||||
* NB: we count on the newborn string root to hold any string
|
||||
|
@ -1861,18 +1833,12 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
|
|||
*/
|
||||
repstr = js_ValueToString(cx, *invokevp);
|
||||
if (!repstr)
|
||||
goto lambda_out;
|
||||
return false;
|
||||
|
||||
rdata.repstr = repstr;
|
||||
*sizep = repstr->length();
|
||||
|
||||
ok = true;
|
||||
|
||||
lambda_out:
|
||||
if (freeMoreParens)
|
||||
cx->free(cx->regExpStatics.moreParens);
|
||||
cx->regExpStatics = save;
|
||||
return ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
repstr = rdata.repstr;
|
||||
|
@ -2212,10 +2178,11 @@ str_split(JSContext *cx, uintN argc, jsval *vp)
|
|||
* substring that was delimited.
|
||||
*/
|
||||
if (re && sep->chars) {
|
||||
for (uintN num = 0; num < cx->regExpStatics.parenCount; num++) {
|
||||
JSRegExpStatics *res = &cx->regExpStatics;
|
||||
for (uintN num = 0; num < res->parens.length(); num++) {
|
||||
if (limited && len >= limit)
|
||||
break;
|
||||
JSSubString *parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num);
|
||||
JSSubString *parsub = &res->parens[num];
|
||||
sub = js_NewStringCopyN(cx, parsub->chars, parsub->length);
|
||||
if (!sub || !splits.push(sub))
|
||||
return false;
|
||||
|
|
|
@ -534,7 +534,7 @@ XPC_SJOW_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|||
NS_STACK_CLASS class SafeCallGuard {
|
||||
public:
|
||||
SafeCallGuard(JSContext *cx, nsIPrincipal *principal)
|
||||
: cx(cx), tvr(cx) {
|
||||
: cx(cx), statics(cx), tvr(cx) {
|
||||
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
|
||||
if (ssm) {
|
||||
// Note: We pass null as the target frame pointer because we know that
|
||||
|
|
Загрузка…
Ссылка в новой задаче