Bug 547851 - remove JSStackFrame::regs, JSStackFrame::callerFrame.sp (r=dvander)

This commit is contained in:
Luke Wagner
2010-03-03 18:10:13 -08:00
parent 4e60a6c93d
commit f6842fecdf
22 changed files with 671 additions and 483 deletions

View File

@@ -386,10 +386,7 @@ js_PopInterpFrame(JSContext* cx, TracerState* state)
cx->display[fp->script->staticLevel] = fp->displaySave; cx->display[fp->script->staticLevel] = fp->displaySave;
/* Pop the frame and its memory. */ /* Pop the frame and its memory. */
JSStackFrame *down = fp->down; cx->stack().popInlineFrame(cx, fp, fp->down);
cx->stack().popInlineFrame(cx, fp, down);
JS_ASSERT(cx->fp == down && cx->fp->regs == &fp->callerRegs);
down->regs = fp->regs;
/* Update the inline call count. */ /* Update the inline call count. */
*state->inlineCallCountp = *state->inlineCallCountp - 1; *state->inlineCallCountp = *state->inlineCallCountp - 1;

View File

@@ -277,7 +277,7 @@ StackSpace::getInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
assertIsCurrent(cx); assertIsCurrent(cx);
JS_ASSERT(currentCallStack->isActive()); JS_ASSERT(currentCallStack->isActive());
jsval *start = cx->fp->regs->sp; jsval *start = cx->regs->sp;
ptrdiff_t nvals = nmissing + VALUES_PER_CALL_STACK + VALUES_PER_STACK_FRAME + nfixed; ptrdiff_t nvals = nmissing + VALUES_PER_CALL_STACK + VALUES_PER_STACK_FRAME + nfixed;
if (!ensureSpace(cx, start, nvals)) if (!ensureSpace(cx, start, nvals))
return false; return false;
@@ -288,7 +288,7 @@ StackSpace::getInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
JS_REQUIRES_STACK void JS_REQUIRES_STACK void
StackSpace::pushInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag, StackSpace::pushInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
InvokeFrameGuard &fg) InvokeFrameGuard &fg, JSFrameRegs &regs)
{ {
JS_ASSERT(!!ag.cs ^ !!fg.cs); JS_ASSERT(!!ag.cs ^ !!fg.cs);
JS_ASSERT_IF(ag.cs, ag.cs == currentCallStack && !ag.cs->inContext()); JS_ASSERT_IF(ag.cs, ag.cs == currentCallStack && !ag.cs->inContext());
@@ -298,7 +298,7 @@ StackSpace::pushInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
} }
JSStackFrame *fp = fg.fp; JSStackFrame *fp = fg.fp;
fp->down = cx->fp; fp->down = cx->fp;
cx->pushCallStackAndFrame(currentCallStack, fp); cx->pushCallStackAndFrame(currentCallStack, fp, regs);
currentCallStack->setInitialVarObj(NULL); currentCallStack->setInitialVarObj(NULL);
fg.cx = cx; fg.cx = cx;
} }
@@ -361,13 +361,13 @@ StackSpace::getExecuteFrame(JSContext *cx, JSStackFrame *down,
JS_REQUIRES_STACK void JS_REQUIRES_STACK void
StackSpace::pushExecuteFrame(JSContext *cx, ExecuteFrameGuard &fg, StackSpace::pushExecuteFrame(JSContext *cx, ExecuteFrameGuard &fg,
JSObject *initialVarObj) JSFrameRegs &regs, JSObject *initialVarObj)
{ {
fg.fp->down = fg.down; fg.fp->down = fg.down;
CallStack *cs = fg.cs; CallStack *cs = fg.cs;
cs->setPreviousInThread(currentCallStack); cs->setPreviousInThread(currentCallStack);
currentCallStack = cs; currentCallStack = cs;
cx->pushCallStackAndFrame(cs, fg.fp); cx->pushCallStackAndFrame(cs, fg.fp, regs);
cs->setInitialVarObj(initialVarObj); cs->setInitialVarObj(initialVarObj);
fg.cx = cx; fg.cx = cx;
} }
@@ -391,13 +391,14 @@ StackSpace::getSynthesizedSlowNativeFrame(JSContext *cx, CallStack *&cs, JSStack
} }
JS_REQUIRES_STACK void JS_REQUIRES_STACK void
StackSpace::pushSynthesizedSlowNativeFrame(JSContext *cx, CallStack *cs, JSStackFrame *fp) StackSpace::pushSynthesizedSlowNativeFrame(JSContext *cx, CallStack *cs, JSStackFrame *fp,
JSFrameRegs &regs)
{ {
JS_ASSERT(!fp->script && FUN_SLOW_NATIVE(fp->fun)); JS_ASSERT(!fp->script && FUN_SLOW_NATIVE(fp->fun));
fp->down = cx->fp; fp->down = cx->fp;
cs->setPreviousInThread(currentCallStack); cs->setPreviousInThread(currentCallStack);
currentCallStack = cs; currentCallStack = cs;
cx->pushCallStackAndFrame(cs, fp); cx->pushCallStackAndFrame(cs, fp, regs);
cs->setInitialVarObj(NULL); cs->setInitialVarObj(NULL);
} }
@@ -412,6 +413,84 @@ StackSpace::popSynthesizedSlowNativeFrame(JSContext *cx)
currentCallStack = currentCallStack->getPreviousInThread(); currentCallStack = currentCallStack->getPreviousInThread();
} }
/*
* When a pair of down-linked stack frames are in the same callstack, the
* up-frame's address is the top of the down-frame's stack, modulo missing
* arguments.
*/
static inline jsval *
InlineDownFrameSP(JSStackFrame *up)
{
JS_ASSERT(up->fun && up->script);
jsval *sp = up->argv + up->argc;
#ifdef DEBUG
uint16 nargs = up->fun->nargs;
uintN argc = up->argc;
uintN missing = argc < nargs ? nargs - argc : 0;
JS_ASSERT(sp == (jsval *)up - missing);
#endif
return sp;
}
JS_REQUIRES_STACK
FrameRegsIter::FrameRegsIter(JSContext *cx)
{
curcs = cx->getCurrentCallStack();
if (!curcs) {
curfp = NULL;
return;
}
if (curcs->isSuspended()) {
curfp = curcs->getSuspendedFrame();
cursp = curcs->getSuspendedRegs()->sp;
curpc = curcs->getSuspendedRegs()->pc;
}
JS_ASSERT(cx->fp);
curfp = cx->fp;
cursp = cx->regs->sp;
curpc = cx->regs->pc;
return;
}
FrameRegsIter &
FrameRegsIter::operator++()
{
JSStackFrame *up = curfp;
JSStackFrame *down = curfp = curfp->down;
if (!down)
return *this;
curpc = down->savedPC;
/* For a contiguous down and up, compute sp from up. */
if (up != curcs->getInitialFrame()) {
cursp = InlineDownFrameSP(up);
return *this;
}
/*
* If the up-frame is in csup and the down-frame is in csdown, it is not
* necessarily the case that |csup->getPreviousInContext == csdown| or that
* |csdown->getSuspendedFrame == down| (because of indirect eval and
* JS_EvaluateInStackFrame). To compute down's sp, we need to do a linear
* scan, keeping track of what is immediately after down in memory.
*/
curcs = curcs->getPreviousInContext();
cursp = curcs->getSuspendedSP();
JSStackFrame *f = curcs->getSuspendedFrame();
while (f != down) {
if (f == curcs->getInitialFrame()) {
curcs = curcs->getPreviousInContext();
cursp = curcs->getSuspendedSP();
f = curcs->getSuspendedFrame();
} else {
cursp = InlineDownFrameSP(f);
f = f->down;
}
}
return *this;
}
bool bool
JSThreadData::init() JSThreadData::init()
{ {
@@ -1577,14 +1656,12 @@ ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
static void static void
PopulateReportBlame(JSContext *cx, JSErrorReport *report) PopulateReportBlame(JSContext *cx, JSErrorReport *report)
{ {
JSStackFrame *fp;
/* /*
* Walk stack until we find a frame that is associated with some script * Walk stack until we find a frame that is associated with some script
* rather than a native frame. * rather than a native frame.
*/ */
for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) { for (JSStackFrame *fp = js_GetTopStackFrame(cx); fp; fp = fp->down) {
if (fp->regs) { if (fp->pc(cx)) {
report->filename = fp->script->filename; report->filename = fp->script->filename;
report->lineno = js_FramePCToLineNumber(cx, fp); report->lineno = js_FramePCToLineNumber(cx, fp);
break; break;
@@ -2163,13 +2240,10 @@ js_GetCurrentBytecodePC(JSContext* cx)
#endif #endif
{ {
JS_ASSERT_NOT_ON_TRACE(cx); /* for static analysis */ JS_ASSERT_NOT_ON_TRACE(cx); /* for static analysis */
JSStackFrame* fp = cx->fp; pc = cx->regs ? cx->regs->pc : NULL;
if (fp && fp->regs) { if (!pc)
pc = fp->regs->pc;
imacpc = fp->imacpc;
} else {
return NULL; return NULL;
} imacpc = cx->fp->imacpc;
} }
/* /*
@@ -2194,18 +2268,27 @@ js_CurrentPCIsInImacro(JSContext *cx)
JSContext::JSContext(JSRuntime *rt) JSContext::JSContext(JSRuntime *rt)
: runtime(rt), : runtime(rt),
fp(NULL), fp(NULL),
regs(NULL),
regExpStatics(this), regExpStatics(this),
busyArrays(this) busyArrays(this)
{} {}
void void
JSContext::pushCallStackAndFrame(js::CallStack *newcs, JSStackFrame *newfp) JSContext::pushCallStackAndFrame(js::CallStack *newcs, JSStackFrame *newfp,
JSFrameRegs &newregs)
{ {
if (hasActiveCallStack()) if (hasActiveCallStack()) {
currentCallStack->suspend(fp); JS_ASSERT(fp->savedPC == JSStackFrame::sInvalidPC);
fp->savedPC = regs->pc;
currentCallStack->suspend(fp, regs);
}
newcs->setPreviousInContext(currentCallStack); newcs->setPreviousInContext(currentCallStack);
currentCallStack = newcs; currentCallStack = newcs;
#ifdef DEBUG
newfp->savedPC = JSStackFrame::sInvalidPC;
#endif
setCurrentFrame(newfp); setCurrentFrame(newfp);
setCurrentRegs(&newregs);
newcs->joinContext(this, newfp); newcs->joinContext(this, newfp);
} }
@@ -2214,18 +2297,25 @@ JSContext::popCallStackAndFrame()
{ {
JS_ASSERT(currentCallStack->maybeContext() == this); JS_ASSERT(currentCallStack->maybeContext() == this);
JS_ASSERT(currentCallStack->getInitialFrame() == fp); JS_ASSERT(currentCallStack->getInitialFrame() == fp);
JS_ASSERT(fp->savedPC == JSStackFrame::sInvalidPC);
currentCallStack->leaveContext(); currentCallStack->leaveContext();
currentCallStack = currentCallStack->getPreviousInContext(); currentCallStack = currentCallStack->getPreviousInContext();
if (currentCallStack) { if (currentCallStack) {
if (currentCallStack->isSaved()) { if (currentCallStack->isSaved()) {
setCurrentFrame(NULL); setCurrentFrame(NULL);
setCurrentRegs(NULL);
} else { } else {
setCurrentFrame(currentCallStack->getSuspendedFrame()); setCurrentFrame(currentCallStack->getSuspendedFrame());
setCurrentRegs(currentCallStack->getSuspendedRegs());
currentCallStack->resume(); currentCallStack->resume();
#ifdef DEBUG
fp->savedPC = JSStackFrame::sInvalidPC;
#endif
} }
} else { } else {
JS_ASSERT(fp->down == NULL); JS_ASSERT(fp->down == NULL);
setCurrentFrame(NULL); setCurrentFrame(NULL);
setCurrentRegs(NULL);
} }
} }
@@ -2233,16 +2323,23 @@ void
JSContext::saveActiveCallStack() JSContext::saveActiveCallStack()
{ {
JS_ASSERT(hasActiveCallStack()); JS_ASSERT(hasActiveCallStack());
currentCallStack->save(fp); currentCallStack->save(fp, regs);
JS_ASSERT(fp->savedPC == JSStackFrame::sInvalidPC);
fp->savedPC = regs->pc;
setCurrentFrame(NULL); setCurrentFrame(NULL);
setCurrentRegs(NULL);
} }
void void
JSContext::restoreCallStack() JSContext::restoreCallStack()
{ {
JS_ASSERT(!hasActiveCallStack()); js::CallStack *ccs = currentCallStack;
setCurrentFrame(currentCallStack->getSuspendedFrame()); setCurrentFrame(ccs->getSuspendedFrame());
currentCallStack->restore(); setCurrentRegs(ccs->getSuspendedRegs());
ccs->restore();
#ifdef DEBUG
fp->savedPC = JSStackFrame::sInvalidPC;
#endif
} }
JSGenerator * JSGenerator *
@@ -2265,7 +2362,7 @@ JSContext::generatorFor(JSStackFrame *fp) const
} }
CallStack * CallStack *
JSContext::containingCallStack(JSStackFrame *target) JSContext::containingCallStack(const JSStackFrame *target)
{ {
/* The context may have nothing running. */ /* The context may have nothing running. */
CallStack *cs = currentCallStack; CallStack *cs = currentCallStack;

View File

@@ -200,6 +200,10 @@ struct TracerState
uintN nativeVpLen; uintN nativeVpLen;
jsval* nativeVp; jsval* nativeVp;
// The regs pointed to by cx->regs while a deep-bailed slow native
// completes execution.
JSFrameRegs bailedSlowNativeRegs;
TracerState(JSContext *cx, TraceMonitor *tm, TreeFragment *ti, TracerState(JSContext *cx, TraceMonitor *tm, TreeFragment *ti,
uintN &inlineCallCountp, VMSideExit** innermostNestedGuardp); uintN &inlineCallCountp, VMSideExit** innermostNestedGuardp);
~TracerState(); ~TracerState();
@@ -251,10 +255,11 @@ struct GlobalState {
* *
* A callstack in a context may additionally be "active" or "suspended". A * A callstack in a context may additionally be "active" or "suspended". A
* suspended callstack |cs| has a "suspended frame" which serves as the current * suspended callstack |cs| has a "suspended frame" which serves as the current
* frame of |cs|. There is at most one active callstack in a given context. * frame of |cs|. Additionally, a suspended callstack has "suspended regs",
* Callstacks in a context execute LIFO and are maintained in a stack. The top * which is a snapshot of |cx->regs| when |cs| was suspended. There is at most
* of this stack is the context's "current callstack". If a context |cx| has an * one active callstack in a given context. Callstacks in a context execute
* active callstack |cs|, then: * LIFO and are maintained in a stack. The top of this stack is the context's
* "current callstack". If a context |cx| has an active callstack |cs|, then:
* 1. |cs| is |cx|'s current callstack, * 1. |cs| is |cx|'s current callstack,
* 2. |cx->fp != NULL|, and * 2. |cx->fp != NULL|, and
* 3. |cs|'s current frame is |cx->fp|. * 3. |cs|'s current frame is |cx->fp|.
@@ -284,6 +289,9 @@ class CallStack
/* If this callstack is suspended, the top of the callstack. */ /* If this callstack is suspended, the top of the callstack. */
JSStackFrame *suspendedFrame; JSStackFrame *suspendedFrame;
/* If this callstack is suspended, |cx->regs| when it was suspended. */
JSFrameRegs *suspendedRegs;
/* This callstack was suspended by JS_SaveFrameChain. */ /* This callstack was suspended by JS_SaveFrameChain. */
bool saved; bool saved;
@@ -366,11 +374,12 @@ class CallStack
/* Transitioning between isActive <--> isSuspended */ /* Transitioning between isActive <--> isSuspended */
void suspend(JSStackFrame *fp) { void suspend(JSStackFrame *fp, JSFrameRegs *regs) {
JS_ASSERT(isActive()); JS_ASSERT(isActive());
JS_ASSERT(fp && contains(fp)); JS_ASSERT(fp && contains(fp));
suspendedFrame = fp; suspendedFrame = fp;
JS_ASSERT(isSuspended()); JS_ASSERT(isSuspended());
suspendedRegs = regs;
} }
void resume() { void resume() {
@@ -381,9 +390,9 @@ class CallStack
/* When isSuspended, transitioning isSaved <--> !isSaved */ /* When isSuspended, transitioning isSaved <--> !isSaved */
void save(JSStackFrame *fp) { void save(JSStackFrame *fp, JSFrameRegs *regs) {
JS_ASSERT(!isSaved()); JS_ASSERT(!isSaved());
suspend(fp); suspend(fp, regs);
saved = true; saved = true;
JS_ASSERT(isSaved()); JS_ASSERT(isSaved());
} }
@@ -423,6 +432,16 @@ class CallStack
return suspendedFrame; return suspendedFrame;
} }
JSFrameRegs *getSuspendedRegs() const {
JS_ASSERT(isSuspended());
return suspendedRegs;
}
jsval *getSuspendedSP() const {
JS_ASSERT(isSuspended());
return suspendedRegs->sp;
}
/* JSContext / js::StackSpace bookkeeping. */ /* JSContext / js::StackSpace bookkeeping. */
void setPreviousInContext(CallStack *cs) { void setPreviousInContext(CallStack *cs) {
@@ -682,7 +701,7 @@ class StackSpace
JS_REQUIRES_STACK JS_REQUIRES_STACK
void pushInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag, void pushInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
InvokeFrameGuard &fg); InvokeFrameGuard &fg, JSFrameRegs &regs);
/* /*
* For the simpler case when arguments are allocated at the same time as * For the simpler case when arguments are allocated at the same time as
@@ -695,7 +714,7 @@ class StackSpace
ExecuteFrameGuard &fg) const; ExecuteFrameGuard &fg) const;
JS_REQUIRES_STACK JS_REQUIRES_STACK
void pushExecuteFrame(JSContext *cx, ExecuteFrameGuard &fg, void pushExecuteFrame(JSContext *cx, ExecuteFrameGuard &fg,
JSObject *initialVarObj); JSFrameRegs &regs, JSObject *initialVarObj);
/* /*
* Since RAII cannot be used for inline frames, callers must manually * Since RAII cannot be used for inline frames, callers must manually
@@ -706,7 +725,8 @@ class StackSpace
uintN nmissing, uintN nfixed) const; uintN nmissing, uintN nfixed) const;
JS_REQUIRES_STACK JS_REQUIRES_STACK
inline void pushInlineFrame(JSContext *cx, JSStackFrame *fp, JSStackFrame *newfp); inline void pushInlineFrame(JSContext *cx, JSStackFrame *fp, jsbytecode *pc,
JSStackFrame *newfp);
JS_REQUIRES_STACK JS_REQUIRES_STACK
inline void popInlineFrame(JSContext *cx, JSStackFrame *up, JSStackFrame *down); inline void popInlineFrame(JSContext *cx, JSStackFrame *up, JSStackFrame *down);
@@ -719,7 +739,8 @@ class StackSpace
void getSynthesizedSlowNativeFrame(JSContext *cx, CallStack *&cs, JSStackFrame *&fp); void getSynthesizedSlowNativeFrame(JSContext *cx, CallStack *&cs, JSStackFrame *&fp);
JS_REQUIRES_STACK JS_REQUIRES_STACK
void pushSynthesizedSlowNativeFrame(JSContext *cx, CallStack *cs, JSStackFrame *fp); void pushSynthesizedSlowNativeFrame(JSContext *cx, CallStack *cs, JSStackFrame *fp,
JSFrameRegs &regs);
JS_REQUIRES_STACK JS_REQUIRES_STACK
void popSynthesizedSlowNativeFrame(JSContext *cx); void popSynthesizedSlowNativeFrame(JSContext *cx);
@@ -731,6 +752,34 @@ class StackSpace
JS_STATIC_ASSERT(StackSpace::CAPACITY_VALS % StackSpace::COMMIT_VALS == 0); JS_STATIC_ASSERT(StackSpace::CAPACITY_VALS % StackSpace::COMMIT_VALS == 0);
/*
* While |cx->fp|'s pc/sp are available in |cx->regs|, to compute the saved
* value of pc/sp for any other frame, it is necessary to know about that
* frame's up-frame. This iterator maintains this information when walking down
* a chain of stack frames starting at |cx->fp|.
*
* Usage:
* for (FrameRegsIter i(cx); !i.done(); ++i)
* ... i.fp() ... i.sp() ... i.pc()
*/
class FrameRegsIter
{
CallStack *curcs;
JSStackFrame *curfp;
jsval *cursp;
jsbytecode *curpc;
public:
JS_REQUIRES_STACK FrameRegsIter(JSContext *cx);
bool done() const { return curfp == NULL; }
FrameRegsIter &operator++();
JSStackFrame *fp() const { return curfp; }
jsval *sp() const { return cursp; }
jsbytecode *pc() const { return curpc; }
};
/* Holds the number of recording attemps for an address. */ /* Holds the number of recording attemps for an address. */
typedef HashMap<jsbytecode*, typedef HashMap<jsbytecode*,
size_t, size_t,
@@ -1637,14 +1686,26 @@ struct JSContext
JS_REQUIRES_STACK JS_REQUIRES_STACK
JSStackFrame *fp; JSStackFrame *fp;
/*
* Currently executing frame's regs, set by stack operations.
* |fp != NULL| iff |regs != NULL| (although regs->pc can be NULL)
*/
JS_REQUIRES_STACK
JSFrameRegs *regs;
private: private:
friend class js::StackSpace; friend class js::StackSpace;
friend JSBool js_Interpret(JSContext *);
/* 'fp' must only be changed by calling this function. */ /* 'fp' and 'regs' must only be changed by calling these functions. */
void setCurrentFrame(JSStackFrame *fp) { void setCurrentFrame(JSStackFrame *fp) {
this->fp = fp; this->fp = fp;
} }
void setCurrentRegs(JSFrameRegs *regs) {
this->regs = regs;
}
public: public:
/* Temporary arena pool used while compiling and decompiling. */ /* Temporary arena pool used while compiling and decompiling. */
JSArenaPool tempPool; JSArenaPool tempPool;
@@ -1721,7 +1782,8 @@ struct JSContext
} }
/* Add the given callstack to the list as the new active callstack. */ /* Add the given callstack to the list as the new active callstack. */
void pushCallStackAndFrame(js::CallStack *newcs, JSStackFrame *newfp); void pushCallStackAndFrame(js::CallStack *newcs, JSStackFrame *newfp,
JSFrameRegs &regs);
/* Remove the active callstack and make the next callstack active. */ /* Remove the active callstack and make the next callstack active. */
void popCallStackAndFrame(); void popCallStackAndFrame();
@@ -1736,7 +1798,7 @@ struct JSContext
* Perform a linear search of all frames in all callstacks in the given context * 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. * for the given frame, returning the callstack, if found, and null otherwise.
*/ */
js::CallStack *containingCallStack(JSStackFrame *target); js::CallStack *containingCallStack(const JSStackFrame *target);
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
JSThread *thread; JSThread *thread;
@@ -1820,9 +1882,7 @@ struct JSContext
JSGenerator *generatorFor(JSStackFrame *fp) const; JSGenerator *generatorFor(JSStackFrame *fp) const;
/* Early OOM-check. */ /* Early OOM-check. */
bool ensureGeneratorStackSpace() { inline bool ensureGeneratorStackSpace();
return genStack.reserve(genStack.length() + 1);
}
bool enterGenerator(JSGenerator *gen) { bool enterGenerator(JSGenerator *gen) {
return genStack.append(gen); return genStack.append(gen);
@@ -1968,6 +2028,15 @@ struct JSContext
return JS_THREAD_DATA(this)->stackSpace; return JS_THREAD_DATA(this)->stackSpace;
} }
#ifdef DEBUG
void assertValidStackDepth(uintN depth) {
JS_ASSERT(0 <= regs->sp - StackBase(fp));
JS_ASSERT(depth <= uintptr_t(regs->sp - StackBase(fp)));
}
#else
void assertValidStackDepth(uintN /*depth*/) {}
#endif
private: private:
/* /*
@@ -1993,6 +2062,13 @@ JSStackFrame::varobj(JSContext *cx) const
return fun ? callobj : cx->activeCallStack()->getInitialVarObj(); return fun ? callobj : cx->activeCallStack()->getInitialVarObj();
} }
JS_ALWAYS_INLINE jsbytecode *
JSStackFrame::pc(JSContext *cx) const
{
JS_ASSERT(cx->containingCallStack(this) != NULL);
return cx->fp == this ? cx->regs->pc : savedPC;
}
/* /*
* InvokeArgsGuard is used outside the JS engine (where jscntxtinlines.h is * InvokeArgsGuard is used outside the JS engine (where jscntxtinlines.h is
* not included). To avoid visibility issues, force members inline. * not included). To avoid visibility issues, force members inline.
@@ -2031,7 +2107,11 @@ InvokeArgsGuard::~InvokeArgsGuard()
# define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0) # define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0)
#endif #endif
#ifdef __cplusplus static inline uintN
FramePCOffset(JSContext *cx, JSStackFrame* fp)
{
return uintN((fp->imacpc ? fp->imacpc : fp->pc(cx)) - fp->script->code);
}
static inline JSAtom ** static inline JSAtom **
FrameAtomBase(JSContext *cx, JSStackFrame *fp) FrameAtomBase(JSContext *cx, JSStackFrame *fp)
@@ -2435,8 +2515,6 @@ class JSAutoResolveFlags
JS_DECL_USE_GUARD_OBJECT_NOTIFIER JS_DECL_USE_GUARD_OBJECT_NOTIFIER
}; };
#endif /* __cpluscplus */
/* /*
* Slightly more readable macros for testing per-context option settings (also * Slightly more readable macros for testing per-context option settings (also
* to hide bitset implementation detail). * to hide bitset implementation detail).

View File

@@ -46,6 +46,15 @@
#include "jsobjinlines.h" #include "jsobjinlines.h"
inline bool
JSContext::ensureGeneratorStackSpace()
{
bool ok = genStack.reserve(genStack.length() + 1);
if (!ok)
js_ReportOutOfMemory(this);
return ok;
}
namespace js { namespace js {
JS_REQUIRES_STACK JS_ALWAYS_INLINE JSStackFrame * JS_REQUIRES_STACK JS_ALWAYS_INLINE JSStackFrame *
@@ -61,12 +70,12 @@ StackSpace::firstUnused() const
CallStack *ccs = currentCallStack; CallStack *ccs = currentCallStack;
if (!ccs) if (!ccs)
return base; return base;
if (!ccs->inContext()) if (JSContext *cx = ccs->maybeContext()) {
return ccs->getInitialArgEnd(); if (!ccs->isSuspended())
JSStackFrame *fp = ccs->getCurrentFrame(); return cx->regs->sp;
if (JSFrameRegs *regs = fp->regs) return ccs->getSuspendedRegs()->sp;
return regs->sp; }
return fp->slots(); return ccs->getInitialArgEnd();
} }
/* Inline so we don't need the friend API. */ /* Inline so we don't need the friend API. */
@@ -124,7 +133,7 @@ StackSpace::getInlineFrame(JSContext *cx, jsval *sp,
{ {
assertIsCurrent(cx); assertIsCurrent(cx);
JS_ASSERT(cx->hasActiveCallStack()); JS_ASSERT(cx->hasActiveCallStack());
JS_ASSERT(cx->fp->regs->sp == sp); JS_ASSERT(cx->regs->sp == sp);
ptrdiff_t nvals = nmissing + VALUES_PER_STACK_FRAME + nfixed; ptrdiff_t nvals = nmissing + VALUES_PER_STACK_FRAME + nfixed;
if (!ensureSpace(cx, sp, nvals)) if (!ensureSpace(cx, sp, nvals))
@@ -135,13 +144,18 @@ StackSpace::getInlineFrame(JSContext *cx, jsval *sp,
} }
JS_REQUIRES_STACK JS_ALWAYS_INLINE void JS_REQUIRES_STACK JS_ALWAYS_INLINE void
StackSpace::pushInlineFrame(JSContext *cx, JSStackFrame *fp, JSStackFrame *newfp) StackSpace::pushInlineFrame(JSContext *cx, JSStackFrame *fp, jsbytecode *pc,
JSStackFrame *newfp)
{ {
assertIsCurrent(cx); assertIsCurrent(cx);
JS_ASSERT(cx->hasActiveCallStack()); JS_ASSERT(cx->hasActiveCallStack());
JS_ASSERT(cx->fp == fp); JS_ASSERT(cx->fp == fp && cx->regs->pc == pc);
fp->savedPC = pc;
newfp->down = fp; newfp->down = fp;
#ifdef DEBUG
newfp->savedPC = JSStackFrame::sInvalidPC;
#endif
cx->setCurrentFrame(newfp); cx->setCurrentFrame(newfp);
} }
@@ -151,7 +165,14 @@ StackSpace::popInlineFrame(JSContext *cx, JSStackFrame *up, JSStackFrame *down)
assertIsCurrent(cx); assertIsCurrent(cx);
JS_ASSERT(cx->hasActiveCallStack()); JS_ASSERT(cx->hasActiveCallStack());
JS_ASSERT(cx->fp == up && up->down == down); JS_ASSERT(cx->fp == up && up->down == down);
JS_ASSERT(up->savedPC == JSStackFrame::sInvalidPC);
JSFrameRegs *regs = cx->regs;
regs->pc = down->savedPC;
regs->sp = up->argv - 1;
#ifdef DEBUG
down->savedPC = JSStackFrame::sInvalidPC;
#endif
cx->setCurrentFrame(down); cx->setCurrentFrame(down);
} }

View File

@@ -684,19 +684,16 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
PodZero(fp->slots(), nfixed); PodZero(fp->slots(), nfixed);
PodZero(fp); PodZero(fp);
fp->script = script; fp->script = script;
fp->regs = NULL;
fp->fun = fun; fp->fun = fun;
fp->argv = vp + 2; fp->argv = vp + 2;
fp->scopeChain = closure->getParent(); fp->scopeChain = closure->getParent();
if (script) {
JS_ASSERT(script->length >= JSOP_STOP_LENGTH); /* Initialize regs. */
regs.pc = script->code + script->length - JSOP_STOP_LENGTH; regs.pc = script ? script->code : NULL;
regs.sp = fp->slots() + script->nfixed; regs.sp = fp->slots() + nfixed;
fp->regs = &regs;
}
/* Officially push |fp|. |frame|'s destructor pops. */ /* Officially push |fp|. |frame|'s destructor pops. */
cx->stack().pushExecuteFrame(cx, frame, NULL); cx->stack().pushExecuteFrame(cx, frame, regs, NULL);
/* Now that fp has been pushed, get the call object. */ /* Now that fp has been pushed, get the call object. */
if (script && fun && fun->isHeavyweight() && if (script && fun && fun->isHeavyweight() &&
@@ -1070,7 +1067,7 @@ JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
JS_PUBLIC_API(jsbytecode *) JS_PUBLIC_API(jsbytecode *)
JS_GetFramePC(JSContext *cx, JSStackFrame *fp) JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
{ {
return fp->regs ? fp->regs->pc : NULL; return fp->pc(cx);
} }
JS_PUBLIC_API(JSStackFrame *) JS_PUBLIC_API(JSStackFrame *)

View File

@@ -79,7 +79,7 @@ jsdtrace_fun_linenumber(JSContext *cx, const JSFunction *fun)
static int static int
jsdtrace_frame_linenumber(JSContext *cx, JSStackFrame *fp) jsdtrace_frame_linenumber(JSContext *cx, JSStackFrame *fp)
{ {
if (fp && fp->regs) if (fp)
return (int) js_FramePCToLineNumber(cx, fp); return (int) js_FramePCToLineNumber(cx, fp);
return 0; return 0;

View File

@@ -338,7 +338,7 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
elem->filename = NULL; elem->filename = NULL;
if (fp->script) { if (fp->script) {
elem->filename = fp->script->filename; elem->filename = fp->script->filename;
if (fp->regs) if (fp->pc(cx))
elem->ulineno = js_FramePCToLineNumber(cx, fp); elem->ulineno = js_FramePCToLineNumber(cx, fp);
} }
++elem; ++elem;
@@ -753,7 +753,7 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
} else { } else {
if (!fp) if (!fp)
fp = js_GetScriptedCaller(cx, NULL); fp = js_GetScriptedCaller(cx, NULL);
lineno = (fp && fp->regs) ? js_FramePCToLineNumber(cx, fp) : 0; lineno = (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
} }
return (obj->getClass() != &js_ErrorClass) || return (obj->getClass() != &js_ErrorClass) ||

View File

@@ -2621,14 +2621,8 @@ js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags)
void void
js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
{ {
JSStackFrame *fp;
uintN error; uintN error;
const char *name, *source; const char *name = NULL, *source = NULL;
for (fp = js_GetTopStackFrame(cx); fp && !fp->regs; fp = fp->down)
continue;
name = source = NULL;
AutoValueRooter tvr(cx); AutoValueRooter tvr(cx);
if (flags & JSV2F_ITERATOR) { if (flags & JSV2F_ITERATOR) {
error = JSMSG_BAD_ITERATOR; error = JSMSG_BAD_ITERATOR;
@@ -2650,15 +2644,18 @@ js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
error = JSMSG_NOT_FUNCTION; error = JSMSG_NOT_FUNCTION;
} }
js_ReportValueError3(cx, error, LeaveTrace(cx);
(fp && fp->regs && FrameRegsIter i(cx);
StackBase(fp) <= vp && vp < fp->regs->sp) while (!i.done() && !i.pc())
? vp - fp->regs->sp ++i;
: (flags & JSV2F_SEARCH_STACK)
? JSDVG_SEARCH_STACK ptrdiff_t spindex =
: JSDVG_IGNORE_STACK, !i.done() && StackBase(i.fp()) <= vp && vp < i.sp()
*vp, NULL, ? vp - i.sp()
name, source); : flags & JSV2F_SEARCH_STACK ? JSDVG_SEARCH_STACK
: JSDVG_IGNORE_STACK;
js_ReportValueError3(cx, error, spindex, *vp, NULL, name, source);
} }
/* /*

View File

@@ -95,6 +95,10 @@ using namespace js;
/* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */ /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
#if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
#ifdef DEBUG
jsbytecode *const JSStackFrame::sInvalidPC = (jsbytecode *)0xbeef;
#endif
JSObject * JSObject *
js_GetScopeChain(JSContext *cx, JSStackFrame *fp) js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
{ {
@@ -559,6 +563,7 @@ js_Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
* Get a pointer to new frame/slots. This memory is not "claimed", so the * Get a pointer to new frame/slots. This memory is not "claimed", so the
* code before pushInvokeFrame must not reenter the interpreter. * code before pushInvokeFrame must not reenter the interpreter.
*/ */
JSFrameRegs regs;
InvokeFrameGuard frame; InvokeFrameGuard frame;
if (!cx->stack().getInvokeFrame(cx, args, nmissing, nfixed, frame)) if (!cx->stack().getInvokeFrame(cx, args, nmissing, nfixed, frame))
return false; return false;
@@ -583,13 +588,21 @@ js_Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
fp->annotation = NULL; fp->annotation = NULL;
fp->scopeChain = NULL; fp->scopeChain = NULL;
fp->blockChain = NULL; fp->blockChain = NULL;
fp->regs = NULL;
fp->imacpc = NULL; fp->imacpc = NULL;
fp->flags = flags; fp->flags = flags;
fp->displaySave = NULL; fp->displaySave = NULL;
/* Initialize regs. */
if (script) {
regs.pc = script->code;
regs.sp = fp->slots() + script->nfixed;
} else {
regs.pc = NULL;
regs.sp = fp->slots();
}
/* Officially push |fp|. |frame|'s destructor pops. */ /* Officially push |fp|. |frame|'s destructor pops. */
cx->stack().pushInvokeFrame(cx, args, frame); cx->stack().pushInvokeFrame(cx, args, frame, regs);
/* Now that the frame has been pushed, fix up the scope chain. */ /* Now that the frame has been pushed, fix up the scope chain. */
if (native) { if (native) {
@@ -722,6 +735,7 @@ js_Execute(JSContext *cx, JSObject *const chain, JSScript *script,
* N.B. when fp->argv is removed (bug 539144), argv will have to be copied * N.B. when fp->argv is removed (bug 539144), argv will have to be copied
* in before execution and copied out after. * in before execution and copied out after.
*/ */
JSFrameRegs regs;
ExecuteFrameGuard frame; ExecuteFrameGuard frame;
if (!cx->stack().getExecuteFrame(cx, down, 0, script->nslots, frame)) if (!cx->stack().getExecuteFrame(cx, down, 0, script->nslots, frame))
return false; return false;
@@ -797,11 +811,14 @@ js_Execute(JSContext *cx, JSObject *const chain, JSScript *script,
fp->script = script; fp->script = script;
fp->imacpc = NULL; fp->imacpc = NULL;
fp->rval = JSVAL_VOID; fp->rval = JSVAL_VOID;
fp->regs = NULL;
fp->blockChain = NULL; fp->blockChain = NULL;
/* Initialize regs. */
regs.pc = script->code;
regs.sp = StackBase(fp);
/* Officially push |fp|. |frame|'s destructor pops. */ /* Officially push |fp|. |frame|'s destructor pops. */
cx->stack().pushExecuteFrame(cx, frame, initialVarObj); cx->stack().pushExecuteFrame(cx, frame, regs, initialVarObj);
/* Now that the frame has been pushed, we can call the thisObject hook. */ /* Now that the frame has been pushed, we can call the thisObject hook. */
if (!down) { if (!down) {
@@ -1114,7 +1131,7 @@ js_EnterWith(JSContext *cx, jsint stackIndex)
JSObject *obj, *parent, *withobj; JSObject *obj, *parent, *withobj;
fp = cx->fp; fp = cx->fp;
sp = fp->regs->sp; sp = cx->regs->sp;
JS_ASSERT(stackIndex < 0); JS_ASSERT(stackIndex < 0);
JS_ASSERT(StackBase(fp) <= sp + stackIndex); JS_ASSERT(StackBase(fp) <= sp + stackIndex);
@@ -1175,16 +1192,16 @@ js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
* Unwind block and scope chains to match the given depth. The function sets * Unwind block and scope chains to match the given depth. The function sets
* fp->sp on return to stackDepth. * fp->sp on return to stackDepth.
*/ */
JS_REQUIRES_STACK JSBool JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool
js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth, js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind)
JSBool normalUnwind)
{ {
JSObject *obj; JSObject *obj;
JSClass *clasp; JSClass *clasp;
JS_ASSERT(stackDepth >= 0); JS_ASSERT(stackDepth >= 0);
JS_ASSERT(StackBase(fp) + stackDepth <= fp->regs->sp); JS_ASSERT(StackBase(cx->fp) + stackDepth <= cx->regs->sp);
JSStackFrame *fp = cx->fp;
for (obj = fp->blockChain; obj; obj = obj->getParent()) { for (obj = fp->blockChain; obj; obj = obj->getParent()) {
JS_ASSERT(obj->getClass() == &js_BlockClass); JS_ASSERT(obj->getClass() == &js_BlockClass);
if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth) if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
@@ -1205,7 +1222,7 @@ js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
} }
} }
fp->regs->sp = StackBase(fp) + stackDepth; cx->regs->sp = StackBase(fp) + stackDepth;
return normalUnwind; return normalUnwind;
} }
@@ -1274,7 +1291,7 @@ js_TraceOpcode(JSContext *cx)
tracefp = (FILE *) cx->tracefp; tracefp = (FILE *) cx->tracefp;
JS_ASSERT(tracefp); JS_ASSERT(tracefp);
fp = cx->fp; fp = cx->fp;
regs = fp->regs; regs = cx->regs;
/* /*
* Operations in prologues don't produce interesting values, and * Operations in prologues don't produce interesting values, and
@@ -2008,7 +2025,7 @@ js_Interpret(JSContext *cx)
uintN inlineCallCount; uintN inlineCallCount;
JSAtom **atoms; JSAtom **atoms;
JSVersion currentVersion, originalVersion; JSVersion currentVersion, originalVersion;
JSFrameRegs regs; JSFrameRegs regs, *prevContextRegs;
JSObject *obj, *obj2, *parent; JSObject *obj, *obj2, *parent;
JSBool ok, cond; JSBool ok, cond;
jsint len; jsint len;
@@ -2214,7 +2231,7 @@ js_Interpret(JSContext *cx)
script = fp->script; \ script = fp->script; \
atoms = FrameAtomBase(cx, fp); \ atoms = FrameAtomBase(cx, fp); \
currentVersion = (JSVersion) script->version; \ currentVersion = (JSVersion) script->version; \
JS_ASSERT(fp->regs == &regs); \ JS_ASSERT(cx->regs == &regs); \
JS_END_MACRO JS_END_MACRO
#define MONITOR_BRANCH(reason) \ #define MONITOR_BRANCH(reason) \
@@ -2312,21 +2329,18 @@ js_Interpret(JSContext *cx)
*/ */
CHECK_INTERRUPT_HANDLER(); CHECK_INTERRUPT_HANDLER();
#if !JS_HAS_GENERATORS /*
JS_ASSERT(!fp->regs); * Access to |cx->regs| is very common, so we copy in and repoint to a
#else * local variable, and copy out on exit.
/* Initialize the pc and sp registers unless we're resuming a generator. */ */
if (JS_LIKELY(!fp->regs)) { JS_ASSERT(cx->regs);
#endif prevContextRegs = cx->regs;
ASSERT_NOT_THROWING(cx); regs = *cx->regs;
regs.pc = script->code; cx->setCurrentRegs(&regs);
regs.sp = StackBase(fp);
fp->regs = &regs;
#if JS_HAS_GENERATORS #if JS_HAS_GENERATORS
} else { if (JS_UNLIKELY(fp->isGenerator())) {
JS_ASSERT(fp->regs == &cx->generatorFor(fp)->savedRegs); JS_ASSERT(prevContextRegs == &cx->generatorFor(fp)->savedRegs);
regs = *fp->regs;
fp->regs = &regs;
JS_ASSERT((size_t) (regs.pc - script->code) <= script->length); JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script)); JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script));
@@ -2344,13 +2358,10 @@ js_Interpret(JSContext *cx)
goto error; goto error;
} }
} }
#endif /* JS_HAS_GENERATORS */ #endif
#ifdef JS_TRACER #ifdef JS_TRACER
/* /* We cannot reenter the interpreter while recording. */
* We cannot reenter the interpreter while recording; wait to abort until
* after cx->fp->regs is set.
*/
if (TRACE_RECORDER(cx)) if (TRACE_RECORDER(cx))
AbortRecording(cx, "attempt to reenter interpreter while recording"); AbortRecording(cx, "attempt to reenter interpreter while recording");
#endif #endif
@@ -2415,6 +2426,7 @@ js_Interpret(JSContext *cx)
#endif /* !JS_THREADED_INTERP */ #endif /* !JS_THREADED_INTERP */
error: error:
JS_ASSERT(cx->regs == &regs);
#ifdef JS_TRACER #ifdef JS_TRACER
if (fp->imacpc && cx->throwing) { if (fp->imacpc && cx->throwing) {
// Handle other exceptions as if they came from the imacro-calling pc. // Handle other exceptions as if they came from the imacro-calling pc.
@@ -2507,8 +2519,8 @@ js_Interpret(JSContext *cx)
*/ */
regs.pc = (script)->main + tn->start + tn->length; regs.pc = (script)->main + tn->start + tn->length;
ok = js_UnwindScope(cx, fp, tn->stackDepth, JS_TRUE); ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
JS_ASSERT(fp->regs->sp == StackBase(fp) + tn->stackDepth); JS_ASSERT(regs.sp == StackBase(fp) + tn->stackDepth);
if (!ok) { if (!ok) {
/* /*
* Restart the handler search with updated pc and stack depth * Restart the handler search with updated pc and stack depth
@@ -2578,13 +2590,13 @@ js_Interpret(JSContext *cx)
forced_return: forced_return:
/* /*
* Unwind the scope making sure that ok stays false even when UnwindScope * Unwind the scope making sure that ok stays false even when js_UnwindScope
* returns true. * returns true.
* *
* When a trap handler returns JSTRAP_RETURN, we jump here with ok set to * When a trap handler returns JSTRAP_RETURN, we jump here with ok set to
* true bypassing any finally blocks. * true bypassing any finally blocks.
*/ */
ok &= js_UnwindScope(cx, fp, 0, ok || cx->throwing); ok &= js_UnwindScope(cx, 0, ok || cx->throwing);
JS_ASSERT(regs.sp == StackBase(fp)); JS_ASSERT(regs.sp == StackBase(fp));
#ifdef DEBUG #ifdef DEBUG
@@ -2607,21 +2619,17 @@ js_Interpret(JSContext *cx)
* frame pc. * frame pc.
*/ */
JS_ASSERT(inlineCallCount == 0); JS_ASSERT(inlineCallCount == 0);
JS_ASSERT(fp->regs == &regs); JS_ASSERT(cx->regs == &regs);
*prevContextRegs = regs;
cx->setCurrentRegs(prevContextRegs);
#ifdef JS_TRACER #ifdef JS_TRACER
if (TRACE_RECORDER(cx)) if (TRACE_RECORDER(cx))
AbortRecording(cx, "recording out of js_Interpret"); AbortRecording(cx, "recording out of js_Interpret");
#endif #endif
#if JS_HAS_GENERATORS
if (JS_UNLIKELY(fp->isGenerator())) { JS_ASSERT_IF(!fp->isGenerator(), !fp->blockChain);
cx->generatorFor(fp)->savedRegs = regs; JS_ASSERT_IF(!fp->isGenerator(), !js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
} else
#endif /* JS_HAS_GENERATORS */
{
JS_ASSERT(!fp->blockChain);
JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
}
fp->regs = NULL;
/* Undo the remaining effects committed on entry to js_Interpret. */ /* Undo the remaining effects committed on entry to js_Interpret. */
if (script->staticLevel < JS_DISPLAY_SIZE) if (script->staticLevel < JS_DISPLAY_SIZE)

View File

@@ -80,13 +80,10 @@ enum JSFrameFlags {
* function. * function.
* *
* NB: This struct is manually initialized in jsinterp.c and jsiter.c. If you * NB: This struct is manually initialized in jsinterp.c and jsiter.c. If you
* add new members, update both files. But first, try to remove members. The * add new members, update both files.
* sharp* and xml* members should be moved onto the stack as local variables
* with well-known slots, if possible.
*/ */
struct JSStackFrame struct JSStackFrame
{ {
JSFrameRegs *regs;
jsbytecode *imacpc; /* null or interpreter macro call pc */ jsbytecode *imacpc; /* null or interpreter macro call pc */
JSObject *callobj; /* lazily created Call object */ JSObject *callobj; /* lazily created Call object */
jsval argsobj; /* lazily created arguments object, must be jsval argsobj; /* lazily created arguments object, must be
@@ -102,6 +99,10 @@ struct JSStackFrame
/* Maintained by StackSpace operations */ /* Maintained by StackSpace operations */
JSStackFrame *down; /* previous frame, part of JSStackFrame *down; /* previous frame, part of
stack layout invariant */ stack layout invariant */
jsbytecode *savedPC; /* only valid if cx->fp != this */
#ifdef DEBUG
static jsbytecode *const sInvalidPC;
#endif
/* /*
* We can't determine in advance which local variables can live on * We can't determine in advance which local variables can live on
@@ -150,12 +151,9 @@ struct JSStackFrame
script->staticLevel */ script->staticLevel */
/* Members only needed for inline calls. */ /* Members only needed for inline calls. */
JSFrameRegs callerRegs; /* caller's regs for inline call */
void *hookData; /* debugger call hook data */ void *hookData; /* debugger call hook data */
JSVersion callerVersion; /* dynamic version of calling script */ JSVersion callerVersion; /* dynamic version of calling script */
inline void assertValidStackDepth(uintN depth);
void putActivationObjects(JSContext *cx) { void putActivationObjects(JSContext *cx) {
/* /*
* The order of calls here is important as js_PutCallObject needs to * The order of calls here is important as js_PutCallObject needs to
@@ -169,6 +167,9 @@ struct JSStackFrame
} }
} }
/* Get the frame's current bytecode, assuming |this| is in |cx|. */
jsbytecode *pc(JSContext *cx) const;
jsval *argEnd() const { jsval *argEnd() const {
return (jsval *)this; return (jsval *)this;
} }
@@ -220,32 +221,12 @@ JS_STATIC_ASSERT(sizeof(JSStackFrame) % sizeof(jsval) == 0);
} }
#ifdef __cplusplus
static JS_INLINE uintN
FramePCOffset(JSStackFrame* fp)
{
return uintN((fp->imacpc ? fp->imacpc : fp->regs->pc) - fp->script->code);
}
#endif
static JS_INLINE jsval * static JS_INLINE jsval *
StackBase(JSStackFrame *fp) StackBase(JSStackFrame *fp)
{ {
return fp->slots() + fp->script->nfixed; return fp->slots() + fp->script->nfixed;
} }
#ifdef DEBUG
void
JSStackFrame::assertValidStackDepth(uintN depth)
{
JS_ASSERT(0 <= regs->sp - StackBase(this));
JS_ASSERT(depth <= uintptr_t(regs->sp - StackBase(this)));
}
#else
void
JSStackFrame::assertValidStackDepth(uintN /*depth*/){}
#endif
static JS_INLINE uintN static JS_INLINE uintN
GlobalVarCount(JSStackFrame *fp) GlobalVarCount(JSStackFrame *fp)
{ {
@@ -433,8 +414,7 @@ js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth);
* fp->sp on return to stackDepth. * fp->sp on return to stackDepth.
*/ */
extern JS_REQUIRES_STACK JSBool extern JS_REQUIRES_STACK JSBool
js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth, js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind);
JSBool normalUnwind);
extern JSBool extern JSBool
js_OnUnknownMethod(JSContext *cx, jsval *vp); js_OnUnknownMethod(JSContext *cx, jsval *vp);

View File

@@ -74,6 +74,7 @@
#include "jsxml.h" #include "jsxml.h"
#endif #endif
#include "jscntxtinlines.h"
#include "jsobjinlines.h" #include "jsobjinlines.h"
#include "jsstrinlines.h" #include "jsstrinlines.h"
@@ -794,14 +795,13 @@ js_NewGenerator(JSContext *cx)
/* Initialize JSGenerator. */ /* Initialize JSGenerator. */
gen->obj = obj; gen->obj = obj;
gen->state = JSGEN_NEWBORN; gen->state = JSGEN_NEWBORN;
gen->savedRegs.pc = fp->regs->pc; gen->savedRegs.pc = cx->regs->pc;
JS_ASSERT(fp->regs->sp == fp->slots() + fp->script->nfixed); JS_ASSERT(cx->regs->sp == fp->slots() + fp->script->nfixed);
gen->savedRegs.sp = slots + fp->script->nfixed; gen->savedRegs.sp = slots + fp->script->nfixed;
gen->vplen = vplen; gen->vplen = vplen;
gen->liveFrame = newfp; gen->liveFrame = newfp;
/* Copy generator's stack frame copy in from |cx->fp|. */ /* Copy generator's stack frame copy in from |cx->fp|. */
newfp->regs = &gen->savedRegs;
newfp->imacpc = NULL; newfp->imacpc = NULL;
newfp->callobj = fp->callobj; newfp->callobj = fp->callobj;
if (fp->callobj) { /* Steal call object. */ if (fp->callobj) { /* Steal call object. */
@@ -922,7 +922,6 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
memcpy(vp, genVp, usedBefore * sizeof(jsval)); memcpy(vp, genVp, usedBefore * sizeof(jsval));
fp->flags &= ~JSFRAME_FLOATING_GENERATOR; fp->flags &= ~JSFRAME_FLOATING_GENERATOR;
fp->argv = vp + 2; fp->argv = vp + 2;
fp->regs = &gen->savedRegs;
gen->savedRegs.sp = fp->slots() + (gen->savedRegs.sp - genfp->slots()); gen->savedRegs.sp = fp->slots() + (gen->savedRegs.sp - genfp->slots());
JS_ASSERT(uintN(gen->savedRegs.sp - fp->slots()) <= fp->script->nslots); JS_ASSERT(uintN(gen->savedRegs.sp - fp->slots()) <= fp->script->nslots);
@@ -945,7 +944,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
(void)cx->enterGenerator(gen); /* OOM check above. */ (void)cx->enterGenerator(gen); /* OOM check above. */
/* Officially push |fp|. |frame|'s destructor pops. */ /* Officially push |fp|. |frame|'s destructor pops. */
cx->stack().pushExecuteFrame(cx, frame, NULL); cx->stack().pushExecuteFrame(cx, frame, gen->savedRegs, NULL);
ok = js_Interpret(cx); ok = js_Interpret(cx);

View File

@@ -1023,9 +1023,10 @@ js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
return principals->codebase; return principals->codebase;
} }
if (caller->regs && js_GetOpcode(cx, caller->script, caller->regs->pc) == JSOP_EVAL) { jsbytecode *pc = caller->pc(cx);
JS_ASSERT(js_GetOpcode(cx, caller->script, caller->regs->pc + JSOP_EVAL_LENGTH) == JSOP_LINENO); if (pc && js_GetOpcode(cx, caller->script, pc) == JSOP_EVAL) {
*linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH); JS_ASSERT(js_GetOpcode(cx, caller->script, pc + JSOP_EVAL_LENGTH) == JSOP_LINENO);
*linenop = GET_UINT16(pc + JSOP_EVAL_LENGTH);
} else { } else {
*linenop = js_FramePCToLineNumber(cx, caller); *linenop = js_FramePCToLineNumber(cx, caller);
} }
@@ -1069,7 +1070,8 @@ obj_eval(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE; return JS_FALSE;
} }
bool indirectCall = (caller->regs && *caller->regs->pc != JSOP_EVAL); jsbytecode *callerPC = caller->pc(cx);
bool indirectCall = (callerPC && *callerPC != JSOP_EVAL);
/* /*
* This call to js_GetWrappedObject is safe because of the security checks * This call to js_GetWrappedObject is safe because of the security checks
@@ -2795,16 +2797,14 @@ js_InferFlags(JSContext *cx, uintN defaultFlags)
JS_ASSERT_NOT_ON_TRACE(cx); JS_ASSERT_NOT_ON_TRACE(cx);
JSStackFrame *fp;
jsbytecode *pc; jsbytecode *pc;
const JSCodeSpec *cs; const JSCodeSpec *cs;
uint32 format; uint32 format;
uintN flags = 0; uintN flags = 0;
fp = js_GetTopStackFrame(cx); JSStackFrame *const fp = js_GetTopStackFrame(cx);
if (!fp || !fp->regs) if (!fp || !(pc = cx->regs->pc))
return defaultFlags; return defaultFlags;
pc = fp->regs->pc;
cs = &js_CodeSpec[js_GetOpcode(cx, fp->script, pc)]; cs = &js_CodeSpec[js_GetOpcode(cx, fp->script, pc)];
format = cs->format; format = cs->format;
if (JOF_MODE(format) != JOF_NAME) if (JOF_MODE(format) != JOF_NAME)
@@ -3010,15 +3010,11 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
JS_REQUIRES_STACK JSBool JS_REQUIRES_STACK JSBool
js_PutBlockObject(JSContext *cx, JSBool normalUnwind) js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
{ {
JSStackFrame *fp;
JSObject *obj;
uintN depth, count;
/* Blocks have one fixed slot available for the first local.*/ /* Blocks have one fixed slot available for the first local.*/
JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2); JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2);
fp = cx->fp; JSStackFrame *const fp = cx->fp;
obj = fp->scopeChain; JSObject *obj = fp->scopeChain;
JS_ASSERT(obj->getClass() == &js_BlockClass); JS_ASSERT(obj->getClass() == &js_BlockClass);
JS_ASSERT(obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp)); JS_ASSERT(obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp));
JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
@@ -3035,10 +3031,10 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
JS_ASSERT(obj->numSlots() == JS_INITIAL_NSLOTS); JS_ASSERT(obj->numSlots() == JS_INITIAL_NSLOTS);
/* The block and its locals must be on the current stack for GC safety. */ /* The block and its locals must be on the current stack for GC safety. */
depth = OBJ_BLOCK_DEPTH(cx, obj); uintN depth = OBJ_BLOCK_DEPTH(cx, obj);
count = OBJ_BLOCK_COUNT(cx, obj); uintN count = OBJ_BLOCK_COUNT(cx, obj);
JS_ASSERT(depth <= (size_t) (fp->regs->sp - StackBase(fp))); JS_ASSERT(depth <= (size_t) (cx->regs->sp - StackBase(fp)));
JS_ASSERT(count <= (size_t) (fp->regs->sp - StackBase(fp) - depth)); JS_ASSERT(count <= (size_t) (cx->regs->sp - StackBase(fp) - depth));
/* See comments in CheckDestructuring from jsparse.cpp. */ /* See comments in CheckDestructuring from jsparse.cpp. */
JS_ASSERT(count >= 1); JS_ASSERT(count >= 1);
@@ -4776,7 +4772,7 @@ js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, jsval *vp)
JS_FRIEND_API(bool) JS_FRIEND_API(bool)
js_CheckUndeclaredVarAssignment(JSContext *cx, jsval propname) js_CheckUndeclaredVarAssignment(JSContext *cx, jsval propname)
{ {
JSStackFrame *fp = js_GetTopStackFrame(cx); JSStackFrame *const fp = js_GetTopStackFrame(cx);
if (!fp) if (!fp)
return true; return true;
@@ -6418,32 +6414,42 @@ MaybeDumpValue(const char *name, jsval v)
} }
JS_FRIEND_API(void) JS_FRIEND_API(void)
js_DumpStackFrame(JSStackFrame *fp) js_DumpStackFrame(JSContext *cx, JSStackFrame *start)
{ {
jsval *sp = NULL; /* This should only called during live debugging. */
VOUCH_DOES_NOT_REQUIRE_STACK();
if (!start)
start = cx->fp;
FrameRegsIter i(cx);
while (!i.done() && i.fp() != start)
++i;
if (i.done()) {
fprintf(stderr, "fp = %p not found in cx = %p\n", (void *)start, (void *)cx);
return;
}
for (; !i.done(); ++i) {
JSStackFrame *const fp = i.fp();
for (; fp; fp = fp->down) {
fprintf(stderr, "JSStackFrame at %p\n", (void *) fp); fprintf(stderr, "JSStackFrame at %p\n", (void *) fp);
if (fp->argv) if (fp->argv) {
fprintf(stderr, "callee: ");
dumpValue(fp->argv[-2]); dumpValue(fp->argv[-2]);
else } else {
fprintf(stderr, "global frame, no callee"); fprintf(stderr, "global frame, no callee");
}
fputc('\n', stderr); fputc('\n', stderr);
if (fp->script) if (fp->script)
fprintf(stderr, "file %s line %u\n", fp->script->filename, (unsigned) fp->script->lineno); fprintf(stderr, "file %s line %u\n", fp->script->filename, (unsigned) fp->script->lineno);
if (fp->regs) { if (jsbytecode *pc = i.pc()) {
if (!fp->regs->pc) {
fprintf(stderr, "*** regs && !regs->pc, skipping frame\n\n");
continue;
}
if (!fp->script) { if (!fp->script) {
fprintf(stderr, "*** regs && !script, skipping frame\n\n"); fprintf(stderr, "*** pc && !script, skipping frame\n\n");
continue; continue;
} }
jsbytecode *pc = fp->regs->pc;
sp = fp->regs->sp;
if (fp->imacpc) { if (fp->imacpc) {
fprintf(stderr, " pc in imacro at %p\n called from ", pc); fprintf(stderr, " pc in imacro at %p\n called from ", pc);
pc = fp->imacpc; pc = fp->imacpc;
@@ -6453,19 +6459,15 @@ js_DumpStackFrame(JSStackFrame *fp)
fprintf(stderr, "pc = %p\n", pc); fprintf(stderr, "pc = %p\n", pc);
fprintf(stderr, " current op: %s\n", js_CodeName[*pc]); fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
} }
if (sp && fp->slots()) { jsval *sp = i.sp();
fprintf(stderr, " slots: %p\n", (void *) fp->slots()); fprintf(stderr, " slots: %p\n", (void *) fp->slots());
fprintf(stderr, " sp: %p = slots + %u\n", (void *) sp, (unsigned) (sp - fp->slots())); fprintf(stderr, " sp: %p = slots + %u\n", (void *) sp, (unsigned) (sp - fp->slots()));
if (sp - fp->slots() < 10000) { // sanity if (sp - fp->slots() < 10000) { // sanity
for (jsval *p = fp->slots(); p < sp; p++) { for (jsval *p = fp->slots(); p < sp; p++) {
fprintf(stderr, " %p: ", (void *) p); fprintf(stderr, " %p: ", (void *) p);
dumpValue(*p); dumpValue(*p);
fputc('\n', stderr); fputc('\n', stderr);
}
} }
} else {
fprintf(stderr, " sp: %p\n", (void *) sp);
fprintf(stderr, " slots: %p\n", (void *) fp->slots());
} }
fprintf(stderr, " argv: %p (argc: %u)\n", (void *) fp->argv, (unsigned) fp->argc); fprintf(stderr, " argv: %p (argc: %u)\n", (void *) fp->argv, (unsigned) fp->argc);
MaybeDumpObject("callobj", fp->callobj); MaybeDumpObject("callobj", fp->callobj);

View File

@@ -1294,7 +1294,7 @@ JS_FRIEND_API(void) js_DumpAtom(JSAtom *atom);
JS_FRIEND_API(void) js_DumpValue(jsval val); JS_FRIEND_API(void) js_DumpValue(jsval val);
JS_FRIEND_API(void) js_DumpId(jsid id); JS_FRIEND_API(void) js_DumpId(jsid id);
JS_FRIEND_API(void) js_DumpObject(JSObject *obj); JS_FRIEND_API(void) js_DumpObject(JSObject *obj);
JS_FRIEND_API(void) js_DumpStackFrame(JSStackFrame *fp); JS_FRIEND_API(void) js_DumpStackFrameChain(JSContext *cx, JSStackFrame *start = NULL);
#endif #endif
extern uintN extern uintN

View File

@@ -1993,7 +1993,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
*/ */
fp = js_GetScriptedCaller(cx, NULL); fp = js_GetScriptedCaller(cx, NULL);
format = cs->format; format = cs->format;
if (((fp && fp->regs && pc == fp->regs->pc) || if (((fp && pc == fp->pc(cx)) ||
(pc == startpc && nuses != 0)) && (pc == startpc && nuses != 0)) &&
format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) { format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) {
mode = JOF_MODE(format); mode = JOF_MODE(format);
@@ -5102,26 +5102,25 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
JSStackFrame *fp; JSStackFrame *fp;
jsbytecode *pc; jsbytecode *pc;
JSScript *script; JSScript *script;
JSFrameRegs *regs;
intN pcdepth;
jsval *sp, *stackBase;
char *name;
JS_ASSERT(spindex < 0 || JS_ASSERT(spindex < 0 ||
spindex == JSDVG_IGNORE_STACK || spindex == JSDVG_IGNORE_STACK ||
spindex == JSDVG_SEARCH_STACK); spindex == JSDVG_SEARCH_STACK);
fp = js_GetScriptedCaller(cx, NULL); LeaveTrace(cx);
if (!fp || !fp->regs || !fp->regs->sp)
/* Get scripted caller */
FrameRegsIter i(cx);
while (!i.done() && !i.fp()->script)
++i;
if (i.done() || !i.pc())
goto do_fallback; goto do_fallback;
fp = i.fp();
script = fp->script; script = fp->script;
regs = fp->regs; pc = fp->imacpc ? fp->imacpc : i.pc();
pc = fp->imacpc ? fp->imacpc : regs->pc; JS_ASSERT(pc >= script->main && pc < script->code + script->length);
if (pc < script->main || script->code + script->length <= pc) {
JS_NOT_REACHED("bug");
goto do_fallback;
}
if (spindex != JSDVG_IGNORE_STACK) { if (spindex != JSDVG_IGNORE_STACK) {
jsbytecode **pcstack; jsbytecode **pcstack;
@@ -5134,7 +5133,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
cx->malloc(StackDepth(script) * sizeof *pcstack); cx->malloc(StackDepth(script) * sizeof *pcstack);
if (!pcstack) if (!pcstack)
return NULL; return NULL;
pcdepth = ReconstructPCStack(cx, script, pc, pcstack); intN pcdepth = ReconstructPCStack(cx, script, pc, pcstack);
if (pcdepth < 0) if (pcdepth < 0)
goto release_pcstack; goto release_pcstack;
@@ -5150,8 +5149,8 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
* calculated value matching v under assumption that it is * calculated value matching v under assumption that it is
* it that caused exception, see bug 328664. * it that caused exception, see bug 328664.
*/ */
stackBase = StackBase(fp); jsval *stackBase = StackBase(fp);
sp = regs->sp; jsval *sp = i.sp();
do { do {
if (sp == stackBase) { if (sp == stackBase) {
pcdepth = -1; pcdepth = -1;
@@ -5178,10 +5177,13 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
} }
{ {
jsbytecode* savepc = regs->pc; jsbytecode* savepc = i.pc();
jsbytecode* imacpc = fp->imacpc; jsbytecode* imacpc = fp->imacpc;
if (imacpc) { if (imacpc) {
regs->pc = imacpc; if (fp == cx->fp)
cx->regs->pc = imacpc;
else
fp->savedPC = imacpc;
fp->imacpc = NULL; fp->imacpc = NULL;
} }
@@ -5189,18 +5191,23 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
* FIXME: bug 489843. Stack reconstruction may have returned a pc * FIXME: bug 489843. Stack reconstruction may have returned a pc
* value *inside* an imacro; this would confuse the decompiler. * value *inside* an imacro; this would confuse the decompiler.
*/ */
char *name;
if (imacpc && size_t(pc - script->code) >= script->length) if (imacpc && size_t(pc - script->code) >= script->length)
name = FAILED_EXPRESSION_DECOMPILER; name = FAILED_EXPRESSION_DECOMPILER;
else else
name = DecompileExpression(cx, script, fp->fun, pc); name = DecompileExpression(cx, script, fp->fun, pc);
if (imacpc) { if (imacpc) {
regs->pc = savepc; if (fp == cx->fp)
cx->regs->pc = imacpc;
else
fp->savedPC = savepc;
fp->imacpc = imacpc; fp->imacpc = imacpc;
} }
if (name != FAILED_EXPRESSION_DECOMPILER)
return name;
} }
if (name != FAILED_EXPRESSION_DECOMPILER)
return name;
do_fallback: do_fallback:
if (!fallback) { if (!fallback) {

View File

@@ -224,7 +224,6 @@ BEGIN_CASE(JSOP_STOP)
{ {
JS_ASSERT(!fp->blockChain); JS_ASSERT(!fp->blockChain);
JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0)); JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
JS_ASSERT(fp->down->regs == &fp->callerRegs);
if (JS_LIKELY(script->staticLevel < JS_DISPLAY_SIZE)) if (JS_LIKELY(script->staticLevel < JS_DISPLAY_SIZE))
cx->display[script->staticLevel] = fp->displaySave; cx->display[script->staticLevel] = fp->displaySave;
@@ -277,15 +276,12 @@ BEGIN_CASE(JSOP_STOP)
JSStackFrame *down = fp->down; JSStackFrame *down = fp->down;
bool recursive = fp->script == down->script; bool recursive = fp->script == down->script;
/* Restore caller's registers. */ /* Pop the frame. */
regs = fp->callerRegs;
regs.sp -= 1 + (size_t) fp->argc;
regs.sp[-1] = fp->rval;
down->regs = &regs;
/* Pop |fp| from the context. */
cx->stack().popInlineFrame(cx, fp, down); cx->stack().popInlineFrame(cx, fp, down);
/* Propagate return value before fp is lost. */
regs.sp[-1] = fp->rval;
/* Sync interpreter registers. */ /* Sync interpreter registers. */
fp = cx->fp; fp = cx->fp;
script = fp->script; script = fp->script;
@@ -2071,7 +2067,6 @@ BEGIN_CASE(JSOP_APPLY)
} }
JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags)); JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
newfp->thisv = vp[1]; newfp->thisv = vp[1];
newfp->regs = NULL;
newfp->imacpc = NULL; newfp->imacpc = NULL;
/* Push void to initialize local variables. */ /* Push void to initialize local variables. */
@@ -2091,16 +2086,15 @@ BEGIN_CASE(JSOP_APPLY)
js_SetVersion(cx, currentVersion); js_SetVersion(cx, currentVersion);
} }
/* Push the frame and set interpreter registers. */ /* Push the frame. */
newfp->callerRegs = regs; stack.pushInlineFrame(cx, fp, regs.pc, newfp);
fp->regs = &newfp->callerRegs;
regs.sp = newsp; /* Initializer regs after pushInlineFrame snapshots pc. */
regs.pc = newscript->code; regs.pc = newscript->code;
newfp->regs = &regs; regs.sp = newsp;
stack.pushInlineFrame(cx, fp, newfp);
JS_ASSERT(newfp == cx->fp);
/* Import into locals. */ /* Import into locals. */
JS_ASSERT(newfp == cx->fp);
fp = newfp; fp = newfp;
script = newscript; script = newscript;
atoms = script->atomMap.vector; atoms = script->atomMap.vector;
@@ -2132,8 +2126,8 @@ BEGIN_CASE(JSOP_APPLY)
goto error; goto error;
} }
} else if (fp->script == fp->down->script && } else if (fp->script == fp->down->script &&
*fp->down->regs->pc == JSOP_CALL && *fp->down->savedPC == JSOP_CALL &&
*fp->regs->pc == JSOP_TRACE) { *regs.pc == JSOP_TRACE) {
MONITOR_BRANCH(Record_EnterFrame); MONITOR_BRANCH(Record_EnterFrame);
} }
#endif #endif
@@ -3195,7 +3189,7 @@ END_CASE(JSOP_HOLE)
BEGIN_CASE(JSOP_NEWARRAY) BEGIN_CASE(JSOP_NEWARRAY)
len = GET_UINT16(regs.pc); len = GET_UINT16(regs.pc);
cx->fp->assertValidStackDepth(len); cx->assertValidStackDepth(len);
obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE); obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE);
if (!obj) if (!obj)
goto error; goto error;

View File

@@ -127,7 +127,7 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoI
* Optimize the cached vword based on our parameters and the current pc's * Optimize the cached vword based on our parameters and the current pc's
* opcode format flags. * opcode format flags.
*/ */
pc = cx->fp->regs->pc; pc = cx->regs->pc;
op = js_GetOpcode(cx, cx->fp->script, pc); op = js_GetOpcode(cx, cx->fp->script, pc);
cs = &js_CodeSpec[op]; cs = &js_CodeSpec[op];
kshape = 0; kshape = 0;

View File

@@ -163,8 +163,8 @@ TraceRecorder::downSnapshot(FrameInfo* downFrame)
exitTypeMap[i] = typeMap[i]; exitTypeMap[i] = typeMap[i];
/* Add the return type. */ /* Add the return type. */
JS_ASSERT_IF(*cx->fp->regs->pc != JSOP_RETURN, *cx->fp->regs->pc == JSOP_STOP); JS_ASSERT_IF(*cx->regs->pc != JSOP_RETURN, *cx->regs->pc == JSOP_STOP);
if (*cx->fp->regs->pc == JSOP_RETURN) if (*cx->regs->pc == JSOP_RETURN)
exitTypeMap[downPostSlots] = determineSlotType(&stackval(-1)); exitTypeMap[downPostSlots] = determineSlotType(&stackval(-1));
else else
exitTypeMap[downPostSlots] = TT_VOID; exitTypeMap[downPostSlots] = TT_VOID;
@@ -198,12 +198,21 @@ TraceRecorder::downSnapshot(FrameInfo* downFrame)
return exit; return exit;
} }
static JS_REQUIRES_STACK jsval *
DownFrameSP(JSContext *cx)
{
FrameRegsIter i(cx);
++i;
JS_ASSERT(i.fp() == cx->fp->down);
return i.sp();
}
JS_REQUIRES_STACK AbortableRecordingStatus JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::upRecursion() TraceRecorder::upRecursion()
{ {
JS_ASSERT((JSOp)*cx->fp->down->regs->pc == JSOP_CALL); JS_ASSERT((JSOp)*cx->fp->down->savedPC == JSOP_CALL);
JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, cx->fp->down->script, JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, cx->fp->down->script,
cx->fp->down->regs->pc)].length == JSOP_CALL_LENGTH); cx->fp->down->savedPC)].length == JSOP_CALL_LENGTH);
JS_ASSERT(callDepth == 0); JS_ASSERT(callDepth == 0);
@@ -215,10 +224,10 @@ TraceRecorder::upRecursion()
if (anchor && (anchor->exitType == RECURSIVE_EMPTY_RP_EXIT || if (anchor && (anchor->exitType == RECURSIVE_EMPTY_RP_EXIT ||
anchor->exitType == RECURSIVE_SLURP_MISMATCH_EXIT || anchor->exitType == RECURSIVE_SLURP_MISMATCH_EXIT ||
anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT)) { anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT)) {
return slurpDownFrames(cx->fp->down->regs->pc); return slurpDownFrames(cx->fp->down->savedPC);
} }
jsbytecode* return_pc = cx->fp->down->regs->pc; jsbytecode* return_pc = cx->fp->down->savedPC;
jsbytecode* recursive_pc = return_pc + JSOP_CALL_LENGTH; jsbytecode* recursive_pc = return_pc + JSOP_CALL_LENGTH;
/* /*
@@ -245,7 +254,7 @@ TraceRecorder::upRecursion()
* Need to compute this from the down frame, since the stack could have * Need to compute this from the down frame, since the stack could have
* moved on this one. * moved on this one.
*/ */
fi->spdist = cx->fp->down->regs->sp - cx->fp->down->slots(); fi->spdist = DownFrameSP(cx) - cx->fp->down->slots();
JS_ASSERT(cx->fp->argc == cx->fp->down->argc); JS_ASSERT(cx->fp->argc == cx->fp->down->argc);
fi->set_argc(uint16(cx->fp->argc), false); fi->set_argc(uint16(cx->fp->argc), false);
fi->callerHeight = downPostSlots; fi->callerHeight = downPostSlots;
@@ -308,7 +317,7 @@ TraceRecorder::upRecursion()
exit = downSnapshot(fi); exit = downSnapshot(fi);
LIns* rval_ins; LIns* rval_ins;
if (*cx->fp->regs->pc == JSOP_RETURN) { if (*cx->regs->pc == JSOP_RETURN) {
JS_ASSERT(!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT); JS_ASSERT(!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT);
rval_ins = get(&stackval(-1)); rval_ins = get(&stackval(-1));
JS_ASSERT(rval_ins); JS_ASSERT(rval_ins);
@@ -318,7 +327,7 @@ TraceRecorder::upRecursion()
TraceType returnType = exit->stackTypeMap()[downPostSlots]; TraceType returnType = exit->stackTypeMap()[downPostSlots];
if (returnType == TT_INT32) { if (returnType == TT_INT32) {
JS_ASSERT(*cx->fp->regs->pc == JSOP_RETURN); JS_ASSERT(*cx->regs->pc == JSOP_RETURN);
JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32); JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32);
JS_ASSERT(isPromoteInt(rval_ins)); JS_ASSERT(isPromoteInt(rval_ins));
rval_ins = demote(lir, rval_ins); rval_ins = demote(lir, rval_ins);
@@ -327,7 +336,7 @@ TraceRecorder::upRecursion()
UpRecursiveSlotMap slotMap(*this, downPostSlots, rval_ins); UpRecursiveSlotMap slotMap(*this, downPostSlots, rval_ins);
for (unsigned i = 0; i < downPostSlots; i++) for (unsigned i = 0; i < downPostSlots; i++)
slotMap.addSlot(exit->stackType(i)); slotMap.addSlot(exit->stackType(i));
if (*cx->fp->regs->pc == JSOP_RETURN) if (*cx->regs->pc == JSOP_RETURN)
slotMap.addSlot(&stackval(-1)); slotMap.addSlot(&stackval(-1));
else else
slotMap.addSlot(TT_VOID); slotMap.addSlot(TT_VOID);
@@ -386,7 +395,7 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc)
unsigned frameDepth; unsigned frameDepth;
unsigned downPostSlots; unsigned downPostSlots;
JSStackFrame* fp = cx->fp; FrameRegsIter i(cx);
LIns* fp_ins = LIns* fp_ins =
addName(lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, fp), ACC_OTHER), "fp"); addName(lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, fp), ACC_OTHER), "fp");
@@ -399,7 +408,7 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc)
if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) {
fp_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, down), ACC_OTHER), fp_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, down), ACC_OTHER),
"downFp"); "downFp");
fp = fp->down; ++i;
argv_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, argv), ACC_OTHER), argv_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, argv), ACC_OTHER),
"argv"); "argv");
@@ -427,14 +436,12 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc)
RECURSIVE_LOOP_EXIT); RECURSIVE_LOOP_EXIT);
} }
/* fp->down->regs->pc should be == pc. */ /* fp->down->savedPC should be == pc. */
guard(true, guard(true,
lir->ins2(LIR_eqp, lir->ins2(LIR_eqp,
lir->insLoad(LIR_ldp, addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, savedPC),
addName(lir->insLoad(LIR_ldp, fp_ins, ACC_OTHER),
offsetof(JSStackFrame, regs), ACC_OTHER), "savedPC"),
"regs"),
offsetof(JSFrameRegs, pc), ACC_OTHER),
INS_CONSTPTR(return_pc)), INS_CONSTPTR(return_pc)),
RECURSIVE_SLURP_MISMATCH_EXIT); RECURSIVE_SLURP_MISMATCH_EXIT);
@@ -489,13 +496,13 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc)
* thrown away. * thrown away.
*/ */
TraceType* typeMap = exit->stackTypeMap(); TraceType* typeMap = exit->stackTypeMap();
jsbytecode* oldpc = cx->fp->regs->pc; jsbytecode* oldpc = cx->regs->pc;
cx->fp->regs->pc = exit->pc; cx->regs->pc = exit->pc;
captureStackTypes(frameDepth, typeMap); captureStackTypes(frameDepth, typeMap);
cx->fp->regs->pc = oldpc; cx->regs->pc = oldpc;
if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) {
JS_ASSERT_IF(*cx->fp->regs->pc != JSOP_RETURN, *cx->fp->regs->pc == JSOP_STOP); JS_ASSERT_IF(*cx->regs->pc != JSOP_RETURN, *cx->regs->pc == JSOP_STOP);
if (*cx->fp->regs->pc == JSOP_RETURN) if (*cx->regs->pc == JSOP_RETURN)
typeMap[downPostSlots] = determineSlotType(&stackval(-1)); typeMap[downPostSlots] = determineSlotType(&stackval(-1));
else else
typeMap[downPostSlots] = TT_VOID; typeMap[downPostSlots] = TT_VOID;
@@ -522,10 +529,10 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc)
if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) {
/* /*
* It is safe to read cx->fp->regs->pc here because the frame hasn't * It is safe to read cx->regs->pc here because the frame hasn't
* been popped yet. We're guaranteed to have a return or stop. * been popped yet. We're guaranteed to have a return or stop.
*/ */
JSOp op = JSOp(*cx->fp->regs->pc); JSOp op = JSOp(*cx->regs->pc);
JS_ASSERT(op == JSOP_RETURN || op == JSOP_STOP); JS_ASSERT(op == JSOP_RETURN || op == JSOP_STOP);
if (op == JSOP_RETURN) { if (op == JSOP_RETURN) {
@@ -575,6 +582,8 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc)
info.slurpFailSlot = (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT) ? info.slurpFailSlot = (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT) ?
anchor->slurpFailSlot : 0; anchor->slurpFailSlot : 0;
JSStackFrame *const fp = i.fp();
/* callee */ /* callee */
slurpSlot(lir->insLoad(LIR_ldp, argv_ins, -2 * ptrdiff_t(sizeof(jsval)), ACC_OTHER), slurpSlot(lir->insLoad(LIR_ldp, argv_ins, -2 * ptrdiff_t(sizeof(jsval)), ACC_OTHER),
&fp->argv[-2], &fp->argv[-2],
@@ -612,7 +621,8 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc)
slots_ins, slots_ins,
INS_CONSTWORD(nfixed * sizeof(jsval))), INS_CONSTWORD(nfixed * sizeof(jsval))),
"stackBase"); "stackBase");
size_t limit = size_t(fp->regs->sp - StackBase(fp));
size_t limit = size_t(i.sp() - StackBase(fp));
if (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT) if (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT)
limit--; limit--;
else else
@@ -634,7 +644,7 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc)
RecursiveSlotMap slotMap(*this, downPostSlots, rval_ins); RecursiveSlotMap slotMap(*this, downPostSlots, rval_ins);
for (unsigned i = 0; i < downPostSlots; i++) for (unsigned i = 0; i < downPostSlots; i++)
slotMap.addSlot(typeMap[i]); slotMap.addSlot(typeMap[i]);
if (*cx->fp->regs->pc == JSOP_RETURN) if (*cx->regs->pc == JSOP_RETURN)
slotMap.addSlot(&stackval(-1), typeMap[downPostSlots]); slotMap.addSlot(&stackval(-1), typeMap[downPostSlots]);
else else
slotMap.addSlot(TT_VOID); slotMap.addSlot(TT_VOID);

View File

@@ -4783,7 +4783,7 @@ MatchRegExp(REGlobalData *gData, REMatchState *x)
"entering REGEXP trace at %s:%u@%u, code: %p\n", "entering REGEXP trace at %s:%u@%u, code: %p\n",
caller ? caller->script->filename : "<unknown>", caller ? caller->script->filename : "<unknown>",
caller ? js_FramePCToLineNumber(gData->cx, caller) : 0, caller ? js_FramePCToLineNumber(gData->cx, caller) : 0,
caller ? FramePCOffset(caller) : 0, caller ? FramePCOffset(gData->cx, caller) : 0,
JS_FUNC_TO_DATA_PTR(void *, native)); JS_FUNC_TO_DATA_PTR(void *, native));
}) })
#endif #endif

View File

@@ -1312,7 +1312,7 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
uintN uintN
js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp) js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp)
{ {
return js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : fp->regs->pc); return js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : fp->pc(cx));
} }
uintN uintN

File diff suppressed because it is too large Load Diff

View File

@@ -1754,7 +1754,6 @@ ParseXMLSource(JSContext *cx, JSString *src)
JSXML *xml; JSXML *xml;
const char *filename; const char *filename;
uintN lineno; uintN lineno;
JSStackFrame *fp;
JSOp op; JSOp op;
JSParseNode *pn; JSParseNode *pn;
JSXMLArray nsarray; JSXMLArray nsarray;
@@ -1797,13 +1796,16 @@ ParseXMLSource(JSContext *cx, JSString *src)
&dstlen); &dstlen);
chars [offset + dstlen] = 0; chars [offset + dstlen] = 0;
LeaveTrace(cx);
xml = NULL; xml = NULL;
for (fp = js_GetTopStackFrame(cx); fp && !fp->regs; fp = fp->down) FrameRegsIter i(cx);
JS_ASSERT(!fp->script); for (; !i.done() && !i.pc(); ++i)
JS_ASSERT(!i.fp()->script);
filename = NULL; filename = NULL;
lineno = 1; lineno = 1;
if (fp) { if (!i.done()) {
op = (JSOp) *fp->regs->pc; JSStackFrame *fp = i.fp();
op = (JSOp) *i.pc();
if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) { if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
filename = fp->script->filename; filename = fp->script->filename;
lineno = js_FramePCToLineNumber(cx, fp); lineno = js_FramePCToLineNumber(cx, fp);
@@ -7717,7 +7719,8 @@ js_StepXMLListFilter(JSContext *cx, JSBool initialized)
JSXML *xml, *list; JSXML *xml, *list;
JSXMLFilter *filter; JSXMLFilter *filter;
sp = js_GetTopStackFrame(cx)->regs->sp; LeaveTrace(cx);
sp = cx->regs->sp;
if (!initialized) { if (!initialized) {
/* /*
* We haven't iterated yet, so initialize the filter based on the * We haven't iterated yet, so initialize the filter based on the

View File

@@ -3023,7 +3023,7 @@ EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
ok = JS_EvaluateUCScript(scx, sobj, src, srclen, ok = JS_EvaluateUCScript(scx, sobj, src, srclen,
fp->script->filename, fp->script->filename,
JS_PCToLineNumber(cx, fp->script, JS_PCToLineNumber(cx, fp->script,
fp->regs->pc), fp->pc(cx)),
rval); rval);
} }
@@ -3061,13 +3061,13 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
JS_ASSERT(cx->fp); JS_ASSERT(cx->fp);
JSStackFrame *fp = cx->fp; FrameRegsIter fi(cx);
for (uint32 i = 0; i < upCount; ++i) { for (uint32 i = 0; i < upCount; ++i, ++fi) {
if (!fp->down) if (!fi.fp()->down)
break; break;
fp = fp->down;
} }
JSStackFrame *const fp = fi.fp();
if (!fp->script) { if (!fp->script) {
JS_ReportError(cx, "cannot eval in non-script frame"); JS_ReportError(cx, "cannot eval in non-script frame");
return JS_FALSE; return JS_FALSE;
@@ -3080,7 +3080,7 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
JSBool ok = JS_EvaluateUCInStackFrame(cx, fp, str->chars(), str->length(), JSBool ok = JS_EvaluateUCInStackFrame(cx, fp, str->chars(), str->length(),
fp->script->filename, fp->script->filename,
JS_PCToLineNumber(cx, fp->script, JS_PCToLineNumber(cx, fp->script,
fp->regs->pc), fi.pc()),
vp); vp);
if (saveCurrent) if (saveCurrent)