зеркало из https://github.com/mozilla/gecko-dev.git
Fix regexp object (scope) vs GC deadlock (76233, r=beard, sr=shaver, a=asa).
This commit is contained in:
Родитель
09cab04692
Коммит
28427e3770
|
@ -1222,9 +1222,10 @@ js_NewRegExp(JSContext *cx, JSTokenStream *ts,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
resize = sizeof *re + state.progLength - 1;
|
resize = sizeof *re + state.progLength - 1;
|
||||||
re = (JSRegExp*) JS_malloc(cx, JS_ROUNDUP(resize, sizeof(jsword)));
|
re = (JSRegExp *) JS_malloc(cx, JS_ROUNDUP(resize, sizeof(jsword)));
|
||||||
if (!re)
|
if (!re)
|
||||||
goto out;
|
goto out;
|
||||||
|
re->nrefs = 1;
|
||||||
re->source = str;
|
re->source = str;
|
||||||
re->lastIndex = 0;
|
re->lastIndex = 0;
|
||||||
re->parenCount = state.parenCount;
|
re->parenCount = state.parenCount;
|
||||||
|
@ -1305,12 +1306,17 @@ static void freeRENtree(JSContext *cx, RENode *ren, RENode *stop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define HOLD_REGEXP(cx, re) JS_ATOMIC_INCREMENT(&(re)->nrefs)
|
||||||
|
#define DROP_REGEXP(cx, re) js_DestroyRegExp(cx, re)
|
||||||
|
|
||||||
void
|
void
|
||||||
js_DestroyRegExp(JSContext *cx, JSRegExp *re)
|
js_DestroyRegExp(JSContext *cx, JSRegExp *re)
|
||||||
{
|
{
|
||||||
js_UnlockGCThing(cx, re->source);
|
if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) {
|
||||||
freeRENtree(cx, re->ren, NULL);
|
js_UnlockGCThing(cx, re->source);
|
||||||
JS_free(cx, re);
|
freeRENtree(cx, re->ren, NULL);
|
||||||
|
JS_free(cx, re);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct MatchState {
|
typedef struct MatchState {
|
||||||
|
@ -2283,7 +2289,7 @@ regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
slot = JSVAL_TO_INT(id);
|
slot = JSVAL_TO_INT(id);
|
||||||
JS_LOCK_OBJ(cx, obj);
|
JS_LOCK_OBJ(cx, obj);
|
||||||
re = (JSRegExp*) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);
|
re = (JSRegExp *) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);
|
||||||
if (re) {
|
if (re) {
|
||||||
switch (slot) {
|
switch (slot) {
|
||||||
case REGEXP_SOURCE:
|
case REGEXP_SOURCE:
|
||||||
|
@ -2296,9 +2302,8 @@ regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||||
*vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0);
|
*vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0);
|
||||||
break;
|
break;
|
||||||
case REGEXP_LAST_INDEX:
|
case REGEXP_LAST_INDEX:
|
||||||
if (!js_NewNumberValue(cx, (jsdouble)re->lastIndex, vp))
|
*vp = INT_TO_JSVAL((jsint) re->lastIndex);
|
||||||
return JS_FALSE;
|
break;
|
||||||
break;
|
|
||||||
case REGEXP_MULTILINE:
|
case REGEXP_MULTILINE:
|
||||||
*vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0);
|
*vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0);
|
||||||
break;
|
break;
|
||||||
|
@ -2318,14 +2323,16 @@ regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||||
if (!JSVAL_IS_INT(id))
|
if (!JSVAL_IS_INT(id))
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
slot = JSVAL_TO_INT(id);
|
slot = JSVAL_TO_INT(id);
|
||||||
JS_LOCK_OBJ(cx, obj);
|
if (slot == REGEXP_LAST_INDEX) {
|
||||||
re = (JSRegExp*) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);
|
if (!js_ValueToNumber(cx, *vp, &d))
|
||||||
if (re && slot == REGEXP_LAST_INDEX) {
|
return JS_FALSE;
|
||||||
if (!js_ValueToNumber(cx, *vp, &d))
|
d = js_DoubleToInteger(d);
|
||||||
return JS_FALSE;
|
JS_LOCK_OBJ(cx, obj);
|
||||||
re->lastIndex = (uintN)d;
|
re = (JSRegExp *) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);
|
||||||
|
if (re)
|
||||||
|
re->lastIndex = (uintN)d;
|
||||||
|
JS_UNLOCK_OBJ(cx, obj);
|
||||||
}
|
}
|
||||||
JS_UNLOCK_OBJ(cx, obj);
|
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2487,7 +2494,7 @@ regexp_finalize(JSContext *cx, JSObject *obj)
|
||||||
{
|
{
|
||||||
JSRegExp *re;
|
JSRegExp *re;
|
||||||
|
|
||||||
re = (JSRegExp*) JS_GetPrivate(cx, obj);
|
re = (JSRegExp *) JS_GetPrivate(cx, obj);
|
||||||
if (!re)
|
if (!re)
|
||||||
return;
|
return;
|
||||||
js_DestroyRegExp(cx, re);
|
js_DestroyRegExp(cx, re);
|
||||||
|
@ -2516,7 +2523,7 @@ regexp_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||||
uint8 flags;
|
uint8 flags;
|
||||||
|
|
||||||
if (xdr->mode == JSXDR_ENCODE) {
|
if (xdr->mode == JSXDR_ENCODE) {
|
||||||
re = (JSRegExp*) JS_GetPrivate(xdr->cx, *objp);
|
re = (JSRegExp *) JS_GetPrivate(xdr->cx, *objp);
|
||||||
if (!re)
|
if (!re)
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
source = re->source;
|
source = re->source;
|
||||||
|
@ -2560,7 +2567,6 @@ static JSBool
|
||||||
regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||||
jsval *rval)
|
jsval *rval)
|
||||||
{
|
{
|
||||||
JSBool ok;
|
|
||||||
JSRegExp *re;
|
JSRegExp *re;
|
||||||
jschar *chars;
|
jschar *chars;
|
||||||
size_t length, nflags;
|
size_t length, nflags;
|
||||||
|
@ -2569,12 +2575,12 @@ regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||||
|
|
||||||
if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
|
if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
ok = JS_TRUE;
|
|
||||||
JS_LOCK_OBJ(cx, obj);
|
JS_LOCK_OBJ(cx, obj);
|
||||||
re = (JSRegExp*) JS_GetPrivate(cx, obj);
|
re = (JSRegExp *) JS_GetPrivate(cx, obj);
|
||||||
if (!re) {
|
if (!re) {
|
||||||
|
JS_UNLOCK_OBJ(cx, obj);
|
||||||
*rval = STRING_TO_JSVAL(cx->runtime->emptyString);
|
*rval = STRING_TO_JSVAL(cx->runtime->emptyString);
|
||||||
goto out;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
length = re->source->length + 2;
|
length = re->source->length + 2;
|
||||||
|
@ -2583,8 +2589,8 @@ regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||||
nflags++;
|
nflags++;
|
||||||
chars = (jschar*) JS_malloc(cx, (length + nflags + 1) * sizeof(jschar));
|
chars = (jschar*) JS_malloc(cx, (length + nflags + 1) * sizeof(jschar));
|
||||||
if (!chars) {
|
if (!chars) {
|
||||||
ok = JS_FALSE;
|
JS_UNLOCK_OBJ(cx, obj);
|
||||||
goto out;
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
chars[0] = '/';
|
chars[0] = '/';
|
||||||
|
@ -2598,18 +2604,16 @@ regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||||
if (re->flags & JSREG_MULTILINE)
|
if (re->flags & JSREG_MULTILINE)
|
||||||
chars[length++] = 'm';
|
chars[length++] = 'm';
|
||||||
}
|
}
|
||||||
|
JS_UNLOCK_OBJ(cx, obj);
|
||||||
chars[length] = 0;
|
chars[length] = 0;
|
||||||
|
|
||||||
str = js_NewString(cx, chars, length, 0);
|
str = js_NewString(cx, chars, length, 0);
|
||||||
if (!str) {
|
if (!str) {
|
||||||
JS_free(cx, chars);
|
JS_free(cx, chars);
|
||||||
ok = JS_FALSE;
|
return JS_FALSE;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
*rval = STRING_TO_JSVAL(str);
|
*rval = STRING_TO_JSVAL(str);
|
||||||
out:
|
return JS_TRUE;
|
||||||
JS_UNLOCK_OBJ(cx, obj);
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSBool
|
static JSBool
|
||||||
|
@ -2624,17 +2628,13 @@ regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||||
if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
|
if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
opt = NULL;
|
opt = NULL;
|
||||||
JS_LOCK_OBJ(cx, obj);
|
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
str = js_NewStringCopyN(cx, cx->runtime->emptyString->chars,
|
str = js_NewStringCopyN(cx, cx->runtime->emptyString->chars,
|
||||||
cx->runtime->emptyString->length, 0);
|
cx->runtime->emptyString->length, 0);
|
||||||
if (!str) {
|
if (!str)
|
||||||
ok = JS_FALSE;
|
return JS_FALSE;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (JSVAL_IS_OBJECT(argv[0])) {
|
if (JSVAL_IS_OBJECT(argv[0])) {
|
||||||
obj2 = JSVAL_TO_OBJECT(argv[0]);
|
|
||||||
/*
|
/*
|
||||||
* If we get passed in a RegExp object we construct a new
|
* If we get passed in a RegExp object we construct a new
|
||||||
* RegExp that is a duplicate of it by re-compiling the
|
* RegExp that is a duplicate of it by re-compiling the
|
||||||
|
@ -2642,58 +2642,54 @@ regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||||
* here if the flags are specified. (We must use the flags
|
* here if the flags are specified. (We must use the flags
|
||||||
* from the original RegExp also).
|
* from the original RegExp also).
|
||||||
*/
|
*/
|
||||||
|
obj2 = JSVAL_TO_OBJECT(argv[0]);
|
||||||
if (obj2 && OBJ_GET_CLASS(cx, obj2) == &js_RegExpClass) {
|
if (obj2 && OBJ_GET_CLASS(cx, obj2) == &js_RegExpClass) {
|
||||||
if (argc >= 2 && !JSVAL_IS_VOID(argv[1])) { /* 'flags' defined */
|
if (argc >= 2 && !JSVAL_IS_VOID(argv[1])) { /* 'flags' passed */
|
||||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||||
JSMSG_NEWREGEXP_FLAGGED);
|
JSMSG_NEWREGEXP_FLAGGED);
|
||||||
ok = JS_FALSE;
|
return JS_FALSE;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
re = (JSRegExp*) JS_GetPrivate(cx, obj2);
|
JS_LOCK_OBJ(cx, obj2);
|
||||||
|
re = (JSRegExp *) JS_GetPrivate(cx, obj2);
|
||||||
if (!re) {
|
if (!re) {
|
||||||
ok = JS_FALSE;
|
JS_UNLOCK_OBJ(cx, obj2);
|
||||||
goto out;
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE);
|
re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE);
|
||||||
|
JS_UNLOCK_OBJ(cx, obj2);
|
||||||
goto madeit;
|
goto madeit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
str = js_ValueToString(cx, argv[0]);
|
str = js_ValueToString(cx, argv[0]);
|
||||||
if (!str) {
|
if (!str)
|
||||||
ok = JS_FALSE;
|
return JS_FALSE;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
argv[0] = STRING_TO_JSVAL(str);
|
argv[0] = STRING_TO_JSVAL(str);
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
if (JSVAL_IS_VOID(argv[1]))
|
if (JSVAL_IS_VOID(argv[1])) {
|
||||||
opt = NULL;
|
opt = NULL;
|
||||||
else {
|
} else {
|
||||||
opt = js_ValueToString(cx, argv[1]);
|
opt = js_ValueToString(cx, argv[1]);
|
||||||
if (!opt) {
|
if (!opt)
|
||||||
ok = JS_FALSE;
|
return JS_FALSE;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
argv[1] = STRING_TO_JSVAL(opt);
|
argv[1] = STRING_TO_JSVAL(opt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE);
|
re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE);
|
||||||
madeit:
|
madeit:
|
||||||
if (!re) {
|
if (!re)
|
||||||
ok = JS_FALSE;
|
return JS_FALSE;
|
||||||
goto out;
|
JS_LOCK_OBJ(cx, obj);
|
||||||
}
|
oldre = (JSRegExp *) JS_GetPrivate(cx, obj);
|
||||||
oldre = (JSRegExp*) JS_GetPrivate(cx, obj);
|
|
||||||
ok = JS_SetPrivate(cx, obj, re);
|
ok = JS_SetPrivate(cx, obj, re);
|
||||||
|
JS_UNLOCK_OBJ(cx, obj);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
js_DestroyRegExp(cx, re);
|
js_DestroyRegExp(cx, re);
|
||||||
goto out;
|
} else {
|
||||||
|
if (oldre)
|
||||||
|
js_DestroyRegExp(cx, oldre);
|
||||||
|
*rval = OBJECT_TO_JSVAL(obj);
|
||||||
}
|
}
|
||||||
if (oldre)
|
|
||||||
js_DestroyRegExp(cx, oldre);
|
|
||||||
*rval = OBJECT_TO_JSVAL(obj);
|
|
||||||
out:
|
|
||||||
JS_UNLOCK_OBJ(cx, obj);
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2701,17 +2697,27 @@ static JSBool
|
||||||
regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||||
JSBool test, jsval *rval)
|
JSBool test, jsval *rval)
|
||||||
{
|
{
|
||||||
JSBool ok, locked;
|
JSBool ok;
|
||||||
JSRegExp *re;
|
JSRegExp *re;
|
||||||
JSString *str;
|
JSString *str;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
ok = JS_FALSE;
|
||||||
if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
|
if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
|
||||||
return JS_FALSE;
|
return ok;
|
||||||
re = (JSRegExp*) JS_GetPrivate(cx, obj);
|
JS_LOCK_OBJ(cx, obj);
|
||||||
if (!re)
|
re = (JSRegExp *) JS_GetPrivate(cx, obj);
|
||||||
|
if (!re) {
|
||||||
|
JS_UNLOCK_OBJ(cx, obj);
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
ok = locked = JS_FALSE;
|
}
|
||||||
|
|
||||||
|
/* NB: we must reach out: after this paragraph, in order to drop re. */
|
||||||
|
HOLD_REGEXP(cx, re);
|
||||||
|
i = (re->flags & JSREG_GLOB) ? re->lastIndex : 0;
|
||||||
|
JS_UNLOCK_OBJ(cx, obj);
|
||||||
|
|
||||||
|
/* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
str = cx->regExpStatics.input;
|
str = cx->regExpStatics.input;
|
||||||
if (!str) {
|
if (!str) {
|
||||||
|
@ -2721,7 +2727,7 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||||
(re->flags & JSREG_GLOB) ? "g" : "",
|
(re->flags & JSREG_GLOB) ? "g" : "",
|
||||||
(re->flags & JSREG_FOLD) ? "i" : "",
|
(re->flags & JSREG_FOLD) ? "i" : "",
|
||||||
(re->flags & JSREG_MULTILINE) ? "m" : "");
|
(re->flags & JSREG_MULTILINE) ? "m" : "");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
str = js_ValueToString(cx, argv[0]);
|
str = js_ValueToString(cx, argv[0]);
|
||||||
|
@ -2729,19 +2735,15 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||||
goto out;
|
goto out;
|
||||||
argv[0] = STRING_TO_JSVAL(str);
|
argv[0] = STRING_TO_JSVAL(str);
|
||||||
}
|
}
|
||||||
if (re->flags & JSREG_GLOB) {
|
|
||||||
JS_LOCK_OBJ(cx, obj);
|
|
||||||
locked = JS_TRUE;
|
|
||||||
i = re->lastIndex;
|
|
||||||
} else {
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
ok = js_ExecuteRegExp(cx, re, str, &i, test, rval);
|
ok = js_ExecuteRegExp(cx, re, str, &i, test, rval);
|
||||||
|
JS_LOCK_OBJ(cx, obj);
|
||||||
if (re->flags & JSREG_GLOB)
|
if (re->flags & JSREG_GLOB)
|
||||||
re->lastIndex = (*rval == JSVAL_NULL) ? 0 : i;
|
re->lastIndex = (*rval == JSVAL_NULL) ? 0 : i;
|
||||||
|
JS_UNLOCK_OBJ(cx, obj);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (locked)
|
DROP_REGEXP(cx, re);
|
||||||
JS_UNLOCK_OBJ(cx, obj);
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@ struct JSRegExpStatics {
|
||||||
: &js_EmptySubString)
|
: &js_EmptySubString)
|
||||||
|
|
||||||
struct JSRegExp {
|
struct JSRegExp {
|
||||||
|
jsrefcount nrefs; /* reference count */
|
||||||
JSString *source; /* locked source string, sans // */
|
JSString *source; /* locked source string, sans // */
|
||||||
uintN lastIndex; /* index after last match, for //g iterator */
|
uintN lastIndex; /* index after last match, for //g iterator */
|
||||||
uintN parenCount; /* number of parenthesized submatches */
|
uintN parenCount; /* number of parenthesized submatches */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче