Bug 1060936 - Directly call debugger hooks. r=shu

This commit is contained in:
Tom Schuster 2014-10-03 18:46:51 +02:00
Родитель 2c5b4e5e64
Коммит ddb2dec55a
8 изменённых файлов: 167 добавлений и 198 удалений

Просмотреть файл

@ -511,8 +511,7 @@ HandleExceptionBaseline(JSContext *cx, const JitFrameIterator &frame, ResumeFrom
if (cx->isExceptionPending() && cx->compartment()->debugMode()) {
BaselineFrame *baselineFrame = frame.baselineFrame();
JSTrapStatus status = DebugExceptionUnwind(cx, baselineFrame, pc);
switch (status) {
switch (Debugger::onExceptionUnwind(cx, baselineFrame)) {
case JSTRAP_ERROR:
// Uncatchable exception.
MOZ_ASSERT(!cx->isExceptionPending());

Просмотреть файл

@ -22,6 +22,7 @@
#include "jit/BaselineFrame-inl.h"
#include "jit/IonFrames-inl.h"
#include "vm/Debugger-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/ObjectImpl-inl.h"
#include "vm/StringObject-inl.h"
@ -763,8 +764,7 @@ DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustRet
{
*mustReturn = false;
JSTrapStatus status = ScriptDebugPrologue(cx, frame, pc);
switch (status) {
switch (Debugger::onEnterFrame(cx, frame)) {
case JSTRAP_CONTINUE:
return true;
@ -780,7 +780,7 @@ DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustRet
return false;
default:
MOZ_CRASH("Invalid trap status");
MOZ_CRASH("bad Debugger::onEnterFrame status");
}
}
@ -808,10 +808,10 @@ DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok)
jsbytecode *unwindPc = frame->script()->main();
frame->setUnwoundScopeOverridePc(unwindPc);
// If ScriptDebugEpilogue returns |true| we have to return the frame's
// If Debugger::onLeaveFrame returns |true| we have to return the frame's
// return value. If it returns |false|, the debugger threw an exception.
// In both cases we have to pop debug scopes.
ok = ScriptDebugEpilogue(cx, frame, pc, ok);
ok = Debugger::onLeaveFrame(cx, frame, ok);
if (frame->isNonEvalFunctionFrame()) {
MOZ_ASSERT_IF(ok, frame->hasReturnValue());

Просмотреть файл

@ -14,6 +14,7 @@
inline bool
js::Debugger::onLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok)
{
MOZ_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
/* Traps must be cleared from eval frames, see slowPathOnLeaveFrame. */
bool evalTraps = frame.isEvalFrame() &&
frame.script()->hasAnyBreakpointsOrStepMode();

Просмотреть файл

@ -498,9 +498,9 @@ Debugger::hasAnyLiveHooks() const
}
JSTrapStatus
Debugger::slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp)
Debugger::slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame)
{
/* Build the list of recipients. */
// Build the list of recipients.
AutoValueVector triggered(cx);
Handle<GlobalObject*> global = cx->global();
@ -510,22 +510,45 @@ Debugger::slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHan
if (dbg->observesFrame(frame) && dbg->observesEnterFrame() &&
!triggered.append(ObjectValue(*dbg->toJSObject())))
{
cx->clearPendingException();
return JSTRAP_ERROR;
}
}
}
/* Deliver the event, checking again as in dispatchHook. */
JSTrapStatus status = JSTRAP_CONTINUE;
RootedValue rval(cx);
// Deliver the event, checking again as in dispatchHook.
for (Value *p = triggered.begin(); p != triggered.end(); p++) {
Debugger *dbg = Debugger::fromJSObject(&p->toObject());
if (dbg->debuggees.has(global) && dbg->observesEnterFrame()) {
JSTrapStatus status = dbg->fireEnterFrame(cx, frame, vp);
status = dbg->fireEnterFrame(cx, frame, &rval);
if (status != JSTRAP_CONTINUE)
return status;
break;
}
}
return JSTRAP_CONTINUE;
switch (status) {
case JSTRAP_CONTINUE:
break;
case JSTRAP_THROW:
cx->setPendingException(rval);
break;
case JSTRAP_ERROR:
cx->clearPendingException();
break;
case JSTRAP_RETURN:
frame.setReturnValue(rval);
break;
default:
MOZ_CRASH("bad Debugger::onEnterFrame JSTrapStatus value");
}
return status;
}
static void
@ -644,6 +667,36 @@ Debugger::slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool frame
}
}
JSTrapStatus
Debugger::slowPathOnExceptionUnwind(JSContext *cx, AbstractFramePtr frame)
{
RootedValue rval(cx);
JSTrapStatus status = dispatchHook(cx, &rval, OnExceptionUnwind);
switch (status) {
case JSTRAP_CONTINUE:
break;
case JSTRAP_THROW:
cx->setPendingException(rval);
break;
case JSTRAP_ERROR:
cx->clearPendingException();
break;
case JSTRAP_RETURN:
cx->clearPendingException();
frame.setReturnValue(rval);
break;
default:
MOZ_CRASH("Invalid trap status");
}
return status;
}
bool
Debugger::wrapEnvironment(JSContext *cx, Handle<Env*> env, MutableHandleValue rval)
{

Просмотреть файл

@ -369,9 +369,9 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
JSObject *getHook(Hook hook) const;
bool hasAnyLiveHooks() const;
static JSTrapStatus slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame,
MutableHandleValue vp);
static JSTrapStatus slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame);
static bool slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok);
static JSTrapStatus slowPathOnExceptionUnwind(JSContext *cx, AbstractFramePtr frame);
static void slowPathOnNewScript(JSContext *cx, HandleScript script,
GlobalObject *compileAndGoGlobal);
static void slowPathOnNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
@ -453,13 +453,61 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
static void detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global);
static void findCompartmentEdges(JS::Zone *v, gc::ComponentFinder<JS::Zone> &finder);
static inline JSTrapStatus onEnterFrame(JSContext *cx, AbstractFramePtr frame,
MutableHandleValue vp);
/*
* Announce to the debugger that the thread has entered a new JavaScript frame,
* |frame|. Call whatever hooks have been registered to observe new frames, and
* return a JSTrapStatus code indication how execution should proceed:
*
* - JSTRAP_CONTINUE: Continue execution normally.
*
* - JSTRAP_THROW: Throw an exception. onEnterFrame has set |cx|'s
* pending exception to the value to be thrown.
*
* - JSTRAP_ERROR: Terminate execution (as is done when a script is terminated
* for running too long). onEnterFrame has cleared |cx|'s pending
* exception.
*
* - JSTRAP_RETURN: Return from the new frame immediately. onEnterFrame
* has set |frame|'s return value appropriately.
*/
static inline JSTrapStatus onEnterFrame(JSContext *cx, AbstractFramePtr frame);
/*
* Announce to the debugger that the thread has exited a JavaScript frame, |frame|.
* If |ok| is true, the frame is returning normally; if |ok| is false, the frame
* is throwing an exception or terminating.
*
* Change cx's current exception and |frame|'s return value to reflect the changes
* in behavior the hooks request, if any. Return the new error/success value.
*
* This function may be called twice for the same outgoing frame; only the
* first call has any effect. (Permitting double calls simplifies some
* cases where an onPop handler's resumption value changes a return to a
* throw, or vice versa: we can redirect to a complete copy of the
* alternative path, containing its own call to onLeaveFrame.)
*/
static inline bool onLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok);
static inline JSTrapStatus onDebuggerStatement(JSContext *cx, MutableHandleValue vp);
static inline JSTrapStatus onExceptionUnwind(JSContext *cx, MutableHandleValue vp);
static inline void onNewScript(JSContext *cx, HandleScript script,
GlobalObject *compileAndGoGlobal);
/*
* Announce to the debugger that an exception has been thrown and propagated
* to |frame|. Call whatever hooks have been registered to observe this and
* return a JSTrapStatus code indication how execution should proceed:
*
* - JSTRAP_CONTINUE: Continue throwing the current exception.
*
* - JSTRAP_THROW: Throw another value. onExceptionUnwind has set |cx|'s
* pending exception to the new value.
*
* - JSTRAP_ERROR: Terminate execution. onExceptionUnwind has cleared |cx|'s
* pending exception.
*
* - JSTRAP_RETURN: Return from |frame|. onExceptionUnwind has cleared
* |cx|'s pending exception and set |frame|'s return value.
*/
static inline JSTrapStatus onExceptionUnwind(JSContext *cx, AbstractFramePtr frame);
static inline void onNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal);
static inline void onNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
static inline bool onLogAllocationSite(JSContext *cx, HandleSavedFrame frame);
static JSTrapStatus onTrap(JSContext *cx, MutableHandleValue vp);
@ -734,11 +782,11 @@ Debugger::observesGlobal(GlobalObject *global) const
}
JSTrapStatus
Debugger::onEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp)
Debugger::onEnterFrame(JSContext *cx, AbstractFramePtr frame)
{
if (!cx->compartment()->debugMode())
return JSTRAP_CONTINUE;
return slowPathOnEnterFrame(cx, frame, vp);
return slowPathOnEnterFrame(cx, frame);
}
JSTrapStatus
@ -750,11 +798,11 @@ Debugger::onDebuggerStatement(JSContext *cx, MutableHandleValue vp)
}
JSTrapStatus
Debugger::onExceptionUnwind(JSContext *cx, MutableHandleValue vp)
Debugger::onExceptionUnwind(JSContext *cx, AbstractFramePtr frame)
{
return cx->compartment()->debugMode()
? dispatchHook(cx, vp, OnExceptionUnwind)
: JSTRAP_CONTINUE;
if (!cx->compartment()->debugMode())
return JSTRAP_CONTINUE;
return slowPathOnExceptionUnwind(cx, frame);
}
void

Просмотреть файл

@ -47,6 +47,7 @@
#include "jsscriptinlines.h"
#include "jit/IonFrames-inl.h"
#include "vm/Debugger-inl.h"
#include "vm/ObjectImpl-inl.h"
#include "vm/Probes-inl.h"
#include "vm/ScopeObject-inl.h"
@ -1024,8 +1025,7 @@ HandleError(JSContext *cx, InterpreterRegs &regs)
again:
if (cx->isExceptionPending()) {
/* Call debugger throw hooks. */
if (MOZ_UNLIKELY(cx->compartment()->debugMode())) {
JSTrapStatus status = DebugExceptionUnwind(cx, regs.fp(), regs.pc);
JSTrapStatus status = Debugger::onExceptionUnwind(cx, regs.fp());
switch (status) {
case JSTRAP_ERROR:
goto again;
@ -1039,8 +1039,7 @@ HandleError(JSContext *cx, InterpreterRegs &regs)
return SuccessfulReturnContinuation;
default:
MOZ_CRASH("Invalid trap status");
}
MOZ_CRASH("Bad Debugger::onExceptionUnwind status");
}
RootedValue exception(cx);
@ -1505,9 +1504,8 @@ Interpret(JSContext *cx, RunState &state)
goto error;
}
}
if (MOZ_UNLIKELY(cx->compartment()->debugMode())) {
JSTrapStatus status = ScriptDebugPrologue(cx, activation.entryFrame(), REGS.pc);
switch (status) {
switch (Debugger::onEnterFrame(cx, activation.entryFrame())) {
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
@ -1517,8 +1515,7 @@ Interpret(JSContext *cx, RunState &state)
case JSTRAP_ERROR:
goto error;
default:
MOZ_CRASH("bad ScriptDebugPrologue status");
}
MOZ_CRASH("bad Debugger::onEnterFrame status");
}
if (cx->runtime()->profilingScripts)
@ -1787,8 +1784,7 @@ CASE(JSOP_RETRVAL)
// Stop the script. (Again no details about which script exactly.)
TraceLogStopEvent(logger);
if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), interpReturnOK);
if (!REGS.fp()->isYielding())
REGS.fp()->epilogue(cx);
@ -2609,8 +2605,8 @@ CASE(JSOP_FUNCALL)
if (!REGS.fp()->prologue(cx))
goto error;
if (MOZ_UNLIKELY(cx->compartment()->debugMode())) {
switch (ScriptDebugPrologue(cx, REGS.fp(), REGS.pc)) {
switch (Debugger::onEnterFrame(cx, REGS.fp())) {
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
@ -2620,8 +2616,7 @@ CASE(JSOP_FUNCALL)
case JSTRAP_ERROR:
goto error;
default:
MOZ_CRASH("bad ScriptDebugPrologue status");
}
MOZ_CRASH("bad Debugger::onEnterFrame status");
}
/* Load first op and dispatch it (safe since JSOP_RETRVAL). */
@ -3467,8 +3462,8 @@ DEFAULT()
MOZ_CRASH("Invalid HandleError continuation");
exit:
if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), interpReturnOK);
if (!REGS.fp()->isYielding())
REGS.fp()->epilogue(cx);
else

Просмотреть файл

@ -20,63 +20,6 @@ namespace js {
class ScopeIter;
/*
* Announce to the debugger that the thread has entered a new JavaScript frame,
* |frame|. Call whatever hooks have been registered to observe new frames, and
* return a JSTrapStatus code indication how execution should proceed:
*
* - JSTRAP_CONTINUE: Continue execution normally.
*
* - JSTRAP_THROW: Throw an exception. ScriptDebugPrologue has set |cx|'s
* pending exception to the value to be thrown.
*
* - JSTRAP_ERROR: Terminate execution (as is done when a script is terminated
* for running too long). ScriptDebugPrologue has cleared |cx|'s pending
* exception.
*
* - JSTRAP_RETURN: Return from the new frame immediately. ScriptDebugPrologue
* has set |frame|'s return value appropriately.
*/
extern JSTrapStatus
ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
/*
* Announce to the debugger that the thread has exited a JavaScript frame, |frame|.
* If |ok| is true, the frame is returning normally; if |ok| is false, the frame
* is throwing an exception or terminating.
*
* Call whatever hooks have been registered to observe frame exits. Change cx's
* current exception and |frame|'s return value to reflect the changes in behavior
* the hooks request, if any. Return the new error/success value.
*
* This function may be called twice for the same outgoing frame; only the
* first call has any effect. (Permitting double calls simplifies some
* cases where an onPop handler's resumption value changes a return to a
* throw, or vice versa: we can redirect to a complete copy of the
* alternative path, containing its own call to ScriptDebugEpilogue.)
*/
extern bool
ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool ok);
/*
* Announce to the debugger that an exception has been thrown and propagated
* to |frame|. Call whatever hooks have been registered to observe this and
* return a JSTrapStatus code indication how execution should proceed:
*
* - JSTRAP_CONTINUE: Continue throwing the current exception.
*
* - JSTRAP_THROW: Throw another value. DebugExceptionUnwind has set |cx|'s
* pending exception to the new value.
*
* - JSTRAP_ERROR: Terminate execution. DebugExceptionUnwind has cleared |cx|'s
* pending exception.
*
* - JSTRAP_RETURN: Return from |frame|. DebugExceptionUnwind has cleared
* |cx|'s pending exception and set |frame|'s return value.
*/
extern JSTrapStatus
DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
/*
* For a given |call|, convert null/undefined |this| into the global object for
* the callee and replace other primitives with boxed versions. This assumes

Просмотреть файл

@ -39,76 +39,6 @@ using namespace js::gc;
using mozilla::PodZero;
JSTrapStatus
js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
{
MOZ_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
RootedValue rval(cx);
JSTrapStatus status = Debugger::onEnterFrame(cx, frame, &rval);
switch (status) {
case JSTRAP_CONTINUE:
break;
case JSTRAP_THROW:
cx->setPendingException(rval);
break;
case JSTRAP_ERROR:
cx->clearPendingException();
break;
case JSTRAP_RETURN:
frame.setReturnValue(rval);
break;
default:
MOZ_CRASH("bad Debugger::onEnterFrame JSTrapStatus value");
}
return status;
}
bool
js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool okArg)
{
MOZ_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
bool ok = okArg;
return Debugger::onLeaveFrame(cx, frame, ok);
}
JSTrapStatus
js::DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
{
MOZ_ASSERT(cx->compartment()->debugMode());
/* Call debugger throw hook if set. */
RootedValue rval(cx);
JSTrapStatus status = Debugger::onExceptionUnwind(cx, &rval);
switch (status) {
case JSTRAP_ERROR:
cx->clearPendingException();
break;
case JSTRAP_RETURN:
cx->clearPendingException();
frame.setReturnValue(rval);
break;
case JSTRAP_THROW:
cx->setPendingException(rval);
break;
case JSTRAP_CONTINUE:
break;
default:
MOZ_CRASH("Invalid trap status");
}
return status;
}
/************************************************************************/
JS_PUBLIC_API(JSScript *)
JS_GetFunctionScript(JSContext *cx, HandleFunction fun)
{