From 89cb250b42693b32c795baccdde23d919eac5e65 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 9 Mar 2009 18:38:54 -0700 Subject: [PATCH] Backed out changeset 65be699dabf0. --- js/src/jstracer.cpp | 214 ++++++++++++++++++++++++++++---------------- js/src/jstracer.h | 3 +- 2 files changed, 139 insertions(+), 78 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index c963152821a..d8b8fbd33a2 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -351,16 +351,16 @@ static inline bool isInt32(jsval v) return JSDOUBLE_IS_INT(d, i); } -static inline jsint asInt32(jsval v) +static inline bool asInt32(jsval v, jsint& 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)); + 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); } /* Return JSVAL_DOUBLE for all numbers (int and double) and the tag otherwise. */ @@ -6040,12 +6040,68 @@ TraceRecorder::guardDenseArrayIndex(JSObject* obj, jsint idx, LIns* obj_ins, return cond; } -bool -TraceRecorder::guardNotGlobalObject(JSObject* obj, LIns* obj_ins) +/* + * 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) { - if (obj == globalObj) - ABORT_TRACE("reference aliases global object"); - guard(false, lir->ins2(LIR_eq, obj_ins, globalObj_ins), MISMATCH_EXIT); + 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); return true; } @@ -7149,10 +7205,9 @@ TraceRecorder::record_JSOP_GETELEM() LIns* obj_ins = get(&lval); LIns* idx_ins = get(&idx); - // 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))) + 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))) ABORT_TRACE("Invalid string index in JSOP_GETELEM"); idx_ins = makeNumberInt32(idx_ins); LIns* args[] = { idx_ins, obj_ins, cx_ins }; @@ -7167,35 +7222,43 @@ TraceRecorder::record_JSOP_GETELEM() JSObject* obj = JSVAL_TO_OBJECT(lval); jsval id; + jsval v; LIns* v_ins; - /* 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)); - + /* Property access using a string name. */ + 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); - - // 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; - + 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)) + return false; + } return call_imacro(getelem_imacros.getprop); } - // 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; + /* 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"); + /* 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)) @@ -7290,50 +7353,46 @@ TraceRecorder::record_JSOP_SETELEM() LIns* v_ins = get(&v); jsid id; - 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); - - // 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_TO_INT(idx) < 0 || !OBJ_IS_DENSE_ARRAY(cx, obj)) { - if (!guardNotGlobalObject(obj, obj_ins)) - return false; - - 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); + 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)) + 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); - jsbytecode* pc = cx->fp->regs->pc; - if (*pc == JSOP_SETELEM && pc[JSOP_SETELEM_LENGTH] != JSOP_POP) - set(&lval, v_ins); + 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; + jsbytecode* pc = cx->fp->regs->pc; + return call_imacro((*pc == JSOP_INITELEM) + ? initelem_imacros.initelem + : setelem_imacros.setelem); + } - return true; + 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); + + 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 @@ -7653,8 +7712,9 @@ 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 (!guardNotGlobalObject(obj, obj_ins)) - return false; + if (obj == globalObj) + ABORT_TRACE("prop op aliases global"); + guard(false, lir->ins2(LIR_eq, obj_ins, globalObj_ins), MISMATCH_EXIT); /* * Property cache ensures that we are dealing with an existing property, diff --git a/js/src/jstracer.h b/js/src/jstracer.h index a26bd9de729..d1a19893470 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -536,7 +536,8 @@ 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); - bool guardNotGlobalObject(JSObject* obj, nanojit::LIns* obj_ins); + JS_REQUIRES_STACK bool guardElemOp(JSObject* obj, nanojit::LIns* obj_ins, jsid id, + size_t op_offset, jsval* vp); void clearFrameSlotsFromCache(); JS_REQUIRES_STACK bool guardCallee(jsval& callee); JS_REQUIRES_STACK bool getClassPrototype(JSObject* ctor, nanojit::LIns*& proto_ins);