Bug 475761 - TM: js_Any_GetProp and friends can reenter. r=brendan. Note that this patch alone does not fix the bug. The rest of the fix comes in bug 462027.

This commit is contained in:
Jason Orendorff 2009-01-29 00:13:03 -06:00
Родитель bef69f676f
Коммит 196e202090
9 изменённых файлов: 340 добавлений и 97 удалений

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

@ -77,10 +77,6 @@ BUILTIN1(extern, UINT32, js_DoubleToUint32, DOUBLE,
BUILTIN2(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, 1, 1)
BUILTIN2(extern, INT32, js_StringToInt32, CONTEXT, STRING, 1, 1)
BUILTIN3(extern, JSVAL, js_Any_getprop, CONTEXT, OBJECT, STRING, 0, 0)
BUILTIN4(extern, BOOL, js_Any_setprop, CONTEXT, OBJECT, STRING, JSVAL, 0, 0)
BUILTIN3(extern, JSVAL, js_Any_getelem, CONTEXT, OBJECT, INT32, 0, 0)
BUILTIN4(extern, BOOL, js_Any_setelem, CONTEXT, OBJECT, INT32, JSVAL, 0, 0)
BUILTIN2(FRIEND, BOOL, js_CloseIterator, CONTEXT, JSVAL, 0, 0)
BUILTIN2(extern, SIDEEXIT, js_CallTree, INTERPSTATE, FRAGMENT, 0, 0)
BUILTIN2(extern, OBJECT, js_FastNewObject, CONTEXT, OBJECT, 0, 0)

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

@ -594,6 +594,64 @@ static struct {
/*11*/ JSOP_STOP,
},
};
static struct {
jsbytecode getprop[10];
jsbytecode getelem[10];
} getelem_imacros = {
{
/* 0*/ JSOP_SWAP,
/* 1*/ JSOP_CALLBUILTIN, ((JSBUILTIN_GetProperty) & 0xff00) >> 8, ((JSBUILTIN_GetProperty) & 0xff),
/* 4*/ JSOP_PICK, 2,
/* 6*/ JSOP_CALL, 0, 1,
/* 9*/ JSOP_STOP,
},
{
/* 0*/ JSOP_SWAP,
/* 1*/ JSOP_CALLBUILTIN, ((JSBUILTIN_GetElement) & 0xff00) >> 8, ((JSBUILTIN_GetElement) & 0xff),
/* 4*/ JSOP_PICK, 2,
/* 6*/ JSOP_CALL, 0, 1,
/* 9*/ JSOP_STOP,
},
};
static struct {
jsbytecode setprop[15];
jsbytecode setelem[15];
} setelem_imacros = {
{
/* 0*/ JSOP_DUP,
/* 1*/ JSOP_PICK, 3,
/* 3*/ JSOP_CALLBUILTIN, ((JSBUILTIN_SetProperty) & 0xff00) >> 8, ((JSBUILTIN_SetProperty) & 0xff),
/* 6*/ JSOP_PICK, 4,
/* 8*/ JSOP_PICK, 4,
/*10*/ JSOP_CALL, 0, 2,
/*13*/ JSOP_POP,
/*14*/ JSOP_STOP,
},
{
/* 0*/ JSOP_DUP,
/* 1*/ JSOP_PICK, 3,
/* 3*/ JSOP_CALLBUILTIN, ((JSBUILTIN_SetElement) & 0xff00) >> 8, ((JSBUILTIN_SetElement) & 0xff),
/* 6*/ JSOP_PICK, 4,
/* 8*/ JSOP_PICK, 4,
/*10*/ JSOP_CALL, 0, 2,
/*13*/ JSOP_POP,
/*14*/ JSOP_STOP,
},
};
static struct {
jsbytecode initelem[15];
} initelem_imacros = {
{
/* 0*/ JSOP_PICK, 2,
/* 2*/ JSOP_DUP,
/* 3*/ JSOP_CALLBUILTIN, ((JSBUILTIN_SetElement) & 0xff00) >> 8, ((JSBUILTIN_SetElement) & 0xff),
/* 6*/ JSOP_PICK, 4,
/* 8*/ JSOP_PICK, 4,
/*10*/ JSOP_CALL, 0, 2,
/*13*/ JSOP_POP,
/*14*/ JSOP_STOP,
},
};
uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_NOP */
0, /* JSOP_PUSH */
@ -650,8 +708,8 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_ELEMDEC */
0, /* JSOP_GETPROP */
0, /* JSOP_SETPROP */
0, /* JSOP_GETELEM */
0, /* JSOP_SETELEM */
2, /* JSOP_GETELEM */
2, /* JSOP_SETELEM */
0, /* JSOP_CALLNAME */
0, /* JSOP_CALL */
0, /* JSOP_NAME */
@ -687,7 +745,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_NEWINIT */
0, /* JSOP_ENDINIT */
0, /* JSOP_INITPROP */
0, /* JSOP_INITELEM */
2, /* JSOP_INITELEM */
0, /* JSOP_DEFSHARP */
0, /* JSOP_USESHARP */
0, /* JSOP_INCARG */

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

@ -1,3 +1,4 @@
# -*- indent-tabs-mode: nil; -*-
# vim: set sw=4 ts=8 et tw=78 ft=asm:
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -635,3 +636,64 @@
.end
.end
.igroup getelem JSOP_GETELEM
.imacro getprop # obj name
swap # name obj
callbuiltin (JSBUILTIN_GetProperty) # name fun obj
pick 2 # fun obj name
call 1 # propval
stop
.end
.imacro getelem # obj i
swap # i obj
callbuiltin (JSBUILTIN_GetElement) # i fun obj
pick 2 # fun obj i
call 1 # propval
stop
.end
.end
.igroup setelem JSOP_SETELEM
.imacro setprop # obj name val
dup # obj name val val
pick 3 # name val val obj
callbuiltin (JSBUILTIN_SetProperty) # name val val fun obj
pick 4 # val val fun obj name
pick 4 # val fun obj name val
call 2 # val junk
pop # val
stop
.end
.imacro setelem # obj i val
dup # obj i val val
pick 3 # i val val obj
callbuiltin (JSBUILTIN_SetElement) # i val val fun obj
pick 4 # val val fun obj i
pick 4 # val fun obj i val
call 2 # val junk
pop # val
stop
.end
.end
.igroup initelem JSOP_INITELEM
.imacro initelem # obj i val
pick 2 # i val obj
dup # i val obj obj
callbuiltin (JSBUILTIN_SetElement) # i val obj fun obj
pick 4 # val obj fun obj i
pick 4 # obj fun obj i val
call 2 # obj junk
pop # obj
stop
.end
.end

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

@ -187,62 +187,6 @@ js_StringToInt32(JSContext* cx, JSString* str)
return js_DoubleToECMAInt32(d);
}
static inline JSBool
js_Int32ToId(JSContext* cx, int32 index, jsid* id)
{
if (index <= JSVAL_INT_MAX) {
*id = INT_TO_JSID(index);
return JS_TRUE;
}
JSString* str = js_NumberToString(cx, index);
if (!str)
return JS_FALSE;
return js_ValueToStringId(cx, STRING_TO_JSVAL(str), id);
}
jsval FASTCALL
js_Any_getprop(JSContext* cx, JSObject* obj, JSString* idstr)
{
jsval v;
jsid id;
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id))
return JSVAL_ERROR_COOKIE;
if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
return JSVAL_ERROR_COOKIE;
return v;
}
JSBool FASTCALL
js_Any_setprop(JSContext* cx, JSObject* obj, JSString* idstr, jsval v)
{
jsid id;
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id))
return JS_FALSE;
return OBJ_SET_PROPERTY(cx, obj, id, &v);
}
jsval FASTCALL
js_Any_getelem(JSContext* cx, JSObject* obj, int32 index)
{
jsval v;
jsid id;
if (!js_Int32ToId(cx, index, &id))
return JSVAL_ERROR_COOKIE;
if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
return JSVAL_ERROR_COOKIE;
return v;
}
JSBool FASTCALL
js_Any_setelem(JSContext* cx, JSObject* obj, int32 index, jsval v)
{
jsid id;
if (!js_Int32ToId(cx, index, &id))
return JSVAL_ERROR_COOKIE;
return OBJ_SET_PROPERTY(cx, obj, id, &v);
}
SideExit* FASTCALL
js_CallTree(InterpState* state, Fragment* f)
{

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

@ -322,6 +322,19 @@ js_StringToNumber(JSContext* cx, JSString* str);
jsdouble FASTCALL
js_BooleanOrUndefinedToNumber(JSContext* cx, int32 unboxed);
static JS_INLINE JSBool
js_Int32ToId(JSContext* cx, int32 index, jsid* id)
{
if (index <= JSVAL_INT_MAX) {
*id = INT_TO_JSID(index);
return JS_TRUE;
}
JSString* str = js_NumberToString(cx, index);
if (!str)
return JS_FALSE;
return js_ValueToStringId(cx, STRING_TO_JSVAL(str), id);
}
#else
#define JS_DEFINE_CALLINFO_1(linkage, rt, op, at0, cse, fold)

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

@ -257,6 +257,10 @@ typedef enum JSRuntimeState {
typedef enum JSBuiltinFunctionId {
JSBUILTIN_ObjectToIterator,
JSBUILTIN_CallIteratorNext,
JSBUILTIN_GetProperty,
JSBUILTIN_GetElement,
JSBUILTIN_SetProperty,
JSBUILTIN_SetElement,
JSBUILTIN_LIMIT
} JSBuiltinFunctionId;

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

@ -489,8 +489,7 @@ OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, -1, 0, 0, JOF_UINT16
/*
* Pick an element from the stack.
*/
OPDEF(JSOP_PICK, 201,"pick", NULL, 2, 1, 0, 0, JOF_UINT8)
OPDEF(JSOP_PICK, 201,"pick", NULL, 2, 0, 0, 0, JOF_UINT8)
/* Throws a TypeError if the value at the top of the stack is not primitive. */
OPDEF(JSOP_PRIMTOP, 202, "primtop", NULL, 1, 1, 1, 0, JOF_BYTE)

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

@ -4228,6 +4228,7 @@ TraceRecorder::monitorRecording(JSContext* cx, TraceRecorder* tr, JSOp op)
!cx->fp->imacpc, stdout);) \
flag = tr->record_##x(); \
if (x == JSOP_ITER || x == JSOP_NEXTITER || x == JSOP_APPLY || \
x == JSOP_GETELEM || x == JSOP_SETELEM || x== JSOP_INITELEM || \
JSOP_IS_BINARY(x) || JSOP_IS_UNARY(x) || \
JSOP_IS_EQUALITY(x)) { \
goto imacro; \
@ -6809,6 +6810,69 @@ TraceRecorder::record_SetPropMiss(JSPropCacheEntry* entry)
return record_SetPropHit(entry, sprop);
}
/* Functions used by JSOP_GETELEM. */
static JSBool
GetProperty(JSContext *cx, uintN argc, jsval *vp)
{
jsval *argv;
jsid id;
JS_ASSERT(argc == 1);
argv = JS_ARGV(cx, vp);
JS_ASSERT(JSVAL_IS_STRING(argv[0]));
if (!js_ValueToStringId(cx, argv[0], &id))
return JS_FALSE;
argv[0] = ID_TO_VALUE(id);
return OBJ_GET_PROPERTY(cx, JS_THIS_OBJECT(cx, vp), id, &JS_RVAL(cx, vp));
}
static jsval FASTCALL
GetProperty_tn(JSContext *cx, JSObject *obj, JSString *name)
{
jsid id;
jsval v;
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(name), &id) ||
!OBJ_GET_PROPERTY(cx, obj, id, &v)) {
return JSVAL_ERROR_COOKIE;
}
return v;
}
static JSBool
GetElement(JSContext *cx, uintN argc, jsval *vp)
{
jsval *argv;
jsid id;
JS_ASSERT(argc == 1);
argv = JS_ARGV(cx, vp);
JS_ASSERT(JSVAL_IS_NUMBER(argv[0]));
if (!JS_ValueToId(cx, argv[0], &id))
return JS_FALSE;
argv[0] = ID_TO_VALUE(id);
return OBJ_GET_PROPERTY(cx, JS_THIS_OBJECT(cx, vp), id, &JS_RVAL(cx, vp));
}
static jsval FASTCALL
GetElement_tn(JSContext* cx, JSObject* obj, int32 index)
{
jsval v;
jsid id;
if (!js_Int32ToId(cx, index, &id))
return JSVAL_ERROR_COOKIE;
if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
return JSVAL_ERROR_COOKIE;
return v;
}
JS_DEFINE_TRCINFO_1(GetProperty,
(3, (static, JSVAL_FAIL, GetProperty_tn, CONTEXT, THIS, STRING, 0, 0)))
JS_DEFINE_TRCINFO_1(GetElement,
(3, (extern, JSVAL_FAIL, GetElement_tn, CONTEXT, THIS, INT32, 0, 0)))
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_GETELEM()
{
@ -6853,12 +6917,7 @@ TraceRecorder::record_JSOP_GETELEM()
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, getProperty), &v))
return false;
}
LIns* args[] = { idx_ins, obj_ins, cx_ins };
v_ins = lir->insCall(&js_Any_getprop_ci, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), MISMATCH_EXIT);
unbox_jsval(v, v_ins);
set(&lval, v_ins);
return true;
return call_imacro(getelem_imacros.getprop);
}
/* At this point we expect a whole number or we bail. */
@ -6870,17 +6929,11 @@ TraceRecorder::record_JSOP_GETELEM()
/* Accessing an object using integer index but not a dense array. */
if (!OBJ_IS_DENSE_ARRAY(cx, obj)) {
idx_ins = makeNumberInt32(idx_ins);
LIns* args[] = { idx_ins, obj_ins, cx_ins };
if (!js_IndexToId(cx, JSVAL_TO_INT(idx), &id))
return false;
idx = ID_TO_VALUE(id);
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, getProperty), &v))
return false;
LIns* v_ins = lir->insCall(&js_Any_getelem_ci, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), MISMATCH_EXIT);
unbox_jsval(v, v_ins);
set(&lval, v_ins);
return true;
return call_imacro(getelem_imacros.getelem);
}
jsval* vp;
@ -6891,6 +6944,71 @@ TraceRecorder::record_JSOP_GETELEM()
return true;
}
/* Functions used by JSOP_SETELEM */
static JSBool
SetProperty(JSContext *cx, uintN argc, jsval *vp)
{
jsval *argv;
jsid id;
JS_ASSERT(argc == 2);
argv = JS_ARGV(cx, vp);
JS_ASSERT(JSVAL_IS_STRING(argv[0]));
if (!js_ValueToStringId(cx, argv[0], &id))
return JS_FALSE;
argv[0] = ID_TO_VALUE(id);
if (!OBJ_SET_PROPERTY(cx, JS_THIS_OBJECT(cx, vp), id, &argv[1]))
return JS_FALSE;
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
static int32 FASTCALL
SetProperty_tn(JSContext* cx, JSObject* obj, JSString* idstr, jsval v)
{
jsid id;
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id) ||
!OBJ_SET_PROPERTY(cx, obj, id, &v)) {
return JSVAL_TO_BOOLEAN(JSVAL_VOID);
}
return JSVAL_TRUE;
}
static JSBool
SetElement(JSContext *cx, uintN argc, jsval *vp)
{
jsval *argv;
jsid id;
JS_ASSERT(argc == 2);
argv = JS_ARGV(cx, vp);
JS_ASSERT(JSVAL_IS_NUMBER(argv[0]));
if (!JS_ValueToId(cx, argv[0], &id))
return JS_FALSE;
argv[0] = ID_TO_VALUE(id);
if (!OBJ_SET_PROPERTY(cx, JS_THIS_OBJECT(cx, vp), id, &argv[1]))
return JS_FALSE;
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
static int32 FASTCALL
SetElement_tn(JSContext* cx, JSObject* obj, int32 index, jsval v)
{
jsid id;
if (!js_Int32ToId(cx, index, &id) || !OBJ_SET_PROPERTY(cx, obj, id, &v))
return JSVAL_TO_BOOLEAN(JSVAL_VOID);
return JSVAL_TRUE;
}
JS_DEFINE_TRCINFO_1(SetProperty,
(4, (extern, BOOL_FAIL, SetProperty_tn, CONTEXT, THIS, STRING, JSVAL, 0, 0)))
JS_DEFINE_TRCINFO_1(SetElement,
(4, (extern, BOOL_FAIL, SetElement_tn, CONTEXT, THIS, INT32, JSVAL, 0, 0)))
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_SETELEM()
{
@ -6918,35 +7036,36 @@ TraceRecorder::record_JSOP_SETELEM()
idx = ID_TO_VALUE(id);
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, setProperty), NULL))
return false;
LIns* args[] = { boxed_v_ins, idx_ins, obj_ins, cx_ins };
LIns* ok_ins = lir->insCall(&js_Any_setprop_ci, args);
guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT);
} else if (JSVAL_IS_INT(idx)) {
return call_imacro(setelem_imacros.setprop);
}
if (JSVAL_IS_INT(idx)) {
if (JSVAL_TO_INT(idx) < 0)
ABORT_TRACE("negative JSOP_SETELEM index");
idx_ins = makeNumberInt32(idx_ins);
LIns* args[] = { boxed_v_ins, idx_ins, obj_ins, cx_ins };
LIns* res_ins;
if (guardDenseArray(obj, obj_ins, BRANCH_EXIT)) {
res_ins = lir->insCall(&js_Array_dense_setelem_ci, args);
} else {
if (!guardDenseArray(obj, obj_ins, BRANCH_EXIT)) {
if (!js_IndexToId(cx, JSVAL_TO_INT(idx), &id))
return false;
idx = ID_TO_VALUE(id);
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, setProperty), NULL))
return false;
res_ins = lir->insCall(&js_Any_setelem_ci, args);
jsbytecode* pc = cx->fp->regs->pc;
return call_imacro((*pc == JSOP_INITELEM)
? initelem_imacros.initelem
: setelem_imacros.setelem);
}
LIns* args[] = { boxed_v_ins, idx_ins, obj_ins, cx_ins };
LIns* res_ins = lir->insCall(&js_Array_dense_setelem_ci, args);
guard(false, lir->ins_eq0(res_ins), MISMATCH_EXIT);
} else {
ABORT_TRACE("non-string, non-int JSOP_SETELEM index");
jsbytecode* pc = cx->fp->regs->pc;
if (*pc == JSOP_SETELEM && pc[JSOP_SETELEM_LENGTH] != JSOP_POP)
set(&lval, v_ins);
return true;
}
jsbytecode* pc = cx->fp->regs->pc;
if (*pc == JSOP_SETELEM && pc[JSOP_SETELEM_LENGTH] != JSOP_POP)
set(&lval, v_ins);
return true;
ABORT_TRACE("non-string, non-int JSOP_SETELEM index");
}
JS_REQUIRES_STACK bool
@ -8811,7 +8930,11 @@ static const struct BuiltinFunctionInfo {
int nargs;
} builtinFunctionInfo[JSBUILTIN_LIMIT] = {
{ObjectToIterator_trcinfo, 1},
{CallIteratorNext_trcinfo, 0}
{CallIteratorNext_trcinfo, 0},
{GetProperty_trcinfo, 1},
{GetElement_trcinfo, 1},
{SetProperty_trcinfo, 2},
{SetElement_trcinfo, 2}
};
JSObject *
@ -9027,6 +9150,9 @@ InitIMacroCode()
imacro_code[JSOP_ITER] = (jsbytecode*)&iter_imacros - 1;
imacro_code[JSOP_NEXTITER] = (jsbytecode*)&nextiter_imacros - 1;
imacro_code[JSOP_GETELEM] = (jsbytecode*)&getelem_imacros - 1;
imacro_code[JSOP_SETELEM] = (jsbytecode*)&setelem_imacros - 1;
imacro_code[JSOP_INITELEM] = (jsbytecode*)&initelem_imacros - 1;
imacro_code[JSOP_APPLY] = (jsbytecode*)&apply_imacros - 1;
imacro_code[JSOP_NEG] = (jsbytecode*)&unary_imacros - 1;

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

@ -4107,6 +4107,47 @@ function testInterpreterReentry3() {
testInterpreterReentry3.expected = 1;
test(testInterpreterReentry3);
function testInterpreterReentry4() {
var obj = {a:1, b:1, c:1, d:1, get e() 1000 };
for (var p in obj)
obj[p];
}
test(testInterpreterReentry4);
function testInterpreterReentry5() {
var arr = [0, 1, 2, 3, 4];
arr.__defineGetter__("4", function() 1000);
for (var i = 0; i < 5; i++)
arr[i];
for (var p in arr)
arr[p];
}
test(testInterpreterReentry5);
/* // These tests should pass but currently crash, pending bug 462027.
function testInterpreterReentry6() {
var obj = {a:1, b:1, c:1, d:1, set e(x) { this._e = x; }};
for (var p in obj)
obj[p] = "grue";
return obj._e;
}
testInterpreterReentry6.expected = "grue";
test(testInterpreterReentry6);
function testInterpreterReentry7() {
var arr = [0, 1, 2, 3, 4];
arr.__defineSetter__("4", function(x) { this._4 = x; });
for (var i = 0; i < 5; i++)
arr[i] = "grue";
var tmp = arr._4;
for (var p in arr)
arr[p] = "bleen";
return tmp + " " + arr._4;
}
testInterpreterReentry7.expected = "grue bleen";
test(testInterpreterReentry7);
*/
/*****************************************************************************
* *
* _____ _ _ _____ ______ _____ _______ *