Fix tracing of JSOP_IN (465241, r=danderson).

This commit is contained in:
Andreas Gal 2008-11-16 22:13:13 -08:00
parent bd10f75bdb
commit 3c5889df4e
4 changed files with 75 additions and 66 deletions

View File

@ -88,6 +88,7 @@ BUILTIN2(extern, SIDEEXIT, js_CallTree, INTERPSTATE, FRAGMENT,
BUILTIN2(extern, OBJECT, js_FastNewObject, CONTEXT, OBJECT, 0, 0)
BUILTIN3(extern, BOOL, js_AddProperty, CONTEXT, OBJECT, SCOPEPROP, 0, 0)
BUILTIN3(extern, BOOL, js_HasNamedProperty, CONTEXT, OBJECT, STRING, 0, 0)
BUILTIN3(extern, BOOL, js_HasNamedPropertyInt32, CONTEXT, OBJECT, INT32, 0, 0)
BUILTIN3(extern, JSVAL, js_CallGetter, CONTEXT, OBJECT, SCOPEPROP, 0, 0)
BUILTIN2(extern, STRING, js_TypeOfObject, CONTEXT, OBJECT, 1, 1)
BUILTIN2(extern, STRING, js_TypeOfBoolean, CONTEXT, INT32, 1, 1)

View File

@ -187,6 +187,20 @@ js_StringToInt32(JSContext* cx, JSString* str)
return js_DoubleToECMAInt32(d);
}
static inline JSBool
js_Int32ToId(JSContext* cx, int32 index, jsid* id)
{
if (unsigned(index) <= JSVAL_INT_MAX) {
*id = INT_TO_JSID(index);
return JS_TRUE;
} else {
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)
{
@ -405,6 +419,21 @@ js_HasNamedProperty(JSContext* cx, JSObject* obj, JSString* idstr)
return prop != NULL;
}
JSBool FASTCALL
js_HasNamedPropertyInt32(JSContext* cx, JSObject* obj, int32 index)
{
jsid id;
if (!js_Int32ToId(cx, index, &id))
return JSVAL_TO_BOOLEAN(JSVAL_VOID);
JSObject* obj2;
JSProperty* prop;
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
return JSVAL_TO_BOOLEAN(JSVAL_VOID);
if (prop)
OBJ_DROP_PROPERTY(cx, obj2, prop);
return prop != NULL;
}
jsval FASTCALL
js_CallGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
{

View File

@ -7182,81 +7182,39 @@ bool
TraceRecorder::record_JSOP_IN()
{
jsval& rval = stackval(-1);
jsval& lval = stackval(-2);
if (JSVAL_IS_PRIMITIVE(rval))
ABORT_TRACE("JSOP_IN on non-object right operand");
jsval& lval = stackval(-2);
if (!JSVAL_IS_PRIMITIVE(lval))
ABORT_TRACE("JSOP_IN on E4X QName left operand");
jsid id;
if (JSVAL_IS_INT(lval)) {
id = INT_JSVAL_TO_JSID(lval);
} else {
if (!JSVAL_IS_STRING(lval))
ABORT_TRACE("non-string left operand to JSOP_IN");
if (!js_ValueToStringId(cx, lval, &id))
return false;
}
// Expect what we see at trace recording time (hit or miss) to be the same
// when executing the trace. Use a builtin helper for named properties, as
// the for-in tracing code does. First, handle indexes in dense arrays as a
// special case.
JSObject* obj = JSVAL_TO_OBJECT(rval);
LIns* obj_ins = get(&rval);
bool cond;
jsid id;
LIns* x;
do {
if (guardDenseArray(obj, obj_ins, BRANCH_EXIT)) {
if (JSVAL_IS_INT(lval)) {
jsint idx = JSVAL_TO_INT(lval);
LIns* idx_ins = f2i(get(&lval));
LIns* dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots));
if (!guardDenseArrayIndex(obj, idx, obj_ins, dslots_ins, idx_ins, MISMATCH_EXIT))
ABORT_TRACE("dense array index out of bounds");
// We can't "see through" a hole to a possible Array.prototype
// property, so we must abort/guard.
if (obj->dslots[idx] == JSVAL_HOLE)
ABORT_TRACE("can't see through hole in dense array");
LIns* addr_ins = lir->ins2(LIR_piadd, dslots_ins,
lir->ins2i(LIR_pilsh, idx_ins,
(sizeof(jsval) == 4) ? 2 : 3));
guard(false,
lir->ins2(LIR_eq, lir->insLoad(LIR_ldp, addr_ins, 0), INS_CONST(JSVAL_HOLE)),
MISMATCH_EXIT);
cond = true;
x = INS_CONST(cond);
break;
}
// Not an index id, but a dense array -- go up to the proto. */
obj = STOBJ_GET_PROTO(obj);
obj_ins = stobj_get_fslot(obj_ins, JSSLOT_PROTO);
} else {
if (JSVAL_IS_INT(id))
ABORT_TRACE("INT in OBJ where OBJ is not a dense array");
}
JSObject* obj2;
JSProperty* prop;
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
ABORT_TRACE("OBJ_LOOKUP_PROPERTY failed in JSOP_IN");
cond = prop != NULL;
if (prop)
OBJ_DROP_PROPERTY(cx, obj2, prop);
if (JSVAL_IS_INT(lval)) {
id = INT_JSVAL_TO_JSID(lval);
LIns* args[] = { makeNumberInt32(get(&lval)), obj_ins, cx_ins };
x = lir->insCall(&js_HasNamedPropertyInt32_ci, args);
} else if (JSVAL_IS_STRING(lval)) {
if (!js_ValueToStringId(cx, lval, &id))
ABORT_TRACE("left operand of JSOP_IN didn't convert to a string-id");
LIns* args[] = { get(&lval), obj_ins, cx_ins };
x = lir->insCall(&js_HasNamedProperty_ci, args);
guard(false, lir->ins2i(LIR_eq, x, JSVAL_TO_BOOLEAN(JSVAL_VOID)), OOM_EXIT);
x = lir->ins2i(LIR_eq, x, 1);
} while (0);
} else {
ABORT_TRACE("string or integer expected");
}
guard(false, lir->ins2i(LIR_eq, x, JSVAL_TO_BOOLEAN(JSVAL_VOID)), OOM_EXIT);
x = lir->ins2i(LIR_eq, x, 1);
JSObject* obj2;
JSProperty* prop;
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
ABORT_TRACE("OBJ_LOOKUP_PROPERTY failed in JSOP_IN");
bool cond = prop != NULL;
if (prop)
OBJ_DROP_PROPERTY(cx, obj2, prop);
/* The interpreter fuses comparisons and the following branch,
so we have to do that here as well. */
fuseIf(cx->fp->regs->pc + 1, cond, x);

View File

@ -2438,6 +2438,27 @@ function testStringToInt32() {
testStringToInt32.expected = "33333";
test(testStringToInt32);
function testIn() {
var array = [3];
var obj = { "-1": 5, "1.7": 3, "foo": 5, "1": 7 };
var a = [];
for (let j = 0; j < 5; ++j) {
a.push("0" in array);
a.push(-1 in obj);
a.push(1.7 in obj);
a.push("foo" in obj);
a.push(1 in obj);
a.push("1" in array);
a.push(-2 in obj);
a.push(2.7 in obj);
a.push("bar" in obj);
a.push(2 in obj);
}
return a.join(",");
}
testIn.expected = "true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false";
test(testIn);
/* NOTE: Keep this test last, since it screws up all for...in loops after it. */
function testGlobalProtoAccess() {
return "ok";