зеркало из https://github.com/mozilla/pjs.git
r=norris
Bug #20547 & 20549 - ECMA3 compatible handling of String.replace and fixes to $ handling.
This commit is contained in:
Родитель
8af17f66e2
Коммит
e94220b323
|
@ -1114,14 +1114,17 @@ nothex:
|
|||
}
|
||||
|
||||
JSRegExp *
|
||||
js_NewRegExp(JSContext *cx, JSString *str, uintN flags)
|
||||
js_NewRegExp(JSContext *cx, JSString *str, uintN flags, JSBool flat)
|
||||
{
|
||||
JSRegExp *re;
|
||||
void *mark;
|
||||
CompilerState state;
|
||||
RENode *ren, *end;
|
||||
RENode *ren, *ren2, *end;
|
||||
size_t resize;
|
||||
|
||||
const jschar *cp;
|
||||
uintN len;
|
||||
|
||||
re = NULL;
|
||||
mark = JS_ARENA_MARK(&cx->tempPool);
|
||||
|
||||
|
@ -1132,7 +1135,41 @@ js_NewRegExp(JSContext *cx, JSString *str, uintN flags)
|
|||
state.parenCount = 0;
|
||||
state.progLength = 0;
|
||||
|
||||
ren = ParseRegExp(&state);
|
||||
if (flat) {
|
||||
len = str->length;
|
||||
cp = str->chars;
|
||||
ren = NULL;
|
||||
while (len > 0) {
|
||||
ren2 = NewRENode(&state,
|
||||
(len == 1) ? REOP_FLAT1 : REOP_FLAT, (void *)cp);
|
||||
if (!ren2)
|
||||
goto out;
|
||||
ren2->flags = RENODE_NONEMPTY;
|
||||
if (len > 1) {
|
||||
if (len > REOP_FLATLEN_MAX) {
|
||||
cp += REOP_FLATLEN_MAX;
|
||||
len -= REOP_FLATLEN_MAX;
|
||||
}
|
||||
else {
|
||||
cp += len;
|
||||
len = 0;
|
||||
}
|
||||
ren2->u.kid2 = (void *)cp;
|
||||
} else {
|
||||
ren2->flags |= RENODE_SINGLE;
|
||||
ren2->u.chr = *cp;
|
||||
len = 0;
|
||||
}
|
||||
if (ren) {
|
||||
if (!SetNext(&state, ren, ren2));
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
ren = ren2;
|
||||
}
|
||||
}
|
||||
else
|
||||
ren = ParseRegExp(&state);
|
||||
if (!ren)
|
||||
goto out;
|
||||
|
||||
|
@ -1164,7 +1201,7 @@ out:
|
|||
}
|
||||
|
||||
JSRegExp *
|
||||
js_NewRegExpOpt(JSContext *cx, JSString *str, JSString *opt)
|
||||
js_NewRegExpOpt(JSContext *cx, JSString *str, JSString *opt, JSBool flat)
|
||||
{
|
||||
uintN flags;
|
||||
jschar *cp;
|
||||
|
@ -1192,7 +1229,7 @@ js_NewRegExpOpt(JSContext *cx, JSString *str, JSString *opt)
|
|||
}
|
||||
}
|
||||
}
|
||||
return js_NewRegExp(cx, str, flags);
|
||||
return js_NewRegExp(cx, str, flags, flat);
|
||||
}
|
||||
|
||||
static void freeRENtree(JSContext *cx, RENode *ren, RENode *stop)
|
||||
|
@ -2371,7 +2408,7 @@ regexp_xdrObject(JSXDRState *xdr, JSObject **objp)
|
|||
*objp = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL);
|
||||
if (!*objp)
|
||||
return JS_FALSE;
|
||||
re = js_NewRegExp(xdr->cx, source, flags);
|
||||
re = js_NewRegExp(xdr->cx, source, flags, JS_FALSE);
|
||||
if (!re)
|
||||
return JS_FALSE;
|
||||
if (!JS_SetPrivate(xdr->cx, *objp, re)) {
|
||||
|
@ -2484,7 +2521,7 @@ regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
argv[1] = STRING_TO_JSVAL(opt);
|
||||
}
|
||||
}
|
||||
re = js_NewRegExpOpt(cx, str, opt);
|
||||
re = js_NewRegExpOpt(cx, str, opt, JS_FALSE);
|
||||
if (!re) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
|
@ -2626,7 +2663,7 @@ js_NewRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags)
|
|||
str = js_NewStringCopyN(cx, chars, length, 0);
|
||||
if (!str)
|
||||
return NULL;
|
||||
re = js_NewRegExp(cx, str, flags);
|
||||
re = js_NewRegExp(cx, str, flags, JS_FALSE);
|
||||
if (!re)
|
||||
return NULL;
|
||||
obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL);
|
||||
|
|
|
@ -74,10 +74,10 @@ struct JSRegExp {
|
|||
};
|
||||
|
||||
extern JSRegExp *
|
||||
js_NewRegExp(JSContext *cx, JSString *str, uintN flags);
|
||||
js_NewRegExp(JSContext *cx, JSString *str, uintN flags, JSBool flat);
|
||||
|
||||
extern JSRegExp *
|
||||
js_NewRegExpOpt(JSContext *cx, JSString *str, JSString *opt);
|
||||
js_NewRegExpOpt(JSContext *cx, JSString *str, JSString *opt, JSBool flat);
|
||||
|
||||
extern void
|
||||
js_DestroyRegExp(JSContext *cx, JSRegExp *re);
|
||||
|
|
|
@ -255,10 +255,10 @@ str_uneval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
#endif
|
||||
|
||||
static JSFunctionSpec string_functions[] = {
|
||||
{"escape", str_escape, 1,0,0},
|
||||
{"unescape", str_unescape, 1,0,0},
|
||||
{"escape", str_escape, 1,0,0},
|
||||
{"unescape", str_unescape, 1,0,0},
|
||||
#if JS_HAS_UNEVAL
|
||||
{"uneval", str_uneval, 1,0,0},
|
||||
{"uneval", str_uneval, 1,0,0},
|
||||
#endif
|
||||
{0,0,0,0,0}
|
||||
};
|
||||
|
@ -808,7 +808,7 @@ typedef struct GlobData {
|
|||
static JSBool
|
||||
match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
JSBool (*glob)(JSContext *cx, jsint count, GlobData *data),
|
||||
GlobData *data, jsval *rval)
|
||||
GlobData *data, jsval *rval, JSBool forceFlat)
|
||||
{
|
||||
JSString *str, *src, *opt;
|
||||
JSObject *reobj;
|
||||
|
@ -828,7 +828,7 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
re = JS_GetPrivate(cx, reobj);
|
||||
} else {
|
||||
if (JSVAL_IS_VOID(argv[0]))
|
||||
re = js_NewRegExp(cx, cx->runtime->emptyString, 0);
|
||||
re = js_NewRegExp(cx, cx->runtime->emptyString, 0, JS_FALSE);
|
||||
else {
|
||||
src = js_ValueToString(cx, argv[0]);
|
||||
if (!src)
|
||||
|
@ -841,7 +841,7 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
} else {
|
||||
opt = NULL;
|
||||
}
|
||||
re = js_NewRegExpOpt(cx, src, opt);
|
||||
re = js_NewRegExpOpt(cx, src, opt, forceFlat);
|
||||
}
|
||||
if (!re)
|
||||
return JS_FALSE;
|
||||
|
@ -933,7 +933,7 @@ str_match(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
mdata.arrayobj = NULL;
|
||||
if (!js_AddRoot(cx, &mdata.arrayobj, "mdata.arrayobj"))
|
||||
return JS_FALSE;
|
||||
ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval);
|
||||
ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval, JS_FALSE);
|
||||
if (ok && mdata.arrayobj)
|
||||
*rval = OBJECT_TO_JSVAL(mdata.arrayobj);
|
||||
js_RemoveRoot(cx->runtime, &mdata.arrayobj);
|
||||
|
@ -951,7 +951,7 @@ str_search(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
|
||||
mdata.base.optarg = 1;
|
||||
mdata.base.mode = GLOB_SEARCH;
|
||||
return match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval);
|
||||
return match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval, JS_FALSE);
|
||||
#else
|
||||
return str_nyi(cx, "search");
|
||||
#endif
|
||||
|
@ -969,6 +969,8 @@ typedef struct ReplaceData {
|
|||
jsint leftIndex; /* left context index in base.str->chars */
|
||||
} ReplaceData;
|
||||
|
||||
static JSSubString dollarStr;
|
||||
|
||||
static JSSubString *
|
||||
interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip)
|
||||
{
|
||||
|
@ -986,19 +988,31 @@ interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip)
|
|||
res = &cx->regExpStatics;
|
||||
dc = dp[1];
|
||||
if (JS7_ISDEC(dc)) {
|
||||
if (dc == '0')
|
||||
return NULL;
|
||||
|
||||
/* Check for overflow to avoid gobbling arbitrary decimal digits. */
|
||||
num = 0;
|
||||
cp = dp;
|
||||
while ((dc = *++cp) != 0 && JS7_ISDEC(dc)) {
|
||||
tmp = 10 * num + JS7_UNDEC(dc);
|
||||
if (tmp < num)
|
||||
break;
|
||||
num = tmp;
|
||||
}
|
||||
if (cx->version != JSVERSION_DEFAULT &&
|
||||
cx->version <= JSVERSION_1_4) {
|
||||
if (dc == '0')
|
||||
return NULL;
|
||||
|
||||
/* Check for overflow to avoid gobbling arbitrary decimal digits. */
|
||||
num = 0;
|
||||
cp = dp;
|
||||
while ((dc = *++cp) != 0 && JS7_ISDEC(dc)) {
|
||||
tmp = 10 * num + JS7_UNDEC(dc);
|
||||
if (tmp < num)
|
||||
break;
|
||||
num = tmp;
|
||||
}
|
||||
}
|
||||
else { /* ECMA 3, 1-9 or 01-99 */
|
||||
num = JS7_UNDEC(dc);
|
||||
cp = dp + 2;
|
||||
dc = dp[2];
|
||||
if ((dc != 0) && JS7_ISDEC(dc)) {
|
||||
num = 10 * num + JS7_UNDEC(dc);
|
||||
cp++;
|
||||
}
|
||||
if (num == 0) return NULL;
|
||||
}
|
||||
/* Adjust num from 1 $n-origin to 0 array-index-origin. */
|
||||
num--;
|
||||
*skip = cp - dp;
|
||||
|
@ -1007,6 +1021,10 @@ interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip)
|
|||
|
||||
*skip = 2;
|
||||
switch (dc) {
|
||||
case '$':
|
||||
dollarStr.chars = dp;
|
||||
dollarStr.length = 1;
|
||||
return &dollarStr;
|
||||
case '&':
|
||||
return &res->lastMatch;
|
||||
case '+':
|
||||
|
@ -1131,10 +1149,14 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep)
|
|||
|
||||
repstr = rdata->repstr;
|
||||
replen = repstr->length;
|
||||
for (dp = rdata->dollar; dp; dp = js_strchr(dp + 1, '$')) {
|
||||
for (dp = rdata->dollar; dp; dp = js_strchr(dp, '$')) {
|
||||
sub = interpret_dollar(cx, dp, rdata, &skip);
|
||||
if (sub)
|
||||
if (sub) {
|
||||
replen += sub->length - skip;
|
||||
dp += skip;
|
||||
}
|
||||
else
|
||||
dp++;
|
||||
}
|
||||
*sizep = replen;
|
||||
return JS_TRUE;
|
||||
|
@ -1162,8 +1184,11 @@ do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars)
|
|||
js_strncpy(chars, sub->chars, len);
|
||||
chars += len;
|
||||
cp += skip;
|
||||
dp += skip;
|
||||
}
|
||||
dp = js_strchr(dp + 1, '$');
|
||||
else
|
||||
dp++;
|
||||
dp = js_strchr(dp, '$');
|
||||
}
|
||||
js_strncpy(chars, cp, repstr->length - (cp - repstr->chars));
|
||||
}
|
||||
|
@ -1239,7 +1264,10 @@ str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
rdata.length = 0;
|
||||
rdata.index = 0;
|
||||
rdata.leftIndex = 0;
|
||||
if (!match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval))
|
||||
/* for ECMA 3, the first argument is to be treated as a string
|
||||
(i.e. converted to one if necessary) UNLESS it's a reg.exp object */
|
||||
if (!match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval,
|
||||
(cx->version == JSVERSION_DEFAULT || cx->version > JSVERSION_1_4)))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!rdata.chars) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче