From ddb2dec55a0e2e7fd3952bf2dbd249d7eebbfa5c Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Fri, 3 Oct 2014 18:46:51 +0200 Subject: [PATCH] Bug 1060936 - Directly call debugger hooks. r=shu --- js/src/jit/IonFrames.cpp | 3 +- js/src/jit/VMFunctions.cpp | 10 ++--- js/src/vm/Debugger-inl.h | 1 + js/src/vm/Debugger.cpp | 65 ++++++++++++++++++++++++++--- js/src/vm/Debugger.h | 74 +++++++++++++++++++++++++++------ js/src/vm/Interpreter.cpp | 85 ++++++++++++++++++-------------------- js/src/vm/Interpreter.h | 57 ------------------------- js/src/vm/OldDebugAPI.cpp | 70 ------------------------------- 8 files changed, 167 insertions(+), 198 deletions(-) diff --git a/js/src/jit/IonFrames.cpp b/js/src/jit/IonFrames.cpp index 75d8924e1c4b..82e1b3d62925 100644 --- a/js/src/jit/IonFrames.cpp +++ b/js/src/jit/IonFrames.cpp @@ -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()); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index c30885f2d758..9980b6db21ad 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -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()); diff --git a/js/src/vm/Debugger-inl.h b/js/src/vm/Debugger-inl.h index 2f5740f706f2..8d41de4a8158 100644 --- a/js/src/vm/Debugger-inl.h +++ b/js/src/vm/Debugger-inl.h @@ -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(); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 55943af48ee0..3d136fd9952d 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -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 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, MutableHandleValue rval) { diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 27115799d054..39db7a232914 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -369,9 +369,9 @@ class Debugger : private mozilla::LinkedListElement 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 global); @@ -453,13 +453,61 @@ class Debugger : private mozilla::LinkedListElement static void detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global); static void findCompartmentEdges(JS::Zone *v, gc::ComponentFinder &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 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 diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 268a0563daf8..f2df74145bfa 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -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,23 +1025,21 @@ HandleError(JSContext *cx, InterpreterRegs ®s) again: if (cx->isExceptionPending()) { /* Call debugger throw hooks. */ - if (MOZ_UNLIKELY(cx->compartment()->debugMode())) { - JSTrapStatus status = DebugExceptionUnwind(cx, regs.fp(), regs.pc); - switch (status) { - case JSTRAP_ERROR: - goto again; + JSTrapStatus status = Debugger::onExceptionUnwind(cx, regs.fp()); + switch (status) { + case JSTRAP_ERROR: + goto again; - case JSTRAP_CONTINUE: - case JSTRAP_THROW: - break; + case JSTRAP_CONTINUE: + case JSTRAP_THROW: + break; - case JSTRAP_RETURN: - ForcedReturn(cx, si, regs); - return SuccessfulReturnContinuation; + case JSTRAP_RETURN: + ForcedReturn(cx, si, regs); + return SuccessfulReturnContinuation; - default: - MOZ_CRASH("Invalid trap status"); - } + default: + MOZ_CRASH("Bad Debugger::onExceptionUnwind status"); } RootedValue exception(cx); @@ -1505,20 +1504,18 @@ Interpret(JSContext *cx, RunState &state) goto error; } } - if (MOZ_UNLIKELY(cx->compartment()->debugMode())) { - JSTrapStatus status = ScriptDebugPrologue(cx, activation.entryFrame(), REGS.pc); - switch (status) { - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - ForcedReturn(cx, REGS); - goto successful_return_continuation; - case JSTRAP_THROW: - case JSTRAP_ERROR: - goto error; - default: - MOZ_CRASH("bad ScriptDebugPrologue status"); - } + + switch (Debugger::onEnterFrame(cx, activation.entryFrame())) { + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + ForcedReturn(cx, REGS); + goto successful_return_continuation; + case JSTRAP_THROW: + case JSTRAP_ERROR: + goto error; + default: + 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,19 +2605,18 @@ CASE(JSOP_FUNCALL) if (!REGS.fp()->prologue(cx)) goto error; - if (MOZ_UNLIKELY(cx->compartment()->debugMode())) { - switch (ScriptDebugPrologue(cx, REGS.fp(), REGS.pc)) { - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - ForcedReturn(cx, REGS); - goto successful_return_continuation; - case JSTRAP_THROW: - case JSTRAP_ERROR: - goto error; - default: - MOZ_CRASH("bad ScriptDebugPrologue status"); - } + + switch (Debugger::onEnterFrame(cx, REGS.fp())) { + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + ForcedReturn(cx, REGS); + goto successful_return_continuation; + case JSTRAP_THROW: + case JSTRAP_ERROR: + goto error; + default: + 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 diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 62b85f11c075..91f4206e3e51 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -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 diff --git a/js/src/vm/OldDebugAPI.cpp b/js/src/vm/OldDebugAPI.cpp index 392b4126618d..a9ad023adcb2 100644 --- a/js/src/vm/OldDebugAPI.cpp +++ b/js/src/vm/OldDebugAPI.cpp @@ -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) {