mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Try harder to trace array access with non-int / non-string index (478525, r=brendan).
This commit is contained in:
parent
d3cc05e80b
commit
088eeb6c9d
@ -351,16 +351,16 @@ static inline bool isInt32(jsval v)
|
||||
return JSDOUBLE_IS_INT(d, i);
|
||||
}
|
||||
|
||||
static inline bool asInt32(jsval v, jsint& rv)
|
||||
static inline jsint asInt32(jsval v)
|
||||
{
|
||||
if (!isNumber(v))
|
||||
return false;
|
||||
if (JSVAL_IS_INT(v)) {
|
||||
rv = JSVAL_TO_INT(v);
|
||||
return true;
|
||||
}
|
||||
jsdouble d = asNumber(v);
|
||||
return JSDOUBLE_IS_INT(d, rv);
|
||||
JS_ASSERT(isNumber(v));
|
||||
if (JSVAL_IS_INT(v))
|
||||
return JSVAL_TO_INT(v);
|
||||
#ifdef DEBUG
|
||||
jsint i;
|
||||
JS_ASSERT(JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i));
|
||||
#endif
|
||||
return jsint(*JSVAL_TO_DOUBLE(v));
|
||||
}
|
||||
|
||||
/* Return JSVAL_DOUBLE for all numbers (int and double) and the tag otherwise. */
|
||||
@ -6040,68 +6040,12 @@ TraceRecorder::guardDenseArrayIndex(JSObject* obj, jsint idx, LIns* obj_ins,
|
||||
return cond;
|
||||
}
|
||||
|
||||
/*
|
||||
* Guard that a computed property access via an element op (JSOP_GETELEM, etc.)
|
||||
* does not find an alias to a global variable, or a property without a slot,
|
||||
* or a slot-ful property with a getter or setter (depending on op_offset in
|
||||
* JSObjectOps). Finally, beware resolve hooks mutating objects. Oh, and watch
|
||||
* out for bears too ;-).
|
||||
*
|
||||
* One win here is that we do not need to generate a guard that obj_ins does
|
||||
* not result in the global object on trace, because we guard on shape and rule
|
||||
* out obj's shape being the global object's shape at recording time. This is
|
||||
* safe because the global shape cannot change on trace.
|
||||
*/
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::guardElemOp(JSObject* obj, LIns* obj_ins, jsid id, size_t op_offset, jsval* vp)
|
||||
bool
|
||||
TraceRecorder::guardNotGlobalObject(JSObject* obj, LIns* obj_ins)
|
||||
{
|
||||
JS_ASSERT(op_offset == offsetof(JSObjectOps, getProperty) ||
|
||||
op_offset == offsetof(JSObjectOps, setProperty));
|
||||
|
||||
LIns* map_ins = lir->insLoad(LIR_ldp, obj_ins, (int)offsetof(JSObject, map));
|
||||
LIns* ops_ins;
|
||||
if (!map_is_native(obj->map, map_ins, ops_ins, op_offset))
|
||||
ABORT_TRACE("non-native map");
|
||||
|
||||
uint32 shape = OBJ_SHAPE(obj);
|
||||
if (JSID_IS_ATOM(id) && shape == treeInfo->globalShape)
|
||||
ABORT_TRACE("elem op probably aliases global");
|
||||
|
||||
JSObject* pobj;
|
||||
JSProperty* prop;
|
||||
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
||||
return false;
|
||||
|
||||
if (vp)
|
||||
*vp = JSVAL_VOID;
|
||||
if (prop) {
|
||||
bool traceable_slot = true;
|
||||
if (pobj == obj) {
|
||||
JSScopeProperty* sprop = (JSScopeProperty*) prop;
|
||||
traceable_slot = ((op_offset == offsetof(JSObjectOps, getProperty))
|
||||
? SPROP_HAS_STUB_GETTER(sprop)
|
||||
: SPROP_HAS_STUB_SETTER(sprop)) &&
|
||||
SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj));
|
||||
if (vp && traceable_slot)
|
||||
*vp = LOCKED_OBJ_GET_SLOT(obj, sprop->slot);
|
||||
}
|
||||
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
if (pobj != obj)
|
||||
ABORT_TRACE("elem op hit prototype property, can't shape-guard");
|
||||
if (!traceable_slot)
|
||||
ABORT_TRACE("elem op hit direct and slotless getter or setter");
|
||||
}
|
||||
|
||||
if (wasDeepAborted())
|
||||
ABORT_TRACE("deep abort from property lookup");
|
||||
|
||||
// If we got this far, we're almost safe -- but we must check for a rogue resolve hook.
|
||||
if (OBJ_SHAPE(obj) != shape)
|
||||
ABORT_TRACE("resolve hook mutated elem op base object");
|
||||
|
||||
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), "shape");
|
||||
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, shape), "guard(shape)"), BRANCH_EXIT);
|
||||
if (obj == globalObj)
|
||||
ABORT_TRACE("reference aliases global object");
|
||||
guard(false, lir->ins2(LIR_eq, obj_ins, globalObj_ins), MISMATCH_EXIT);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -7205,9 +7149,10 @@ TraceRecorder::record_JSOP_GETELEM()
|
||||
LIns* obj_ins = get(&lval);
|
||||
LIns* idx_ins = get(&idx);
|
||||
|
||||
if (JSVAL_IS_STRING(lval) && JSVAL_IS_INT(idx)) {
|
||||
int i = JSVAL_TO_INT(idx);
|
||||
if ((size_t)i >= JSSTRING_LENGTH(JSVAL_TO_STRING(lval)))
|
||||
// Special case for array-like access of strings.
|
||||
if (JSVAL_IS_STRING(lval) && isInt32(idx)) {
|
||||
int i = asInt32(idx);
|
||||
if (size_t(i) >= JSSTRING_LENGTH(JSVAL_TO_STRING(lval)))
|
||||
ABORT_TRACE("Invalid string index in JSOP_GETELEM");
|
||||
idx_ins = makeNumberInt32(idx_ins);
|
||||
LIns* args[] = { idx_ins, obj_ins, cx_ins };
|
||||
@ -7222,43 +7167,35 @@ TraceRecorder::record_JSOP_GETELEM()
|
||||
|
||||
JSObject* obj = JSVAL_TO_OBJECT(lval);
|
||||
jsval id;
|
||||
jsval v;
|
||||
LIns* v_ins;
|
||||
|
||||
/* Property access using a string name. */
|
||||
if (JSVAL_IS_STRING(idx)) {
|
||||
if (!js_ValueToStringId(cx, idx, &id))
|
||||
return false;
|
||||
/* Property access using a string name or something we have to stringify. */
|
||||
if (!JSVAL_IS_INT(idx)) {
|
||||
// If index is not a string, turn it into a string.
|
||||
if (!js_InternNonIntElementId(cx, obj, idx, &id))
|
||||
ABORT_TRACE("failed to intern non-int element id");
|
||||
set(&idx, stringify(idx));
|
||||
|
||||
// Store the interned string to the stack to save the interpreter from redoing this work.
|
||||
idx = ID_TO_VALUE(id);
|
||||
jsuint index;
|
||||
if (js_IdIsIndex(idx, &index) && guardDenseArray(obj, obj_ins, BRANCH_EXIT)) {
|
||||
v = (index >= js_DenseArrayCapacity(obj)) ? JSVAL_HOLE : obj->dslots[index];
|
||||
if (v == JSVAL_HOLE)
|
||||
ABORT_TRACE("can't see through hole in dense array");
|
||||
} else {
|
||||
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, getProperty), &v))
|
||||
|
||||
// The object is not guaranteed to be a dense array at this point, so it might be the
|
||||
// global object, which we have to guard against.
|
||||
if (!guardNotGlobalObject(obj, obj_ins))
|
||||
return false;
|
||||
}
|
||||
|
||||
return call_imacro(getelem_imacros.getprop);
|
||||
}
|
||||
|
||||
/* At this point we expect a whole number or we bail. */
|
||||
if (!JSVAL_IS_INT(idx))
|
||||
ABORT_TRACE("non-string, non-int JSOP_GETELEM index");
|
||||
if (JSVAL_TO_INT(idx) < 0)
|
||||
ABORT_TRACE("negative JSOP_GETELEM index");
|
||||
// Invalid dense array index or not a dense array.
|
||||
if (JSVAL_TO_INT(idx) < 0 || !OBJ_IS_DENSE_ARRAY(cx, obj)) {
|
||||
if (!guardNotGlobalObject(obj, obj_ins))
|
||||
return false;
|
||||
|
||||
/* Accessing an object using integer index but not a dense array. */
|
||||
if (!OBJ_IS_DENSE_ARRAY(cx, obj)) {
|
||||
idx_ins = makeNumberInt32(idx_ins);
|
||||
if (!js_IndexToId(cx, JSVAL_TO_INT(idx), &id))
|
||||
return false;
|
||||
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, getProperty), &v))
|
||||
return false;
|
||||
return call_imacro(getelem_imacros.getelem);
|
||||
}
|
||||
|
||||
// Fast path for dense arrays accessed with a non-negative integer index.
|
||||
jsval* vp;
|
||||
LIns* addr_ins;
|
||||
if (!elem(lval, idx, vp, v_ins, addr_ins))
|
||||
@ -7353,35 +7290,41 @@ TraceRecorder::record_JSOP_SETELEM()
|
||||
LIns* v_ins = get(&v);
|
||||
jsid id;
|
||||
|
||||
LIns* boxed_v_ins = v_ins;
|
||||
box_jsval(v, boxed_v_ins);
|
||||
if (!JSVAL_IS_INT(idx)) {
|
||||
// If index is not a string, turn it into a string.
|
||||
if (!js_InternNonIntElementId(cx, obj, idx, &id))
|
||||
ABORT_TRACE("failed to intern non-int element id");
|
||||
set(&idx, stringify(idx));
|
||||
|
||||
if (JSVAL_IS_STRING(idx)) {
|
||||
if (!js_ValueToStringId(cx, idx, &id))
|
||||
return false;
|
||||
// Store the interned string to the stack to save the interpreter from redoing this work.
|
||||
idx = ID_TO_VALUE(id);
|
||||
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, setProperty), NULL))
|
||||
|
||||
// The object is not guaranteed to be a dense array at this point, so it might be the
|
||||
// global object, which we have to guard against.
|
||||
if (!guardNotGlobalObject(obj, obj_ins))
|
||||
return false;
|
||||
|
||||
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);
|
||||
|
||||
if (!guardDenseArray(obj, obj_ins, BRANCH_EXIT)) {
|
||||
if (!js_IndexToId(cx, JSVAL_TO_INT(idx), &id))
|
||||
if (JSVAL_TO_INT(idx) < 0 || !OBJ_IS_DENSE_ARRAY(cx, obj)) {
|
||||
if (!guardNotGlobalObject(obj, obj_ins))
|
||||
return false;
|
||||
idx = ID_TO_VALUE(id);
|
||||
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, setProperty), NULL))
|
||||
return false;
|
||||
jsbytecode* pc = cx->fp->regs->pc;
|
||||
return call_imacro((*pc == JSOP_INITELEM)
|
||||
|
||||
return call_imacro((*cx->fp->regs->pc == JSOP_INITELEM)
|
||||
? initelem_imacros.initelem
|
||||
: setelem_imacros.setelem);
|
||||
}
|
||||
|
||||
// Fast path for dense arrays accessed with a non-negative integer index. In case the trace
|
||||
// calculated the index using the FPU, force it to be an integer.
|
||||
idx_ins = makeNumberInt32(idx_ins);
|
||||
|
||||
// Box the value so we can use one builtin instead of having to add one builtin for every
|
||||
// storage type.
|
||||
LIns* boxed_v_ins = v_ins;
|
||||
box_jsval(v, boxed_v_ins);
|
||||
|
||||
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);
|
||||
@ -7391,8 +7334,6 @@ TraceRecorder::record_JSOP_SETELEM()
|
||||
set(&lval, v_ins);
|
||||
|
||||
return true;
|
||||
}
|
||||
ABORT_TRACE("non-string, non-int JSOP_SETELEM index");
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
@ -7712,9 +7653,8 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
|
||||
* Can't specialize to assert obj != global, must guard to avoid aliasing
|
||||
* stale homes of stacked global variables.
|
||||
*/
|
||||
if (obj == globalObj)
|
||||
ABORT_TRACE("prop op aliases global");
|
||||
guard(false, lir->ins2(LIR_eq, obj_ins, globalObj_ins), MISMATCH_EXIT);
|
||||
if (!guardNotGlobalObject(obj, obj_ins))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Property cache ensures that we are dealing with an existing property,
|
||||
|
@ -536,8 +536,7 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
JS_REQUIRES_STACK bool guardDenseArrayIndex(JSObject* obj, jsint idx, nanojit::LIns* obj_ins,
|
||||
nanojit::LIns* dslots_ins, nanojit::LIns* idx_ins,
|
||||
ExitType exitType);
|
||||
JS_REQUIRES_STACK bool guardElemOp(JSObject* obj, nanojit::LIns* obj_ins, jsid id,
|
||||
size_t op_offset, jsval* vp);
|
||||
bool guardNotGlobalObject(JSObject* obj, nanojit::LIns* obj_ins);
|
||||
void clearFrameSlotsFromCache();
|
||||
JS_REQUIRES_STACK bool guardCallee(jsval& callee);
|
||||
JS_REQUIRES_STACK bool getClassPrototype(JSObject* ctor, nanojit::LIns*& proto_ins);
|
||||
|
Loading…
Reference in New Issue
Block a user