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()) { if (cx->isExceptionPending() && cx->compartment()->debugMode()) {
BaselineFrame *baselineFrame = frame.baselineFrame(); BaselineFrame *baselineFrame = frame.baselineFrame();
JSTrapStatus status = DebugExceptionUnwind(cx, baselineFrame, pc); switch (Debugger::onExceptionUnwind(cx, baselineFrame)) {
switch (status) {
case JSTRAP_ERROR: case JSTRAP_ERROR:
// Uncatchable exception. // Uncatchable exception.
MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(!cx->isExceptionPending());

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

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

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

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

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

@ -498,9 +498,9 @@ Debugger::hasAnyLiveHooks() const
} }
JSTrapStatus 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); AutoValueVector triggered(cx);
Handle<GlobalObject*> global = cx->global(); Handle<GlobalObject*> global = cx->global();
@ -510,22 +510,45 @@ Debugger::slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHan
if (dbg->observesFrame(frame) && dbg->observesEnterFrame() && if (dbg->observesFrame(frame) && dbg->observesEnterFrame() &&
!triggered.append(ObjectValue(*dbg->toJSObject()))) !triggered.append(ObjectValue(*dbg->toJSObject())))
{ {
cx->clearPendingException();
return JSTRAP_ERROR; 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++) { for (Value *p = triggered.begin(); p != triggered.end(); p++) {
Debugger *dbg = Debugger::fromJSObject(&p->toObject()); Debugger *dbg = Debugger::fromJSObject(&p->toObject());
if (dbg->debuggees.has(global) && dbg->observesEnterFrame()) { if (dbg->debuggees.has(global) && dbg->observesEnterFrame()) {
JSTrapStatus status = dbg->fireEnterFrame(cx, frame, vp); status = dbg->fireEnterFrame(cx, frame, &rval);
if (status != JSTRAP_CONTINUE) 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 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 bool
Debugger::wrapEnvironment(JSContext *cx, Handle<Env*> env, MutableHandleValue rval) Debugger::wrapEnvironment(JSContext *cx, Handle<Env*> env, MutableHandleValue rval)
{ {

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

@ -369,9 +369,9 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
JSObject *getHook(Hook hook) const; JSObject *getHook(Hook hook) const;
bool hasAnyLiveHooks() const; bool hasAnyLiveHooks() const;
static JSTrapStatus slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame, static JSTrapStatus slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame);
MutableHandleValue vp);
static bool slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok); static bool slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok);
static JSTrapStatus slowPathOnExceptionUnwind(JSContext *cx, AbstractFramePtr frame);
static void slowPathOnNewScript(JSContext *cx, HandleScript script, static void slowPathOnNewScript(JSContext *cx, HandleScript script,
GlobalObject *compileAndGoGlobal); GlobalObject *compileAndGoGlobal);
static void slowPathOnNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global); 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 detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global);
static void findCompartmentEdges(JS::Zone *v, gc::ComponentFinder<JS::Zone> &finder); 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 bool onLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok);
static inline JSTrapStatus onDebuggerStatement(JSContext *cx, MutableHandleValue vp); 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 void onNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
static inline bool onLogAllocationSite(JSContext *cx, HandleSavedFrame frame); static inline bool onLogAllocationSite(JSContext *cx, HandleSavedFrame frame);
static JSTrapStatus onTrap(JSContext *cx, MutableHandleValue vp); static JSTrapStatus onTrap(JSContext *cx, MutableHandleValue vp);
@ -734,11 +782,11 @@ Debugger::observesGlobal(GlobalObject *global) const
} }
JSTrapStatus JSTrapStatus
Debugger::onEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp) Debugger::onEnterFrame(JSContext *cx, AbstractFramePtr frame)
{ {
if (!cx->compartment()->debugMode()) if (!cx->compartment()->debugMode())
return JSTRAP_CONTINUE; return JSTRAP_CONTINUE;
return slowPathOnEnterFrame(cx, frame, vp); return slowPathOnEnterFrame(cx, frame);
} }
JSTrapStatus JSTrapStatus
@ -750,11 +798,11 @@ Debugger::onDebuggerStatement(JSContext *cx, MutableHandleValue vp)
} }
JSTrapStatus JSTrapStatus
Debugger::onExceptionUnwind(JSContext *cx, MutableHandleValue vp) Debugger::onExceptionUnwind(JSContext *cx, AbstractFramePtr frame)
{ {
return cx->compartment()->debugMode() if (!cx->compartment()->debugMode())
? dispatchHook(cx, vp, OnExceptionUnwind) return JSTRAP_CONTINUE;
: JSTRAP_CONTINUE; return slowPathOnExceptionUnwind(cx, frame);
} }
void void

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

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

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

@ -20,63 +20,6 @@ namespace js {
class ScopeIter; 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 * For a given |call|, convert null/undefined |this| into the global object for
* the callee and replace other primitives with boxed versions. This assumes * the callee and replace other primitives with boxed versions. This assumes

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

@ -39,76 +39,6 @@ using namespace js::gc;
using mozilla::PodZero; 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_PUBLIC_API(JSScript *)
JS_GetFunctionScript(JSContext *cx, HandleFunction fun) JS_GetFunctionScript(JSContext *cx, HandleFunction fun)
{ {