Bug 535656 - remove JSStackFrame::dormantNext and varobj (r=waldo)

This commit is contained in:
Luke Wagner 2010-01-29 18:25:16 -08:00
parent a20b5365ba
commit 742f31ca8a
17 changed files with 428 additions and 190 deletions

View File

@ -5186,16 +5186,10 @@ JS_IsConstructing(JSContext *cx)
JS_PUBLIC_API(JSStackFrame *)
JS_SaveFrameChain(JSContext *cx)
{
JSStackFrame *fp;
fp = js_GetTopStackFrame(cx);
JSStackFrame *fp = js_GetTopStackFrame(cx);
if (!fp)
return fp;
JS_ASSERT(!fp->dormantNext);
fp->dormantNext = cx->dormantFrameChain;
cx->dormantFrameChain = fp;
cx->fp = NULL;
return NULL;
cx->saveActiveCallStack();
return fp;
}
@ -5206,11 +5200,7 @@ JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp)
JS_ASSERT(!cx->fp);
if (!fp)
return;
JS_ASSERT(fp == cx->dormantFrameChain);
cx->fp = fp;
cx->dormantFrameChain = fp->dormantNext;
fp->dormantNext = NULL;
cx->restoreCallStack();
}
/************************************************************************/

View File

@ -79,6 +79,27 @@ FreeContext(JSContext *cx);
static void
MarkLocalRoots(JSTracer *trc, JSLocalRootStack *lrs);
#ifdef DEBUG
bool
CallStack::contains(JSStackFrame *fp)
{
JSStackFrame *start;
JSStackFrame *stop;
if (isSuspended()) {
start = suspendedFrame;
stop = initialFrame->down;
} else {
start = cx->fp;
stop = cx->activeCallStack()->initialFrame->down;
}
for (JSStackFrame *f = start; f != stop; f = f->down) {
if (f == fp)
return true;
}
return false;
}
#endif
void
JSThreadData::init()
{

View File

@ -194,6 +194,107 @@ struct GlobalState {
SlotList* globalSlots;
};
/*
* A callstack contains a set of stack frames linked by fp->down. A callstack
* is a member of a JSContext and all of a JSContext's callstacks are kept in a
* list starting at cx->currentCallStack. A callstack may be active or
* suspended. There are zero or one active callstacks for a context and any
* number of suspended contexts. If there is an active context, it is the first
* in the currentCallStack list, |cx->fp != NULL| and the callstack's newest
* (top) stack frame is |cx->fp|. For all other (suspended) callstacks, the
* newest frame is pointed to by suspendedFrame.
*
* While all frames in a callstack are down-linked, not all down-linked frames
* are in the same callstack (e.g., calling js_Execute with |down != cx->fp|
* will create a new frame in a new active callstack).
*/
class CallStack
{
#ifdef DEBUG
/* The context to which this callstack belongs. */
JSContext *cx;
#endif
/* If this callstack is suspended, the top of the callstack. */
JSStackFrame *suspendedFrame;
/* This callstack was suspended by JS_SaveFrameChain. */
bool saved;
/* Links members of the JSContext::currentCallStack list. */
CallStack *previous;
/* The varobj on entry to initialFrame. */
JSObject *initialVarObj;
/* The first frame executed in this callstack. */
JSStackFrame *initialFrame;
public:
CallStack(JSContext *cx)
:
#ifdef DEBUG
cx(cx),
#endif
suspendedFrame(NULL), saved(false), previous(NULL),
initialVarObj(NULL), initialFrame(NULL)
{}
#ifdef DEBUG
bool contains(JSStackFrame *fp);
#endif
void suspend(JSStackFrame *fp) {
JS_ASSERT(fp && !isSuspended() && contains(fp));
suspendedFrame = fp;
}
void resume() {
JS_ASSERT(suspendedFrame);
suspendedFrame = NULL;
}
JSStackFrame *getSuspendedFrame() const {
JS_ASSERT(suspendedFrame);
return suspendedFrame;
}
bool isSuspended() const { return suspendedFrame; }
void setPrevious(CallStack *cs) { previous = cs; }
CallStack *getPrevious() const { return previous; }
void setInitialVarObj(JSObject *o) { initialVarObj = o; }
JSObject *getInitialVarObj() const { return initialVarObj; }
void setInitialFrame(JSStackFrame *f) { initialFrame = f; }
JSStackFrame *getInitialFrame() const { return initialFrame; }
/*
* Saving and restoring is a special case of suspending and resuming
* whereby the active callstack becomes suspended without pushing a new
* active callstack. This means that if a callstack c1 is pushed on top of a
* saved callstack c2, when c1 is popped, c2 must not be made active. In
* the normal case, where c2 is not saved, when c1 is popped, c2 is made
* active. This distinction is indicated by the |saved| flag.
*/
void save(JSStackFrame *fp) {
suspend(fp);
saved = true;
}
void restore() {
saved = false;
resume();
}
bool isSaved() const {
JS_ASSERT_IF(saved, isSuspended());
return saved;
}
};
/*
* Trace monitor. Every JSThread (if JS_THREADSAFE) or JSRuntime (if not
* JS_THREADSAFE) has an associated trace monitor that keeps track of loop
@ -1087,6 +1188,16 @@ typedef struct JSResolvingEntry {
extern const JSDebugHooks js_NullDebugHooks; /* defined in jsdbgapi.cpp */
/*
* Wraps a stack frame which has been temporarily popped from its call stack
* and needs to be GC-reachable. See JSContext::{push,pop}GCReachableFrame.
*/
struct JSGCReachableFrame
{
JSGCReachableFrame *next;
JSStackFrame *frame;
};
struct JSContext {
/*
* If this flag is set, we were asked to call back the operation callback
@ -1204,8 +1315,67 @@ struct JSContext {
void *data;
void *data2;
/* GC and thread-safe state. */
JSStackFrame *dormantFrameChain; /* dormant stack frame to scan */
/* Linked list of frames temporarily popped from their chain. */
JSGCReachableFrame *reachableFrames;
void pushGCReachableFrame(JSGCReachableFrame &gcrf, JSStackFrame *f) {
gcrf.next = reachableFrames;
gcrf.frame = f;
reachableFrames = &gcrf;
}
void popGCReachableFrame() {
reachableFrames = reachableFrames->next;
}
private:
friend void js_TraceContext(JSTracer *, JSContext *);
/* Linked list of callstacks. See CallStack. */
js::CallStack *currentCallStack;
public:
/* Assuming there is an active callstack, return it. */
js::CallStack *activeCallStack() const {
JS_ASSERT(currentCallStack && !currentCallStack->isSaved());
return currentCallStack;
}
/* Add the given callstack to the list as the new active callstack. */
void pushCallStack(js::CallStack *newcs) {
if (fp)
currentCallStack->suspend(fp);
else
JS_ASSERT_IF(currentCallStack, currentCallStack->isSaved());
newcs->setPrevious(currentCallStack);
currentCallStack = newcs;
JS_ASSERT(!newcs->isSuspended() && !newcs->isSaved());
}
/* Remove the active callstack and make the next callstack active. */
void popCallStack() {
JS_ASSERT(!currentCallStack->isSuspended() && !currentCallStack->isSaved());
currentCallStack = currentCallStack->getPrevious();
if (currentCallStack && !currentCallStack->isSaved()) {
JS_ASSERT(fp);
currentCallStack->resume();
}
}
/* Mark the top callstack as suspended, without pushing a new one. */
void saveActiveCallStack() {
JS_ASSERT(fp && currentCallStack && !currentCallStack->isSuspended());
currentCallStack->save(fp);
fp = NULL;
}
/* Undoes calls to suspendTopCallStack. */
void restoreCallStack() {
JS_ASSERT(!fp && currentCallStack && currentCallStack->isSuspended());
fp = currentCallStack->getSuspendedFrame();
currentCallStack->restore();
}
#ifdef JS_THREADSAFE
JSThread *thread;
jsrefcount requestDepth;
@ -1428,6 +1598,20 @@ private:
void checkMallocGCPressure(void *p);
};
JS_ALWAYS_INLINE JSObject *
JSStackFrame::varobj(js::CallStack *cs)
{
JS_ASSERT(cs->contains(this));
return fun ? callobj : cs->getInitialVarObj();
}
JS_ALWAYS_INLINE JSObject *
JSStackFrame::varobj(JSContext *cx)
{
JS_ASSERT(cx->activeCallStack()->contains(this));
return fun ? callobj : cx->activeCallStack()->getInitialVarObj();
}
#ifdef JS_THREADSAFE
# define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0)
#endif

View File

@ -1221,18 +1221,16 @@ JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
JS_PUBLIC_API(JSObject *)
JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
{
JSStackFrame *afp;
if (fp->flags & JSFRAME_COMPUTED_THIS)
return JSVAL_TO_OBJECT(fp->thisv); /* JSVAL_COMPUTED_THIS invariant */
/* js_ComputeThis gets confused if fp != cx->fp, so set it aside. */
if (js_GetTopStackFrame(cx) != fp) {
afp = cx->fp;
JSStackFrame *afp = js_GetTopStackFrame(cx);
JSGCReachableFrame reachable;
if (afp != fp) {
if (afp) {
afp->dormantNext = cx->dormantFrameChain;
cx->dormantFrameChain = afp;
cx->fp = fp;
cx->pushGCReachableFrame(reachable, afp);
}
} else {
afp = NULL;
@ -1243,8 +1241,7 @@ JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
if (afp) {
cx->fp = afp;
cx->dormantFrameChain = afp->dormantNext;
afp->dormantNext = NULL;
cx->popGCReachableFrame();
}
return JSVAL_TO_OBJECT(fp->thisv);

View File

@ -2099,7 +2099,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JSObject *scopeobj = (cg->flags & TCF_IN_FUNCTION)
? STOBJ_GET_PARENT(FUN_OBJECT(cg->fun))
: cg->scopeChain;
if (scopeobj != caller->varobj)
if (scopeobj != caller->varobj(cx))
return JS_TRUE;
/*
@ -2194,7 +2194,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JSCodeGenerator *evalcg = (JSCodeGenerator *) tc;
JS_ASSERT(evalcg->flags & TCF_COMPILE_N_GO);
JS_ASSERT(caller->fun && caller->varobj == evalcg->scopeChain);
JS_ASSERT(caller->fun && caller->varobj(cx) == evalcg->scopeChain);
/*
* Don't generate upvars on the left side of a for loop. See

View File

@ -229,7 +229,9 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
* We must be in a function activation; the function must be lightweight
* or else fp must have a variable object.
*/
JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj));
JS_ASSERT(fp->fun);
JS_ASSERT_IF(fp->fun->flags & JSFUN_HEAVYWEIGHT,
fp->varobj(js_ContainingCallStack(cx, fp)));
/* Skip eval and debugger frames. */
while (fp->flags & JSFRAME_SPECIAL)
@ -851,7 +853,6 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
* variables object.
*/
fp->scopeChain = callobj;
fp->varobj = callobj;
return callobj;
}

View File

@ -2282,8 +2282,6 @@ js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp)
JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call");
if (fp->argsobj)
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) {
js_TraceScript(trc, fp->script);
@ -2367,10 +2365,17 @@ JSWeakRoots::mark(JSTracer *trc)
js_CallValueTracerIfGCThing(trc, lastInternalResult);
}
static void inline
TraceFrameChain(JSTracer *trc, JSStackFrame *fp)
{
do {
js_TraceStackFrame(trc, fp);
} while ((fp = fp->down) != NULL);
}
JS_REQUIRES_STACK JS_FRIEND_API(void)
js_TraceContext(JSTracer *trc, JSContext *acx)
{
JSStackFrame *fp, *nextChain;
JSStackHeader *sh;
JSTempValueRooter *tvr;
@ -2403,9 +2408,7 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
}
/*
* Iterate frame chain and dormant chains.
*
* (NB: see comment on this whole "dormant" thing in js_Execute.)
* Trace active and suspended callstacks.
*
* Since js_GetTopStackFrame needs to dereference cx->thread to check for
* JIT frames, we check for non-null thread here and avoid null checks
@ -2415,26 +2418,29 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
if (acx->thread)
#endif
{
fp = js_GetTopStackFrame(acx);
nextChain = acx->dormantFrameChain;
if (!fp)
goto next_chain;
/* If |cx->fp|, the active callstack has newest (top) frame |cx->fp|. */
JSStackFrame *fp = js_GetTopStackFrame(acx);
if (fp) {
JS_ASSERT(!acx->activeCallStack()->isSuspended());
TraceFrameChain(trc, fp);
if (JSObject *o = acx->activeCallStack()->getInitialVarObj())
JS_CALL_OBJECT_TRACER(trc, o, "variables");
}
/* The top frame must not be dormant. */
JS_ASSERT(!fp->dormantNext);
for (;;) {
do {
js_TraceStackFrame(trc, fp);
} while ((fp = fp->down) != NULL);
next_chain:
if (!nextChain)
break;
fp = nextChain;
nextChain = nextChain->dormantNext;
/* Trace suspended frames. */
CallStack *cur = acx->currentCallStack;
CallStack *cs = fp ? cur->getPrevious() : cur;
for (; cs; cs = cs->getPrevious()) {
TraceFrameChain(trc, cs->getSuspendedFrame());
if (cs->getInitialVarObj())
JS_CALL_OBJECT_TRACER(trc, cs->getInitialVarObj(), "var env");
}
}
/* Trace frames that have been temporarily removed but need to be marked. */
for (JSGCReachableFrame *rf = acx->reachableFrames; rf; rf = rf->next)
TraceFrameChain(trc, rf->frame);
/* Mark other roots-by-definition in acx. */
if (acx->globalObject && !JS_HAS_OPTION(acx, JSOPTION_UNROOTED_GLOBAL))
JS_CALL_OBJECT_TRACER(trc, acx->globalObject, "global object");

