Bug 673125: Maintain a list of active js::Interrupt frames, their FrameRegs, and their interruptors. r=jorendorff.

The comment atop InterpreterFrames explains why this is needed, although it
is only used by later patches in the series.
This commit is contained in:
Jim Blandy 2011-08-23 14:44:03 -05:00
parent 8cbe20828b
commit f392770f34
4 changed files with 90 additions and 4 deletions

View File

@ -111,7 +111,8 @@ ThreadData::ThreadData()
waiveGCQuota(false),
dtoaState(NULL),
nativeStackBase(GetNativeStackBase()),
pendingProxyOperation(NULL)
pendingProxyOperation(NULL),
interpreterFrames(NULL)
{
}

View File

@ -226,6 +226,12 @@ struct ThreadData {
/* This must be called with the GC lock held. */
void triggerOperationCallback(JSRuntime *rt);
/*
* Frames currently running in js::Interpret. See InterpreterFrames for
* details.
*/
InterpreterFrames *interpreterFrames;
};
} /* namespace js */

View File

@ -1518,6 +1518,30 @@ CanIncDecWithoutOverflow(int32_t i)
# endif
#endif
template<typename T>
class GenericInterruptEnabler : public InterpreterFrames::InterruptEnablerBase {
public:
GenericInterruptEnabler(T *variable, T value) : variable(variable), value(value) { }
void enableInterrupts() const { *variable = value; }
private:
T *variable;
T value;
};
inline InterpreterFrames::InterpreterFrames(JSContext *cx, FrameRegs *regs,
const InterruptEnablerBase &enabler)
: context(cx), regs(regs), enabler(enabler)
{
older = JS_THREAD_DATA(cx)->interpreterFrames;
JS_THREAD_DATA(cx)->interpreterFrames = this;
}
inline InterpreterFrames::~InterpreterFrames()
{
JS_THREAD_DATA(context)->interpreterFrames = older;
}
/*
* Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
* single-thread DEBUG js shell testing to verify property cache hits.
@ -1705,7 +1729,8 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
register void * const *jumpTable = normalJumpTable;
# define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
typedef GenericInterruptEnabler<void * const *> InterruptEnabler;
InterruptEnabler interruptEnabler(&jumpTable, interruptJumpTable);
# ifdef JS_TRACER
# define CHECK_RECORDER() \
@ -1738,8 +1763,8 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
register intN switchMask = 0;
intN switchOp;
# define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
typedef GenericInterruptEnabler<intN> InterruptEnabler;
InterruptEnabler interruptEnabler(&switchMask, -1);
# ifdef JS_TRACER
# define CHECK_RECORDER() \
@ -1774,6 +1799,8 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
#endif /* !JS_THREADED_INTERP */
#define ENABLE_INTERRUPTS() (interruptEnabler.enableInterrupts())
#define LOAD_ATOM(PCOFF, atom) \
JS_BEGIN_MACRO \
JS_ASSERT(regs.fp()->hasImacropc() \
@ -1955,6 +1982,12 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
FrameRegs regs = cx->regs();
/*
* Help Debugger find frames running scripts that it has put in
* single-step mode.
*/
InterpreterFrames interpreterFrame(cx, &regs, interruptEnabler);
/* Repoint cx->regs to a local variable for faster access. */
struct InterpExitGuard {
JSContext *cx;

View File

@ -306,6 +306,52 @@ GetUpvar(JSContext *cx, uintN level, UpvarCookie cookie);
extern StackFrame *
FindUpvarFrame(JSContext *cx, uintN targetLevel);
/*
* A linked list of the |FrameRegs regs;| variables belonging to all
* js::Interpret C++ frames on this thread's stack.
*
* Note that this is *not* a list of all JS frames running under the
* interpreter; that would include inlined frames, whose FrameRegs are
* saved in various pieces in various places. Rather, this lists each
* js::Interpret call's live 'regs'; when control returns to that call, it
* will resume execution with this 'regs' instance.
*
* When Debugger puts a script in single-step mode, all js::Interpret
* invocations that might be presently running that script must have
* interrupts enabled. It's not practical to simply check
* script->stepModeEnabled() at each point some callee could have changed
* it, because there are so many places js::Interpret could possibly cause
* JavaScript to run: each place an object might be coerced to a primitive
* or a number, for example. So instead, we simply expose a list of the
* 'regs' those frames are using, and let Debugger tweak the affected
* js::Interpret frames when an onStep handler is established.
*
* Elements of this list are allocated within the js::Interpret stack
* frames themselves; the list is headed by this thread's js::ThreadData.
*/
class InterpreterFrames {
public:
class InterruptEnablerBase {
public:
virtual void enableInterrupts() const = 0;
};
InterpreterFrames(JSContext *cx, FrameRegs *regs, const InterruptEnablerBase &enabler);
~InterpreterFrames();
/* If this js::Interpret frame is running |script|, enable interrupts. */
void enableInterruptsIfRunning(JSScript *script) {
if (script == regs->fp()->script())
enabler.enableInterrupts();
}
private:
JSContext *context;
FrameRegs *regs;
const InterruptEnablerBase &enabler;
InterpreterFrames *older;
};
} /* namespace js */
/*