mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 453730: trace JSOP_ARGUMENTS, r=gal
This commit is contained in:
parent
e46aedbb11
commit
975f74c278
@ -87,5 +87,5 @@ BUILTIN2(extern, STRING, js_TypeOfObject, CONTEXT, OBJECT,
|
||||
BUILTIN2(extern, STRING, js_TypeOfBoolean, CONTEXT, INT32, 1, 1)
|
||||
BUILTIN2(extern, DOUBLE, js_BooleanOrUndefinedToNumber, CONTEXT, INT32, 1, 1)
|
||||
BUILTIN2(extern, STRING, js_BooleanOrUndefinedToString, CONTEXT, INT32, 1, 1)
|
||||
BUILTIN1(extern, OBJECT, js_Arguments, CONTEXT, 0, 0)
|
||||
BUILTIN2(extern, OBJECT, js_Arguments, CONTEXT, OBJECT 0, 0)
|
||||
BUILTIN4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, 0, 0)
|
||||
|
@ -395,11 +395,13 @@ js_BooleanOrUndefinedToString(JSContext *cx, int32 unboxed)
|
||||
JS_DEFINE_CALLINFO_2(extern, STRING, js_BooleanOrUndefinedToString, CONTEXT, INT32, 1, 1)
|
||||
|
||||
JSObject* FASTCALL
|
||||
js_Arguments(JSContext* cx)
|
||||
js_Arguments(JSContext* cx, JSObject* parent, JSObject* cached)
|
||||
{
|
||||
return NULL;
|
||||
if (cached)
|
||||
return cached;
|
||||
return js_NewObject(cx, &js_ArgumentsClass, NULL, NULL, 0);
|
||||
}
|
||||
JS_DEFINE_CALLINFO_1(extern, OBJECT, js_Arguments, CONTEXT, 0, 0)
|
||||
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_Arguments, CONTEXT, OBJECT, OBJECT, 0, 0)
|
||||
|
||||
JSObject* FASTCALL
|
||||
js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* parent)
|
||||
|
@ -454,6 +454,7 @@ JS_DECLARE_CALLINFO(js_ArrayCompPush)
|
||||
|
||||
/* Defined in jsfun.cpp. */
|
||||
JS_DECLARE_CALLINFO(js_AllocFlatClosure)
|
||||
JS_DECLARE_CALLINFO(js_PutArguments)
|
||||
|
||||
/* Defined in jsnum.cpp. */
|
||||
JS_DECLARE_CALLINFO(js_NumberToString)
|
||||
|
@ -124,7 +124,7 @@ MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
|
||||
size_t nbits, nbytes;
|
||||
jsbitmap *bitmap;
|
||||
|
||||
argsobj = fp->argsobj;
|
||||
argsobj = JSVAL_TO_OBJECT(fp->argsobj);
|
||||
(void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
|
||||
nbits = fp->argc;
|
||||
JS_ASSERT(slot < nbits);
|
||||
@ -165,7 +165,7 @@ ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
|
||||
jsval bmapval, bmapint;
|
||||
jsbitmap *bitmap;
|
||||
|
||||
argsobj = fp->argsobj;
|
||||
argsobj = JSVAL_TO_OBJECT(fp->argsobj);
|
||||
(void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
|
||||
if (JSVAL_IS_VOID(bmapval))
|
||||
return JS_FALSE;
|
||||
@ -208,7 +208,7 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp)
|
||||
slot = (uintN) JSID_TO_INT(id);
|
||||
if (slot < fp->argc) {
|
||||
if (fp->argsobj && ArgWasDeleted(cx, fp, slot))
|
||||
return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
|
||||
return OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(fp->argsobj), id, vp);
|
||||
*vp = fp->argv[slot];
|
||||
} else {
|
||||
/*
|
||||
@ -224,12 +224,12 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp)
|
||||
* undefined in *vp.
|
||||
*/
|
||||
if (fp->argsobj)
|
||||
return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
|
||||
return OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(fp->argsobj), id, vp);
|
||||
}
|
||||
} else {
|
||||
if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
|
||||
if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH))
|
||||
return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
|
||||
return OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(fp->argsobj), id, vp);
|
||||
*vp = INT_TO_JSVAL((jsint) fp->argc);
|
||||
}
|
||||
}
|
||||
@ -252,7 +252,7 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
|
||||
fp = fp->down;
|
||||
|
||||
/* Create an arguments object for fp only if it lacks one. */
|
||||
argsobj = fp->argsobj;
|
||||
argsobj = JSVAL_TO_OBJECT(fp->argsobj);
|
||||
if (argsobj)
|
||||
return argsobj;
|
||||
|
||||
@ -278,7 +278,7 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
|
||||
while ((parent = OBJ_GET_PARENT(cx, global)) != NULL)
|
||||
global = parent;
|
||||
STOBJ_SET_PARENT(argsobj, global);
|
||||
fp->argsobj = argsobj;
|
||||
fp->argsobj = OBJECT_TO_JSVAL(argsobj);
|
||||
return argsobj;
|
||||
}
|
||||
|
||||
@ -298,7 +298,7 @@ js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
|
||||
* elements of argsobj. Do this first, before clearing and freeing the
|
||||
* deleted argument slot bitmap, because args_enumerate depends on that.
|
||||
*/
|
||||
argsobj = fp->argsobj;
|
||||
argsobj = JSVAL_TO_OBJECT(fp->argsobj);
|
||||
ok = args_enumerate(cx, argsobj);
|
||||
|
||||
/*
|
||||
@ -723,6 +723,25 @@ args_enumerate(JSContext *cx, JSObject *obj)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool JS_FASTCALL
|
||||
js_PutArguments(JSContext* cx, JSObject* argsobj, uint32 length, JSObject* callee, jsval* args)
|
||||
{
|
||||
if (!js_DefineProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
|
||||
INT_TO_JSVAL(length), args_getProperty, args_setProperty, 0, NULL))
|
||||
return JS_FALSE;
|
||||
if (!js_DefineProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom),
|
||||
OBJECT_TO_JSVAL(callee), args_getProperty, args_setProperty, 0, NULL))
|
||||
return JS_FALSE;
|
||||
|
||||
for (uintN i = 0; i < length; ++i) {
|
||||
if (!js_DefineProperty(cx, argsobj, INT_TO_JSID(i), args[i],
|
||||
args_getProperty, args_setProperty, 0, NULL))
|
||||
return JS_FALSE;
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
JS_DEFINE_CALLINFO_5(extern, BOOL, js_PutArguments, CONTEXT, OBJECT, UINT32, OBJECT, JSVALPTR, 0, 0)
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
/*
|
||||
* If a generator-iterator's arguments or call object escapes, it needs to
|
||||
@ -932,7 +951,7 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
if (fp->argsobj) {
|
||||
if (!TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
|
||||
STOBJ_SET_SLOT(callobj, JSSLOT_CALL_ARGUMENTS,
|
||||
OBJECT_TO_JSVAL(fp->argsobj));
|
||||
fp->argsobj);
|
||||
}
|
||||
ok &= js_PutArgsObject(cx, fp);
|
||||
}
|
||||
|
@ -2882,7 +2882,7 @@ js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp)
|
||||
if (fp->callobj)
|
||||
JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call");
|
||||
if (fp->argsobj)
|
||||
JS_CALL_OBJECT_TRACER(trc, fp->argsobj, "arguments");
|
||||
JS_CALL_OBJECT_TRACER(trc, JSVAL_TO_OBJECT(fp->argsobj), "arguments");
|
||||
if (fp->varobj)
|
||||
JS_CALL_OBJECT_TRACER(trc, fp->varobj, "variables");
|
||||
if (fp->script) {
|
||||
|
@ -1298,7 +1298,8 @@ have_fun:
|
||||
*/
|
||||
frame.thisp = (JSObject *)vp[1];
|
||||
frame.varobj = NULL;
|
||||
frame.callobj = frame.argsobj = NULL;
|
||||
frame.callobj = NULL;
|
||||
frame.argsobj = NULL;
|
||||
frame.script = script;
|
||||
frame.callee = funobj;
|
||||
frame.fun = fun;
|
||||
@ -1577,7 +1578,8 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
||||
frame.sharpArray = down->sharpArray;
|
||||
JS_ASSERT(script->nfixed == 0);
|
||||
} else {
|
||||
frame.callobj = frame.argsobj = NULL;
|
||||
frame.callobj = NULL;
|
||||
frame.argsobj = NULL;
|
||||
obj = chain;
|
||||
if (cx->options & JSOPTION_VAROBJFIX) {
|
||||
while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
|
||||
|
@ -71,7 +71,7 @@ struct JSStackFrame {
|
||||
jsbytecode *imacpc; /* null or interpreter macro call pc */
|
||||
jsval *slots; /* variables, locals and operand stack */
|
||||
JSObject *callobj; /* lazily created Call object */
|
||||
JSObject *argsobj; /* lazily created arguments object */
|
||||
jsval argsobj; /* lazily created arguments object, must be JSVAL_OBJECT */
|
||||
JSObject *varobj; /* variables object, where vars go */
|
||||
JSObject *callee; /* function or script object */
|
||||
JSScript *script; /* script being interpreted */
|
||||
|
@ -730,7 +730,7 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
|
||||
}
|
||||
gen->frame.argsobj = fp->argsobj;
|
||||
if (fp->argsobj) {
|
||||
JS_SetPrivate(cx, fp->argsobj, &gen->frame);
|
||||
JS_SetPrivate(cx, JSVAL_TO_OBJECT(fp->argsobj), &gen->frame);
|
||||
fp->argsobj = NULL;
|
||||
}
|
||||
|
||||
|
@ -6239,7 +6239,7 @@ js_DumpStackFrame(JSStackFrame *fp)
|
||||
}
|
||||
fprintf(stderr, " argv: %p (argc: %u)\n", (void *) fp->argv, (unsigned) fp->argc);
|
||||
MaybeDumpObject("callobj", fp->callobj);
|
||||
MaybeDumpObject("argsobj", fp->argsobj);
|
||||
MaybeDumpObject("argsobj", JSVAL_TO_OBJECT(fp->argsobj));
|
||||
MaybeDumpObject("varobj", fp->varobj);
|
||||
MaybeDumpObject("this", fp->thisp);
|
||||
fprintf(stderr, " rval: ");
|
||||
|
@ -246,6 +246,7 @@ js_InitJITStatsClass(JSContext *cx, JSObject *glob)
|
||||
#define INS_CONSTPTR(p) addName(lir->insImmPtr(p), #p)
|
||||
#define INS_CONSTFUNPTR(p) addName(lir->insImmPtr(JS_FUNC_TO_DATA_PTR(void*, p)), #p)
|
||||
#define INS_CONSTWORD(v) addName(lir->insImmPtr((void *) v), #v)
|
||||
#define INS_VOID() INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID))
|
||||
|
||||
using namespace avmplus;
|
||||
using namespace nanojit;
|
||||
@ -1252,6 +1253,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Visit the values in the given JSStackFrame that the tracer cares about. This visitor
|
||||
* function is (implicitly) the primary definition of the native stack area layout. There
|
||||
* are a few other independent pieces of code that must be maintained to assume the same
|
||||
* layout. They are marked like this:
|
||||
*
|
||||
* Duplicate native stack layout computation: see VisitFrameSlots header comment.
|
||||
*/
|
||||
template <typename Visitor>
|
||||
JS_REQUIRES_STACK static bool
|
||||
VisitFrameSlots(Visitor &visitor, unsigned depth, JSStackFrame *fp,
|
||||
@ -1266,6 +1275,9 @@ VisitFrameSlots(Visitor &visitor, unsigned depth, JSStackFrame *fp,
|
||||
if (!visitor.visitStackSlots(&fp->argv[-2], argSlots(fp) + 2, fp))
|
||||
return false;
|
||||
}
|
||||
visitor.setStackSlotKind("arguments");
|
||||
if (!visitor.visitStackSlots(&fp->argsobj, 1, fp))
|
||||
return false;
|
||||
visitor.setStackSlotKind("var");
|
||||
if (!visitor.visitStackSlots(fp->slots, fp->script->nfixed, fp))
|
||||
return false;
|
||||
@ -1414,10 +1426,11 @@ js_NativeStackSlots(JSContext *cx, unsigned callDepth)
|
||||
unsigned slots = 0;
|
||||
unsigned depth = callDepth;
|
||||
for (;;) {
|
||||
/* Duplicate native stack layout computation: see VisitFrameSlots header comment. */
|
||||
unsigned operands = fp->regs->sp - StackBase(fp);
|
||||
slots += operands;
|
||||
if (fp->callee)
|
||||
slots += fp->script->nfixed;
|
||||
slots += fp->script->nfixed + 1 /*argsobj*/;
|
||||
if (depth-- == 0) {
|
||||
if (fp->callee)
|
||||
slots += 2/*callee,this*/ + argSlots(fp);
|
||||
@ -1744,7 +1757,13 @@ TraceRecorder::isGlobal(jsval* p) const
|
||||
(size_t(p - globalObj->dslots) < (STOBJ_NSLOTS(globalObj) - JS_INITIAL_NSLOTS)));
|
||||
}
|
||||
|
||||
/* Determine the offset in the native stack for a jsval we track */
|
||||
/*
|
||||
* Return the offset in the native stack for the given jsval. More formally,
|
||||
* |p| must be the address of a jsval that is represented in the native stack
|
||||
* area. The return value is the offset, from InterpState::stackBase, in bytes,
|
||||
* where the native representation of |*p| is stored. To get the offset relative
|
||||
* to InterpState::sp, subtract TreeInfo::nativeStackBase.
|
||||
*/
|
||||
JS_REQUIRES_STACK ptrdiff_t
|
||||
TraceRecorder::nativeStackOffset(jsval* p) const
|
||||
{
|
||||
@ -2264,6 +2283,9 @@ FlushNativeStackFrame(JSContext* cx, unsigned callDepth, JSTraceType* mp, double
|
||||
for (; n != 0; fp = fp->down) {
|
||||
--n;
|
||||
if (fp->callee) {
|
||||
if (fp->argsobj)
|
||||
JS_SetPrivate(cx, JSVAL_TO_OBJECT(fp->argsobj), fp);
|
||||
|
||||
/*
|
||||
* We might return from trace with a different callee object, but it still
|
||||
* has to be the same JSFunction (FIXME: bug 471425, eliminate fp->callee).
|
||||
@ -4330,12 +4352,13 @@ js_SynthesizeFrame(JSContext* cx, const FrameInfo& fi)
|
||||
newifp->hookData = NULL;
|
||||
}
|
||||
|
||||
/* Duplicate native stack layout computation: see VisitFrameSlots header comment. */
|
||||
// FIXME? we must count stack slots from caller's operand stack up to (but not including)
|
||||
// callee's, including missing arguments. Could we shift everything down to the caller's
|
||||
// fp->slots (where vars start) and avoid some of the complexity?
|
||||
return (fi.spdist - fp->down->script->nfixed) +
|
||||
((fun->nargs > fp->argc) ? fun->nargs - fp->argc : 0) +
|
||||
script->nfixed;
|
||||
script->nfixed + 1/*argsobj*/;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -6369,16 +6392,26 @@ TraceRecorder::scopeChain() const
|
||||
offsetof(JSStackFrame, scopeChain));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
FrameInRange(JSStackFrame* fp, JSStackFrame *target, unsigned callDepth)
|
||||
/*
|
||||
* Return the frame of a call object if that frame is part of the current trace. |depthp| is an
|
||||
* optional outparam: if it is non-null, it will be filled in with the depth of the call object's
|
||||
* frame relevant to cx->fp.
|
||||
*/
|
||||
JS_REQUIRES_STACK JSStackFrame*
|
||||
TraceRecorder::frameIfInRange(JSObject* obj, unsigned* depthp) const
|
||||
{
|
||||
while (fp != target) {
|
||||
if (callDepth-- == 0)
|
||||
return false;
|
||||
JSStackFrame* ofp = (JSStackFrame*) JS_GetPrivate(cx, obj);
|
||||
JSStackFrame* fp = cx->fp;
|
||||
for (unsigned depth = 0; depth <= callDepth; ++depth) {
|
||||
if (fp == ofp) {
|
||||
if (depthp)
|
||||
*depthp = depth;
|
||||
return ofp;
|
||||
}
|
||||
if (!(fp = fp->down))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK JSRecordingStatus
|
||||
@ -6427,8 +6460,8 @@ TraceRecorder::activeCallOrGlobalSlot(JSObject* obj, jsval*& vp)
|
||||
ABORT_TRACE("deep abort from property lookup");
|
||||
|
||||
if (obj == obj2 && OBJ_GET_CLASS(cx, obj) == &js_CallClass) {
|
||||
JSStackFrame* cfp = (JSStackFrame*) JS_GetPrivate(cx, obj);
|
||||
if (cfp && FrameInRange(cx->fp, cfp, callDepth)) {
|
||||
JSStackFrame* cfp = frameIfInRange(obj);
|
||||
if (cfp) {
|
||||
JSScopeProperty* sprop = (JSScopeProperty*) prop;
|
||||
|
||||
uint32 setflags = (js_CodeSpec[*cx->fp->regs->pc].format & (JOF_SET | JOF_INCDEC | JOF_FOR));
|
||||
@ -7887,11 +7920,15 @@ TraceRecorder::clearFrameSlotsFromCache()
|
||||
JSStackFrame* fp = cx->fp;
|
||||
jsval* vp;
|
||||
jsval* vpstop;
|
||||
// Duplicate native stack layout computation: see VisitFrameSlots header comment.
|
||||
// This doesn't do layout arithmetic, but it must clear out all the slots defined as
|
||||
// imported by VisitFrameSlots.
|
||||
if (fp->callee) {
|
||||
vp = &fp->argv[-2];
|
||||
vpstop = &fp->argv[argSlots(fp)];
|
||||
while (vp < vpstop)
|
||||
nativeFrameTracker.set(vp++, (LIns*)0);
|
||||
nativeFrameTracker.set(&fp->argsobj, (LIns*)0);
|
||||
}
|
||||
vp = &fp->slots[0];
|
||||
vpstop = &fp->slots[fp->script->nslots];
|
||||
@ -7920,8 +7957,11 @@ TraceRecorder::record_EnterFrame()
|
||||
debug_only_print0(LC_TMTracer, "----\n");
|
||||
}
|
||||
)
|
||||
LIns* void_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
|
||||
LIns* void_ins = INS_VOID();
|
||||
|
||||
// Duplicate native stack layout computation: see VisitFrameSlots header comment.
|
||||
// This doesn't do layout arithmetic, but it must initialize in the tracker all the
|
||||
// slots defined as imported by VisitFrameSlots.
|
||||
jsval* vp = &fp->argv[fp->argc];
|
||||
jsval* vpstop = vp + ptrdiff_t(fp->fun->nargs) - ptrdiff_t(fp->argc);
|
||||
while (vp < vpstop) {
|
||||
@ -7934,6 +7974,7 @@ TraceRecorder::record_EnterFrame()
|
||||
vpstop = vp + fp->script->nfixed;
|
||||
while (vp < vpstop)
|
||||
set(vp++, void_ins, true);
|
||||
set(&fp->argsobj, INS_CONSTPTR(0), true);
|
||||
return JSRS_CONTINUE;
|
||||
}
|
||||
|
||||
@ -8001,6 +8042,23 @@ TraceRecorder::record_JSOP_RETURN()
|
||||
return JSRS_STOP;
|
||||
}
|
||||
|
||||
// If we have created an |arguments| object for the frame, we must copy the argument
|
||||
// values into the object as properties in case it is used after this frame returns.
|
||||
if (cx->fp->argsobj) {
|
||||
LIns* argsobj_ins = get(&cx->fp->argsobj);
|
||||
LIns* length_ins = INS_CONST(cx->fp->argc);
|
||||
LIns* callee_ins = get(&cx->fp->argv[-2]);
|
||||
LIns* args_ins = lir->insAlloc(sizeof(jsval) * cx->fp->argc);
|
||||
for (uintN i = 0; i < cx->fp->argc; ++i) {
|
||||
LIns* arg_ins = get(&cx->fp->argv[i]);
|
||||
box_jsval(cx->fp->argv[i], arg_ins);
|
||||
lir->insStorei(arg_ins, args_ins, i * sizeof(jsval));
|
||||
}
|
||||
LIns* args[] = { args_ins, callee_ins, length_ins, argsobj_ins, cx_ins };
|
||||
LIns* call_ins = lir->insCall(&js_PutArguments_ci, args);
|
||||
guard(false, lir->ins_eq0(call_ins), STATUS_EXIT);
|
||||
}
|
||||
|
||||
/* If we inlined this function call, make the return value available to the caller code. */
|
||||
jsval& rval = stackval(-1);
|
||||
JSStackFrame *fp = cx->fp;
|
||||
@ -8051,15 +8109,15 @@ TraceRecorder::record_JSOP_IFNE()
|
||||
JS_REQUIRES_STACK JSRecordingStatus
|
||||
TraceRecorder::record_JSOP_ARGUMENTS()
|
||||
{
|
||||
#if 1
|
||||
ABORT_TRACE("can't trace arguments yet");
|
||||
#else
|
||||
LIns* args[] = { cx_ins };
|
||||
LIns* a_ins = lir->insCall(&js_Arguments_ci, args);
|
||||
LIns* a_ins = get(&cx->fp->argsobj);
|
||||
JSObject* global = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
|
||||
LIns* global_ins = INS_CONSTPTR(global);
|
||||
LIns* args[] = { a_ins, global_ins, cx_ins };
|
||||
a_ins = lir->insCall(&js_Arguments_ci, args);
|
||||
guard(false, lir->ins_eq0(a_ins), OOM_EXIT);
|
||||
stack(0, a_ins);
|
||||
set(&cx->fp->argsobj, a_ins);
|
||||
return JSRS_CONTINUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK JSRecordingStatus
|
||||
@ -9231,6 +9289,80 @@ TraceRecorder::record_JSOP_GETELEM()
|
||||
return call_imacro(call ? callelem_imacros.callprop : getelem_imacros.getprop);
|
||||
}
|
||||
|
||||
if (STOBJ_GET_CLASS(obj) == &js_ArgumentsClass) {
|
||||
guardClass(obj, obj_ins, &js_ArgumentsClass, snapshot(MISMATCH_EXIT));
|
||||
|
||||
unsigned depth;
|
||||
JSStackFrame* afp = frameIfInRange(obj, &depth);
|
||||
if (afp) {
|
||||
uintN int_idx = JSVAL_TO_INT(idx);
|
||||
jsval* vp = &afp->argv[int_idx];
|
||||
if (idx_ins->isconstq()) {
|
||||
if (int_idx >= 0 && int_idx < afp->argc)
|
||||
v_ins = get(vp);
|
||||
else
|
||||
v_ins = INS_VOID();
|
||||
} else {
|
||||
// If the index is not a constant expression, we generate LIR to load the value from
|
||||
// the native stack area. The guard on js_ArgumentClass above ensures the up-to-date
|
||||
// value has been written back to the native stack area.
|
||||
|
||||
idx_ins = makeNumberInt32(idx_ins);
|
||||
if (int_idx >= 0 && int_idx < afp->argc) {
|
||||
JSTraceType type = getCoercedType(*vp);
|
||||
|
||||
// Guard that the argument has the same type on trace as during recording.
|
||||
LIns* typemap_ins;
|
||||
if (callDepth == depth) {
|
||||
// In this case, we are in the same frame where the arguments object was created.
|
||||
// The entry type map is not necessarily up-to-date, so we capture a new type map
|
||||
// for this point in the code.
|
||||
unsigned stackSlots = js_NativeStackSlots(cx, 0/*callDepth*/);
|
||||
if (stackSlots * sizeof(JSTraceType) > NJ_MAX_SKIP_PAYLOAD_SZB)
|
||||
ABORT_TRACE("|arguments| requires saving too much stack");
|
||||
JSTraceType* typemap = (JSTraceType*) lir->insSkip(stackSlots * sizeof(JSTraceType))->payload();
|
||||
DetermineTypesVisitor detVisitor(*this, typemap);
|
||||
VisitStackSlots(detVisitor, cx, 0);
|
||||
typemap_ins = INS_CONSTPTR(typemap + 2 /*callee,this*/);
|
||||
} else {
|
||||
// In this case, we are in a deeper frame from where the arguments object was
|
||||
// created. The type map at the point of the call out from the creation frame
|
||||
// is accurate.
|
||||
// Note: this relies on the assumption that we abort on setting an element of
|
||||
// an arguments object in any deeper frame.
|
||||
LIns* fip_ins = lir->insLoad(LIR_ldp, lirbuf->rp, (callDepth-depth)*sizeof(FrameInfo*));
|
||||
typemap_ins = lir->ins2(LIR_add, fip_ins, INS_CONST(sizeof(FrameInfo) + 2/*callee,this*/ * sizeof(JSTraceType)));
|
||||
}
|
||||
|
||||
LIns* typep_ins = lir->ins2(LIR_add, typemap_ins,
|
||||
lir->ins2(LIR_mul, idx_ins, INS_CONST(sizeof(JSTraceType))));
|
||||
LIns* type_ins = lir->insLoad(LIR_ldcb, typep_ins, 0);
|
||||
guard(true,
|
||||
addName(lir->ins2(LIR_eq, type_ins, lir->insImm(type)),
|
||||
"guard(type-stable upvar)"),
|
||||
BRANCH_EXIT);
|
||||
|
||||
// Read the value out of the native stack area.
|
||||
guard(true, lir->ins2(LIR_ult, idx_ins, INS_CONST(afp->argc)),
|
||||
snapshot(BRANCH_EXIT));
|
||||
size_t stackOffset = -treeInfo->nativeStackBase + nativeStackOffset(&afp->argv[0]);
|
||||
LIns* args_addr_ins = lir->ins2(LIR_add, lirbuf->sp, INS_CONST(stackOffset));
|
||||
LIns* argi_addr_ins = lir->ins2(LIR_add, args_addr_ins,
|
||||
lir->ins2(LIR_mul, idx_ins, INS_CONST(sizeof(double))));
|
||||
v_ins = stackLoad(argi_addr_ins, type);
|
||||
} else {
|
||||
guard(false, lir->ins2(LIR_ult, idx_ins, INS_CONST(afp->argc)),
|
||||
snapshot(BRANCH_EXIT));
|
||||
v_ins = INS_VOID();
|
||||
}
|
||||
}
|
||||
JS_ASSERT(v_ins);
|
||||
set(&lval, v_ins);
|
||||
return JSRS_CONTINUE;
|
||||
}
|
||||
ABORT_TRACE("can't reach arguments object's frame");
|
||||
}
|
||||
|
||||
if (!guardDenseArray(obj, obj_ins, BRANCH_EXIT)) {
|
||||
CHECK_STATUS(guardNotGlobalObject(obj, obj_ins));
|
||||
|
||||
@ -9481,7 +9613,15 @@ TraceRecorder::upvar(JSScript* script, JSUpvarArray* uva, uintN index, jsval& v)
|
||||
addName(lir->ins2(LIR_eq, call_ins, lir->insImm(type)),
|
||||
"guard(type-stable upvar)"),
|
||||
BRANCH_EXIT);
|
||||
return stackLoad(outp, type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate LIR to load a value from the native stack. This method ensures that the
|
||||
* correct LIR load operator is used.
|
||||
*/
|
||||
LIns* TraceRecorder::stackLoad(LIns* base, uint8 type)
|
||||
{
|
||||
LOpcode loadOp;
|
||||
switch (type) {
|
||||
case TT_DOUBLE:
|
||||
@ -9503,7 +9643,7 @@ TraceRecorder::upvar(JSScript* script, JSUpvarArray* uva, uintN index, jsval& v)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LIns* result = lir->insLoad(loadOp, outp, 0);
|
||||
LIns* result = lir->insLoad(loadOp, base, 0);
|
||||
if (type == TT_INT32)
|
||||
result = lir->ins1(LIR_i2f, result);
|
||||
return result;
|
||||
@ -9567,9 +9707,7 @@ TraceRecorder::guardCallee(jsval& callee)
|
||||
|
||||
guard(true,
|
||||
lir->ins2(LIR_eq,
|
||||
lir->ins2(LIR_piand,
|
||||
stobj_get_fslot(callee_ins, JSSLOT_PRIVATE),
|
||||
INS_CONSTWORD(~JSVAL_INT)),
|
||||
stobj_get_private(callee_ins),
|
||||
INS_CONSTPTR(OBJ_GET_PRIVATE(cx, callee_obj))),
|
||||
branchExit);
|
||||
guard(true,
|
||||
@ -9713,30 +9851,31 @@ TraceRecorder::record_JSOP_APPLY()
|
||||
aobj = JSVAL_TO_OBJECT(vp[3]);
|
||||
aobj_ins = get(&vp[3]);
|
||||
|
||||
/*
|
||||
* We expect a dense array for the arguments (the other
|
||||
* frequent case is the arguments object, but that we
|
||||
* don't trace at the moment).
|
||||
/*
|
||||
* We trace dense arrays and arguments objects. The code we generate for apply
|
||||
* uses imacros to handle a specific number of arguments.
|
||||
*/
|
||||
if (!guardDenseArray(aobj, aobj_ins))
|
||||
ABORT_TRACE("arguments parameter of apply is not a dense array");
|
||||
if (OBJ_IS_DENSE_ARRAY(cx, aobj)) {
|
||||
guardDenseArray(aobj, aobj_ins);
|
||||
length = jsuint(aobj->fslots[JSSLOT_ARRAY_LENGTH]);
|
||||
guard(true,
|
||||
lir->ins2i(LIR_eq,
|
||||
stobj_get_fslot(aobj_ins, JSSLOT_ARRAY_LENGTH),
|
||||
length),
|
||||
BRANCH_EXIT);
|
||||
} else if (OBJ_GET_CLASS(cx, aobj) == &js_ArgumentsClass) {
|
||||
guardClass(aobj, aobj_ins, &js_ArgumentsClass, snapshot(MISMATCH_EXIT));
|
||||
JSStackFrame* afp = frameIfInRange(aobj);
|
||||
if (!afp)
|
||||
ABORT_TRACE("arguments object not in range");
|
||||
length = afp->argc;
|
||||
} else {
|
||||
ABORT_TRACE("arguments parameter of apply is not a dense array or argments object");
|
||||
}
|
||||
|
||||
/*
|
||||
* We trace only apply calls with a certain number of arguments.
|
||||
*/
|
||||
length = jsuint(aobj->fslots[JSSLOT_ARRAY_LENGTH]);
|
||||
if (length >= JS_ARRAY_LENGTH(apply_imacro_table))
|
||||
ABORT_TRACE("too many arguments to apply");
|
||||
|
||||
/*
|
||||
* Make sure the array has the same length at runtime.
|
||||
*/
|
||||
guard(true,
|
||||
lir->ins2i(LIR_eq,
|
||||
stobj_get_fslot(aobj_ins, JSSLOT_ARRAY_LENGTH),
|
||||
length),
|
||||
BRANCH_EXIT);
|
||||
|
||||
return call_imacro(apply_imacro_table[length]);
|
||||
}
|
||||
|
||||
@ -10010,8 +10149,7 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
|
||||
sprop->id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
|
||||
if (!guardClass(obj, obj_ins, &js_StringClass, snapshot(MISMATCH_EXIT)))
|
||||
ABORT_TRACE("can't trace String.length on non-String objects");
|
||||
LIns* str_ins = stobj_get_fslot(obj_ins, JSSLOT_PRIVATE);
|
||||
str_ins = lir->ins2(LIR_piand, str_ins, INS_CONSTWORD(~JSVAL_TAGMASK));
|
||||
LIns* str_ins = stobj_get_private(obj_ins, JSVAL_TAGMASK);
|
||||
v_ins = lir->ins1(LIR_i2f, getStringLength(str_ins));
|
||||
return JSRS_CONTINUE;
|
||||
}
|
||||
@ -10924,10 +11062,11 @@ TraceRecorder::record_JSOP_ARGSUB()
|
||||
JSStackFrame* fp = cx->fp;
|
||||
if (!(fp->fun->flags & JSFUN_HEAVYWEIGHT)) {
|
||||
uintN slot = GET_ARGNO(fp->regs->pc);
|
||||
if (slot < fp->fun->nargs && slot < fp->argc && !fp->argsobj) {
|
||||
if (slot < fp->argc)
|
||||
stack(0, get(&cx->fp->argv[slot]));
|
||||
return JSRS_CONTINUE;
|
||||
}
|
||||
else
|
||||
stack(0, INS_VOID());
|
||||
return JSRS_CONTINUE;
|
||||
}
|
||||
ABORT_TRACE("can't trace JSOP_ARGSUB hard case");
|
||||
}
|
||||
@ -11770,6 +11909,17 @@ TraceRecorder::record_JSOP_LENGTH()
|
||||
|
||||
JSObject* obj = JSVAL_TO_OBJECT(l);
|
||||
LIns* obj_ins = get(&l);
|
||||
|
||||
if (STOBJ_GET_CLASS(obj) == &js_ArgumentsClass) {
|
||||
guardClass(obj, obj_ins, &js_ArgumentsClass, snapshot(MISMATCH_EXIT));
|
||||
JSStackFrame* afp = frameIfInRange(obj);
|
||||
if (afp) {
|
||||
LIns* v_ins = lir->ins1(LIR_i2f, INS_CONST(afp->argc));
|
||||
set(&l, v_ins);
|
||||
return JSRS_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
LIns* v_ins;
|
||||
if (OBJ_IS_ARRAY(cx, obj)) {
|
||||
if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
|
||||
|
@ -616,6 +616,7 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
JS_REQUIRES_STACK jsval& stackval(int n) const;
|
||||
|
||||
JS_REQUIRES_STACK nanojit::LIns* scopeChain() const;
|
||||
JS_REQUIRES_STACK JSStackFrame* frameIfInRange(JSObject* obj, unsigned* depthp = NULL) const;
|
||||
JS_REQUIRES_STACK JSRecordingStatus activeCallOrGlobalSlot(JSObject* obj, jsval*& vp);
|
||||
|
||||
JS_REQUIRES_STACK nanojit::LIns* arg(unsigned n);
|
||||
@ -623,6 +624,7 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
JS_REQUIRES_STACK nanojit::LIns* var(unsigned n);
|
||||
JS_REQUIRES_STACK void var(unsigned n, nanojit::LIns* i);
|
||||
JS_REQUIRES_STACK nanojit::LIns* upvar(JSScript* script, JSUpvarArray* uva, uintN index, jsval& v);
|
||||
nanojit::LIns* stackLoad(nanojit::LIns* addr, uint8 type);
|
||||
JS_REQUIRES_STACK nanojit::LIns* stack(int n);
|
||||
JS_REQUIRES_STACK void stack(int n, nanojit::LIns* i);
|
||||
|
||||
@ -679,6 +681,11 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
nanojit::LIns*& dslots_ins);
|
||||
nanojit::LIns* stobj_get_slot(nanojit::LIns* obj_ins, unsigned slot,
|
||||
nanojit::LIns*& dslots_ins);
|
||||
nanojit::LIns* stobj_get_private(nanojit::LIns* obj_ins, jsval mask=JSVAL_INT) {
|
||||
return lir->ins2(nanojit::LIR_piand,
|
||||
stobj_get_fslot(obj_ins, JSSLOT_PRIVATE),
|
||||
lir->insImmPtr((void*) ~mask));
|
||||
}
|
||||
JSRecordingStatus native_set(nanojit::LIns* obj_ins, JSScopeProperty* sprop,
|
||||
nanojit::LIns*& dslots_ins, nanojit::LIns* v_ins);
|
||||
JSRecordingStatus native_get(nanojit::LIns* obj_ins, nanojit::LIns* pobj_ins,
|
||||
|
Loading…
Reference in New Issue
Block a user