View File

@ -896,7 +896,6 @@ js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv)
!OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) {
thisp = cx->globalObject;
} else {
JSStackFrame *fp;
jsid id;
jsval v;
uintN attrs;
@ -912,24 +911,26 @@ js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv)
* FIXME: 417851 -- this access check should not be required, as it
* imposes a performance penalty on all js_ComputeGlobalThis calls,
* and it represents a maintenance hazard.
*
* When the above FIXME is made fixed, the whole GC reachable frame
* mechanism can be removed as well.
*/
fp = js_GetTopStackFrame(cx); /* quell GCC overwarning */
JSStackFrame *fp = js_GetTopStackFrame(cx);
JSGCReachableFrame reachable;
if (lazy) {
JS_ASSERT(fp->argv == argv);
fp->dormantNext = cx->dormantFrameChain;
cx->dormantFrameChain = fp;
cx->fp = fp->down;
fp->down = NULL;
cx->pushGCReachableFrame(reachable, fp);
}
thisp = JSVAL_TO_OBJECT(argv[-2]);
id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
ok = thisp->checkAccess(cx, id, JSACC_PARENT, &v, &attrs);
if (lazy) {
cx->dormantFrameChain = fp->dormantNext;
fp->dormantNext = NULL;
fp->down = cx->fp;
cx->fp = fp;
cx->popGCReachableFrame();
}
if (!ok)
return NULL;
@ -1099,6 +1100,7 @@ JS_REQUIRES_STACK JS_FRIEND_API(JSBool)
js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
{
void *mark;
CallStack callStack(cx);
JSStackFrame frame;
jsval *sp, *argv, *newvp;
jsval v;
@ -1113,6 +1115,7 @@ js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
uint32 rootedArgsFlag;
JSInterpreterHook hook;
void *hookData;
bool pushCall;
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
@ -1303,7 +1306,6 @@ have_fun:
* Initialize the frame.
*/
frame.thisv = vp[1];
frame.varobj = NULL;
frame.callobj = NULL;
frame.argsobj = NULL;
frame.script = script;
@ -1321,10 +1323,18 @@ have_fun:
frame.imacpc = NULL;
frame.slots = NULL;
frame.flags = flags | rootedArgsFlag;
frame.dormantNext = NULL;
frame.displaySave = NULL;
MUST_FLOW_THROUGH("out");
pushCall = !cx->fp;
if (pushCall) {
/*
* The initialVarObj is left NULL since fp->callobj is NULL and, for
* interpreted functions, fp->varobj() == fp->callobj.
*/
callStack.setInitialFrame(&frame);
cx->pushCallStack(&callStack);
}
cx->fp = &frame;
/* Init these now in case we goto out before first hook call. */
@ -1332,15 +1342,13 @@ have_fun:
hookData = NULL;
if (native) {
/* If native, use caller varobj and scopeChain for eval. */
JS_ASSERT(!frame.varobj);
JS_ASSERT(!frame.scopeChain);
/* Slow natives expect the caller's scopeChain as their scopeChain. */
if (frame.down) {
frame.varobj = frame.down->varobj;
JS_ASSERT(!pushCall);
frame.scopeChain = frame.down->scopeChain;
}
/* But ensure that we have a scope chain. */
/* Ensure that we have a scope chain. */
if (!frame.scopeChain)
frame.scopeChain = parent;
} else {
@ -1408,6 +1416,8 @@ out:
*vp = frame.rval;
/* Restore cx->fp now that we're done releasing frame objects. */
if (pushCall)
cx->popCallStack();
cx->fp = frame.down;
out2:
@ -1480,16 +1490,37 @@ js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
return js_InternalCall(cx, obj, fval, argc, argv, rval);
}
CallStack *
js_ContainingCallStack(JSContext *cx, JSStackFrame *target)
{
JS_ASSERT(cx->fp);
/* The active callstack's top frame is cx->fp. */
CallStack *cs = cx->activeCallStack();
JSStackFrame *f = cx->fp;
JSStackFrame *stop = cs->getInitialFrame()->down;
for (; f != stop; f = f->down) {
if (f == target)
return cs;
}
/* A suspended callstack's top frame is its suspended frame. */
for (cs = cs->getPrevious(); cs; cs = cs->getPrevious()) {
f = cs->getSuspendedFrame();
stop = cs->getInitialFrame()->down;
for (; f != stop; f = f->down) {
if (f == target)
return cs;
}
}
return NULL;
}
JSBool
js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
JSStackFrame *down, uintN flags, jsval *result)
{
JSInterpreterHook hook;
void *hookData, *mark;
JSStackFrame *oldfp, frame;
JSObject *obj, *tmp;
JSBool ok;
if (script->isEmpty()) {
if (result)
*result = JSVAL_VOID;
@ -1499,19 +1530,29 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
LeaveTrace(cx);
#ifdef INCLUDE_MOZILLA_DTRACE
if (JAVASCRIPT_EXECUTE_START_ENABLED())
jsdtrace_execute_start(script);
struct JSDNotifyGuard {
JSScript *script;
JSDNotifyGuard(JSScript *s) : script(s) {
if (JAVASCRIPT_EXECUTE_START_ENABLED())
jsdtrace_execute_start(script);
}
~JSDNotifyGuard() {
if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
jsdtrace_execute_done(script);
}
} jsdNotifyGuard(script);
#endif
hook = cx->debugHooks->executeHook;
hookData = mark = NULL;
oldfp = js_GetTopStackFrame(cx);
JSInterpreterHook hook = cx->debugHooks->executeHook;
void *hookData = NULL;
JSStackFrame frame;
CallStack callStack(cx);
frame.script = script;
if (down) {
/* Propagate arg state for eval and the debugger API. */
frame.callobj = down->callobj;
frame.argsobj = down->argsobj;
frame.varobj = down->varobj;
frame.fun = (script->staticLevel > 0) ? down->fun : NULL;
frame.thisv = down->thisv;
if (down->flags & JSFRAME_COMPUTED_THIS)
@ -1519,29 +1560,49 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
frame.argc = down->argc;
frame.argv = down->argv;
frame.annotation = down->annotation;
/*
* We want to call |down->varobj()|, but this requires knowing the
* CallStack of |down|. If |down == cx->fp|, the callstack is simply
* the context's active callstack, so we can use |down->varobj(cx)|.
* When |down != cx->fp|, we need to do a slow linear search. Luckily,
* this only happens with eval and JS_EvaluateInStackFrame.
*/
if (down == cx->fp) {
callStack.setInitialVarObj(down->varobj(cx));
} else {
CallStack *cs = js_ContainingCallStack(cx, down);
callStack.setInitialVarObj(down->varobj(cs));
}
} else {
frame.callobj = NULL;
frame.argsobj = NULL;
obj = chain;
JSObject *obj = chain;
if (cx->options & JSOPTION_VAROBJFIX) {
while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
while (JSObject *tmp = OBJ_GET_PARENT(cx, obj))
obj = tmp;
}
frame.varobj = obj;
frame.fun = NULL;
frame.thisv = OBJECT_TO_JSVAL(chain);
frame.argc = 0;
frame.argv = NULL;
frame.annotation = NULL;
callStack.setInitialVarObj(obj);
}
frame.imacpc = NULL;
struct RawStackGuard {
JSContext *cx;
void *mark;
RawStackGuard(JSContext *cx) : cx(cx), mark(NULL) {}
~RawStackGuard() { if (mark) js_FreeRawStack(cx, mark); }
} rawStackGuard(cx);
if (script->nslots != 0) {
frame.slots = js_AllocRawStack(cx, script->nslots, &mark);
if (!frame.slots) {
ok = JS_FALSE;
goto out;
}
frame.slots = js_AllocRawStack(cx, script->nslots, &rawStackGuard.mark);
if (!frame.slots)
return false;
memset(frame.slots, 0, script->nfixed * sizeof(jsval));
#if JS_HAS_SHARP_VARS
@ -1556,10 +1617,8 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
int base = (down->fun && !(down->flags & JSFRAME_SPECIAL))
? down->fun->sharpSlotBase(cx)
: down->script->nfixed - SHARP_NSLOTS;
if (base < 0) {
ok = JS_FALSE;
goto out;
}
if (base < 0)
return false;
sharps[0] = down->slots[base];
sharps[1] = down->slots[base + 1];
} else {
@ -1576,40 +1635,43 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
frame.scopeChain = chain;
frame.regs = NULL;
frame.flags = flags;
frame.dormantNext = NULL;
frame.blockChain = NULL;
/*
* Here we wrap the call to js_Interpret with code to (conditionally)
* save and restore the old stack frame chain into a chain of 'dormant'
* frame chains. Since we are replacing cx->fp, we were running into
* the problem that if GC was called under this frame, some of the GC
* things associated with the old frame chain (available here only in
* the C variable 'oldfp') were not rooted and were being collected.
*
* So, now we preserve the links to these 'dormant' frame chains in cx
* before calling js_Interpret and cleanup afterwards. The GC walks
* these dormant chains and marks objects in the same way that it marks
* objects in the primary cx->fp chain.
* We need to push/pop a new callstack if there is no existing callstack
* or the current callstack needs to be suspended (so that its frames are
* marked by GC).
*/
if (oldfp && oldfp != down) {
JS_ASSERT(!oldfp->dormantNext);
oldfp->dormantNext = cx->dormantFrameChain;
cx->dormantFrameChain = oldfp;
JSStackFrame *oldfp = cx->fp;
bool newCallStack = !oldfp || oldfp != down;
if (newCallStack) {
callStack.setInitialFrame(&frame);
cx->pushCallStack(&callStack);
}
cx->fp = &frame;
struct FinishGuard {
JSContext *cx;
JSStackFrame *oldfp;
bool newCallStack;
FinishGuard(JSContext *cx, JSStackFrame *oldfp, bool newCallStack)
: cx(cx), oldfp(oldfp), newCallStack(newCallStack) {}
~FinishGuard() {
if (newCallStack)
cx->popCallStack();
cx->fp = oldfp;
}
} finishGuard(cx, oldfp, newCallStack);
if (!down) {
OBJ_TO_INNER_OBJECT(cx, chain);
if (!chain)
return JS_FALSE;
return false;
frame.scopeChain = chain;
JSObject *thisp = JSVAL_TO_OBJECT(frame.thisv)->thisObject(cx);
if (!thisp) {
ok = JS_FALSE;
goto out2;
}
if (!thisp)
return false;
frame.thisv = OBJECT_TO_JSVAL(thisp);
frame.flags |= JSFRAME_COMPUTED_THIS;
}
@ -1619,7 +1681,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
cx->debugHooks->executeHookData);
}
ok = js_Interpret(cx);
JSBool ok = js_Interpret(cx);
if (result)
*result = frame.rval;
@ -1629,22 +1691,6 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
hook(cx, &frame, JS_FALSE, &ok, hookData);
}
out2:
if (mark)
js_FreeRawStack(cx, mark);
cx->fp = oldfp;
if (oldfp && oldfp != down) {
JS_ASSERT(cx->dormantFrameChain == oldfp);
cx->dormantFrameChain = oldfp->dormantNext;
oldfp->dormantNext = NULL;
}
out:
#ifdef INCLUDE_MOZILLA_DTRACE
if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
jsdtrace_execute_done(script);
#endif
return ok;
}

