mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Fix tracing of JSOP_IN (465241, r=danderson).
This commit is contained in:
parent
bd10f75bdb
commit
3c5889df4e
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
|
Loading…
Reference in New Issue
Block a user