View File

@ -56,6 +56,7 @@ typedef struct JSFrameRegs {
jsval *sp; /* stack pointer */
} JSFrameRegs;
/*
* JS stack frame, may be allocated on the C stack by native callers. Always
* allocated on cx->stackPool for calls from the interpreter to an interpreted
@ -73,7 +74,6 @@ struct JSStackFrame {
JSObject *callobj; /* lazily created Call object */
jsval argsobj; /* lazily created arguments object, must be
JSVAL_OBJECT */
JSObject *varobj; /* variables object, where vars go */
JSScript *script; /* script being interpreted */
JSFunction *fun; /* function being called or null */
jsval thisv; /* "this" pointer if in method */
@ -123,7 +123,6 @@ struct JSStackFrame {
JSObject *blockChain;
uint32 flags; /* frame flags -- see below */
JSStackFrame *dormantNext; /* next dormant frame chain */
JSStackFrame *displaySave; /* previous value of display entry for
script->staticLevel */
@ -155,9 +154,26 @@ struct JSStackFrame {
JSObject *callee() {
return argv ? JSVAL_TO_OBJECT(argv[-2]) : NULL;
}
/*
* Get the object associated with the Execution Context's
* VariableEnvironment (ES5 10.3). The given CallStack must contain this
* stack frame.
*/
JSObject *varobj(js::CallStack *cs);
/* Short for: varobj(cx->activeCallStack()). */
JSObject *varobj(JSContext *cx);
};
#ifdef __cplusplus
/*
* Perform a linear search of all frames in all callstacks in the given context
* for the given frame, returning the callstack, if found, and NULL otherwise.
*/
extern js::CallStack *
js_ContainingCallStack(JSContext *cx, JSStackFrame *target);
static JS_INLINE uintN
FramePCOffset(JSStackFrame* fp)
{

View File

@ -714,8 +714,8 @@ JS_FRIEND_DATA(JSClass) js_GeneratorClass = {
* from the activation in fp, so we can steal away fp->callobj and fp->argsobj
* if they are non-null.
*/
JSObject *
js_NewGenerator(JSContext *cx, JSStackFrame *fp)
JS_REQUIRES_STACK JSObject *
js_NewGenerator(JSContext *cx)
{
JSObject *obj;
uintN argc, nargs, nslots;
@ -727,6 +727,7 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
return NULL;
/* Load and compute stack slot counts. */
JSStackFrame *fp = cx->fp;
argc = fp->argc;
nargs = JS_MAX(argc, fp->fun->nargs);
nslots = 2 + nargs + fp->script->nslots;
@ -752,7 +753,6 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
}
/* These two references can be shared with fp until it goes away. */
gen->frame.varobj = fp->varobj;
gen->frame.thisv = fp->thisv;
/* Copy call-invariant script and function references. */
@ -786,7 +786,6 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
gen->frame.regs = &gen->savedRegs;
gen->frame.flags = (fp->flags & ~JSFRAME_ROOTED_ARGV) | JSFRAME_GENERATOR;
gen->frame.dormantNext = NULL;
/* JSOP_GENERATOR appears in the prologue, outside all blocks. */
JS_ASSERT(!fp->blockChain);

View File

@ -117,7 +117,7 @@ struct JSGenerator {
((JSGenerator *) ((uint8 *)(fp) - offsetof(JSGenerator, frame)))
extern JSObject *
js_NewGenerator(JSContext *cx, JSStackFrame *fp);
js_NewGenerator(JSContext *cx);
#endif

View File

@ -1256,11 +1256,11 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSBool ok;
JSScript **bucket = NULL; /* avoid GCC warning with early decl&init */
#if JS_HAS_EVAL_THIS_SCOPE
JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
JSObject *withObject = NULL;
JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
JSTempValueRooter scopetvr, varobjtvr;
JSObject *callerScopeChain = NULL;
JSBool setCallerScopeChain = JS_FALSE;
JSTempValueRooter scopetvr;
#endif
JSObject *withObject = NULL;
fp = js_GetTopStackFrame(cx);
caller = js_GetScriptedCaller(cx, fp);
@ -1312,7 +1312,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
* object, then we need to provide one for the compiler to stick any
* declared (var) variables into.
*/
if (!caller->varobj && !js_GetCallObject(cx, caller))
if (caller->fun && !caller->callobj && !js_GetCallObject(cx, caller))
return JS_FALSE;
/* Accept an optional trailing argument that overrides the scope object. */
@ -1361,22 +1361,11 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
/* NB: We know obj is a global object here. */
JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
scopeobj = obj;
/* Set fp->scopeChain too, for the compiler. */
caller->scopeChain = fp->scopeChain = scopeobj;
caller->scopeChain = scopeobj = obj;
/* Remember scopeobj so we can null its private when done. */
setCallerScopeChain = JS_TRUE;
JS_PUSH_TEMP_ROOT_OBJECT(cx, callerScopeChain, &scopetvr);
callerVarObj = caller->varobj;
if (obj != callerVarObj) {
/* Set fp->varobj too, for the compiler. */
caller->varobj = fp->varobj = obj;
setCallerVarObj = JS_TRUE;
JS_PUSH_TEMP_ROOT_OBJECT(cx, callerVarObj, &varobjtvr);
}
} else {
/*
* Compile using the caller's current scope object.
@ -1553,17 +1542,13 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
out:
#if JS_HAS_EVAL_THIS_SCOPE
/* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
if (setCallerVarObj) {
caller->varobj = callerVarObj;
JS_POP_TEMP_ROOT(cx, &varobjtvr);
}
if (setCallerScopeChain) {
caller->scopeChain = callerScopeChain;
JS_POP_TEMP_ROOT(cx, &scopetvr);
}
#endif
if (withObject)
withObject->setPrivate(NULL);
#endif
return ok;
}
@ -7103,7 +7088,6 @@ js_DumpStackFrame(JSStackFrame *fp)
fprintf(stderr, " argv: %p (argc: %u)\n", (void *) fp->argv, (unsigned) fp->argc);
MaybeDumpObject("callobj", fp->callobj);
MaybeDumpObject("argsobj", JSVAL_TO_OBJECT(fp->argsobj));
MaybeDumpObject("varobj", fp->varobj);
MaybeDumpValue("this", fp->thisv);
fprintf(stderr, " rval: ");
dumpValue(fp->rval);
@ -7139,8 +7123,6 @@ js_DumpStackFrame(JSStackFrame *fp)
if (fp->blockChain)
fprintf(stderr, " blockChain: (JSObject *) %p\n", (void *) fp->blockChain);
if (fp->dormantNext)
fprintf(stderr, " dormantNext: (JSStackFrame *) %p\n", (void *) fp->dormantNext);
if (fp->displaySave)
fprintf(stderr, " displaySave: (JSStackFrame *) %p\n", (void *) fp->displaySave);

View File

@ -670,7 +670,7 @@ END_CASE(JSOP_PICK)
BEGIN_CASE(JSOP_SETCONST)
LOAD_ATOM(0);
obj = fp->varobj;
obj = fp->varobj(cx);
rval = FETCH_OPND(-1);
if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), rval,
JS_PropertyStub, JS_PropertyStub,
@ -1441,7 +1441,8 @@ BEGIN_CASE(JSOP_GVARINC)
DO_OP();
}
slot = JSVAL_TO_INT(lval);
rval = OBJ_GET_SLOT(cx, fp->varobj, slot);
JS_ASSERT(fp->varobj(cx) == cx->activeCallStack()->getInitialVarObj());
rval = OBJ_GET_SLOT(cx, cx->activeCallStack()->getInitialVarObj(), slot);
if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
PUSH_OPND(rval + incr2);
rval += incr;
@ -1453,7 +1454,7 @@ BEGIN_CASE(JSOP_GVARINC)
rval = regs.sp[-1];
--regs.sp;
}
OBJ_SET_SLOT(cx, fp->varobj, slot, rval);
OBJ_SET_SLOT(cx, fp->varobj(cx), slot, rval);
len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */
JS_ASSERT(len == js_CodeSpec[op].length);
DO_NEXT_OP(len);
@ -2152,7 +2153,6 @@ BEGIN_CASE(JSOP_APPLY)
newsp += nframeslots;
newifp->frame.callobj = NULL;
newifp->frame.argsobj = NULL;
newifp->frame.varobj = NULL;
newifp->frame.script = script;
newifp->frame.fun = fun;
newifp->frame.argc = argc;
@ -2162,7 +2162,6 @@ BEGIN_CASE(JSOP_APPLY)
newifp->frame.annotation = NULL;
newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj);
newifp->frame.flags = flags;
newifp->frame.dormantNext = NULL;
newifp->frame.blockChain = NULL;
if (script->staticLevel < JS_DISPLAY_SIZE) {
JSStackFrame **disp = &cx->display[script->staticLevel];
@ -2781,7 +2780,8 @@ BEGIN_CASE(JSOP_CALLGVAR)
op = (op == JSOP_GETGVAR) ? JSOP_NAME : JSOP_CALLNAME;
DO_OP();
}
obj = fp->varobj;
JS_ASSERT(fp->varobj(cx) == cx->activeCallStack()->getInitialVarObj());
obj = cx->activeCallStack()->getInitialVarObj();
slot = JSVAL_TO_INT(lval);
rval = OBJ_GET_SLOT(cx, obj, slot);
PUSH_OPND(rval);
@ -2794,7 +2794,8 @@ BEGIN_CASE(JSOP_SETGVAR)
JS_ASSERT(slot < GlobalVarCount(fp));
METER_SLOT_OP(op, slot);
rval = FETCH_OPND(-1);
obj = fp->varobj;
JS_ASSERT(fp->varobj(cx) == cx->activeCallStack()->getInitialVarObj());
obj = cx->activeCallStack()->getInitialVarObj();
lval = fp->slots[slot];
if (JSVAL_IS_NULL(lval)) {
/*
@ -2833,7 +2834,7 @@ BEGIN_CASE(JSOP_DEFVAR)
* code below we need the absolute value.
*/
index += atoms - script->atomMap.vector;
obj = fp->varobj;
obj = fp->varobj(cx);
JS_ASSERT(obj->map->ops->defineProperty == js_DefineProperty);
attrs = JSPROP_ENUMERATE;
if (!(fp->flags & JSFRAME_EVAL))
@ -2882,11 +2883,10 @@ BEGIN_CASE(JSOP_DEFVAR)
SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) &&
SPROP_HAS_STUB_SETTER(sprop)) {
/*
* Fast globals use frame variables to map the global
* name's atom index to the permanent fp->varobj slot
* number, tagged as a jsval. The atom index for the
* global's name literal is identical to its variable
* index.
* Fast globals use frame variables to map the global name's atom
* index to the permanent varobj slot number, tagged as a jsval.
* The atom index for the global's name literal is identical to its
* variable index.
*/
fp->slots[index] = INT_TO_JSVAL(sprop->slot);
}
@ -2991,7 +2991,7 @@ BEGIN_CASE(JSOP_DEFFUN)
* current scope chain even for the case of function expression statements
* and functions defined by eval inside let or with blocks.
*/
parent = fp->varobj;
parent = fp->varobj(cx);
JS_ASSERT(parent);
/*
@ -3039,7 +3039,7 @@ BEGIN_CASE(JSOP_DEFFUN)
: parent->defineProperty(cx, id, rval, getter, setter, attrs);
restore_scope:
/* Restore fp->scopeChain now that obj is defined in fp->varobj. */
/* Restore fp->scopeChain now that obj is defined in fp->callobj. */
fp->scopeChain = obj2;
if (!ok)
goto error;
@ -3067,7 +3067,7 @@ BEGIN_CASE(JSOP_DEFFUN_DBGFC)
rval = JSVAL_VOID;
}
parent = fp->varobj;
parent = fp->varobj(cx);
JS_ASSERT(parent);
id = ATOM_TO_JSID(fun->atom);
@ -4109,7 +4109,7 @@ END_CASE(JSOP_CALLBUILTIN)
BEGIN_CASE(JSOP_GENERATOR)
ASSERT_NOT_THROWING(cx);
regs.pc += JSOP_GENERATOR_LENGTH;
obj = js_NewGenerator(cx, fp);
obj = js_NewGenerator(cx);
if (!obj)
goto error;
JS_ASSERT(!fp->callobj && !fp->argsobj);

View File

@ -148,6 +148,7 @@ namespace js {
class TraceRecorder;
class TraceMonitor;
class CallStack;
class ContextAllocPolicy;
class SystemAllocPolicy;

View File

@ -330,12 +330,12 @@ script_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
*
* Unlike eval, which the compiler detects, Script.prototype.exec may be
* called from a lightweight function, or even from native code (in which
* case fp->varobj and fp->scopeChain are null). If exec is called from
* a lightweight function, we will need to get a Call object representing
* its frame, to act as the var object and scope chain head.
* fp->scopeChain is null). If exec is called from a lightweight function,
* we will need to get a Call object representing its frame, to act as the
* var object and scope chain head.
*/
caller = js_GetScriptedCaller(cx, NULL);
if (caller && !caller->varobj) {
if (caller && !caller->varobj(cx)) {
/* Called from a lightweight function. */
JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags));
@ -349,7 +349,7 @@ script_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
if (caller) {
/*
* Load caller->scopeChain after the conditional js_GetCallObject
* call above, which resets scopeChain as well as varobj.
* call above, which resets scopeChain as well as the callobj.
*/
scopeobj = js_GetScopeChain(cx, caller);
if (!scopeobj)

View File

@ -3519,8 +3519,6 @@ FlushNativeStackFrame(JSContext* cx, unsigned callDepth, const TraceType* mp, do
// we need to update the frame fields.
if (!fp->callobj)
fp->callobj = fp->scopeChain;
if (!fp->varobj)
fp->varobj = fp->scopeChain;
// Iff scope chain's private is NULL, then |fp->scopeChain| was created
// on trace for a call, so we set the private field now. (Call objects
@ -5627,7 +5625,6 @@ SynthesizeFrame(JSContext* cx, const FrameInfo& fi, JSObject* callee)
newifp->frame.callobj = NULL;
newifp->frame.argsobj = NULL;
newifp->frame.varobj = NULL;
newifp->frame.script = script;
newifp->frame.fun = fun;
@ -5659,7 +5656,6 @@ SynthesizeFrame(JSContext* cx, const FrameInfo& fi, JSObject* callee)
newifp->frame.annotation = NULL;
newifp->frame.scopeChain = NULL; // will be updated in FlushNativeStackFrame
newifp->frame.flags = constructing ? JSFRAME_CONSTRUCTING : 0;
newifp->frame.dormantNext = NULL;
newifp->frame.blockChain = NULL;
newifp->mark = newmark;
newifp->frame.thisv = JSVAL_NULL; // will be updated in FlushNativeStackFrame
@ -5729,7 +5725,6 @@ SynthesizeSlowNativeFrame(InterpState& state, JSContext *cx, VMSideExit *exit)
fp->slots = NULL;
fp->callobj = NULL;
fp->argsobj = NULL;
fp->varobj = cx->fp->varobj;
fp->script = NULL;
fp->thisv = state.nativeVp[1];
fp->argc = state.nativeVpLen - 2;
@ -5742,7 +5737,6 @@ SynthesizeSlowNativeFrame(InterpState& state, JSContext *cx, VMSideExit *exit)
fp->scopeChain = cx->fp->scopeChain;
fp->blockChain = NULL;
fp->flags = exit->constructing() ? JSFRAME_CONSTRUCTING : 0;
fp->dormantNext = NULL;
fp->displaySave = NULL;
ifp->mark = mark;

View File

@ -7506,8 +7506,8 @@ js_GetFunctionNamespace(JSContext *cx, jsval *vp)
* Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
* Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
* while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj. There's no
* requirement that fp->varobj lie directly on fp->scopeChain, although it
* should be reachable using the prototype chain from a scope object (cf.
* requirement that fp->varobj lie directly on fp->scopeChain, although
* it should be reachable using the prototype chain from a scope object (cf.
* JSOPTION_VAROBJFIX in jsapi.h).
*
* If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
@ -7567,9 +7567,10 @@ js_SetDefaultXMLNamespace(JSContext *cx, jsval v)
v = OBJECT_TO_JSVAL(ns);
fp = js_GetTopStackFrame(cx);
varobj = fp->varobj;
varobj = fp->varobj(cx);
if (!varobj->defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, v,
JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT)) {
JS_PropertyStub, JS_PropertyStub,
JSPROP_PERMANENT)) {
return JS_FALSE;
}
return JS_TRUE;