Bug 1448880 - Part 4: Split Debugger::onResumeFrame from onEnterFrame. r=jimb
authorJason Orendorff <jorendorff@mozilla.com>
Tue, 23 Oct 2018 23:23:15 +0000
changeset 491040 61031045a58cad9c96f2051a858031bf37443769
parent 491039 9c03b503909a6d03150869a782d5e148870c20f5
child 491041 27b0121d9e314b26e2c11857b18409f5dabbd75b
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersjimb
bugs1448880
milestone65.0a1
Bug 1448880 - Part 4: Split Debugger::onResumeFrame from onEnterFrame. r=jimb Pure refactoring, no change in behavior. This is in anticipation of doing additional work in onResumeFrame. Depends on D6984 Differential Revision: https://phabricator.services.mozilla.com/D6985
js/src/jit/VMFunctions.cpp
js/src/vm/Debugger-inl.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/Interpreter.cpp
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -814,22 +814,22 @@ WrapObjectPure(JSContext* cx, JSObject* 
         // Ensure the wrapper is still exposed.
         JS::ExposeObjectToActiveJS(wrapped);
         return wrapped;
     }
 
     return nullptr;
 }
 
-bool
-DebugPrologue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool* mustReturn)
+static bool
+HandlePrologueResumeMode(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool* mustReturn,
+                         ResumeMode resumeMode)
 {
     *mustReturn = false;
-
-    switch (Debugger::onEnterFrame(cx, frame)) {
+    switch (resumeMode) {
       case ResumeMode::Continue:
         return true;
 
       case ResumeMode::Return:
         // The script is going to return immediately, so we have to call the
         // debug epilogue handler as well.
         MOZ_ASSERT(frame->hasReturnValue());
         *mustReturn = true;
@@ -840,16 +840,23 @@ DebugPrologue(JSContext* cx, BaselineFra
         return false;
 
       default:
         MOZ_CRASH("bad Debugger::onEnterFrame resume mode");
     }
 }
 
 bool
+DebugPrologue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool* mustReturn)
+{
+    ResumeMode resumeMode = Debugger::onEnterFrame(cx, frame);
+    return HandlePrologueResumeMode(cx, frame, pc, mustReturn, resumeMode);
+}
+
+bool
 DebugEpilogueOnBaselineReturn(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
 {
     if (!DebugEpilogue(cx, frame, pc, true)) {
         // DebugEpilogue popped the frame by updating packedExitFP, so run the
         // stop event here before we enter the exception handler.
         TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
         TraceLogStopEvent(logger, TraceLogger_Baseline);
         TraceLogStopEvent(logger, TraceLogger_Scripts);
@@ -953,27 +960,28 @@ InterpretResume(JSContext* cx, HandleObj
 
     return CallSelfHostedFunction(cx, cx->names().InterpretGeneratorResume, UndefinedHandleValue,
                                   args, rval);
 }
 
 bool
 DebugAfterYield(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool* mustReturn)
 {
-    *mustReturn = false;
-
     // The BaselineFrame has just been constructed by JSOP_RESUME in the
     // caller. We need to set its debuggee flag as necessary.
     //
     // If a breakpoint is set on JSOP_DEBUGAFTERYIELD, or stepping is enabled,
     // we may already have done this work. Don't fire onEnterFrame again.
     if (frame->script()->isDebuggee() && !frame->isDebuggee()) {
         frame->setIsDebuggee();
-        return DebugPrologue(cx, frame, pc, mustReturn);
+        ResumeMode resumeMode = Debugger::onResumeFrame(cx, frame);
+        return HandlePrologueResumeMode(cx, frame, pc, mustReturn, resumeMode);
     }
+
+    *mustReturn = false;
     return true;
 }
 
 bool
 GeneratorThrowOrReturn(JSContext* cx, BaselineFrame* frame, Handle<GeneratorObject*> genObj,
                        HandleValue arg, uint32_t resumeKind)
 {
     // Set the frame's pc to the current resume pc, so that frame iterators
--- a/js/src/vm/Debugger-inl.h
+++ b/js/src/vm/Debugger-inl.h
@@ -60,16 +60,26 @@ js::Debugger::onEnterFrame(JSContext* cx
     MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(), frame.isDebuggee());
     if (!frame.isDebuggee()) {
         return ResumeMode::Continue;
     }
     return slowPathOnEnterFrame(cx, frame);
 }
 
 /* static */ js::ResumeMode
+js::Debugger::onResumeFrame(JSContext* cx, AbstractFramePtr frame)
+{
+    MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(), frame.isDebuggee());
+    if (!frame.isDebuggee()) {
+        return ResumeMode::Continue;
+    }
+    return slowPathOnResumeFrame(cx, frame);
+}
+
+/* static */ js::ResumeMode
 js::Debugger::onDebuggerStatement(JSContext* cx, AbstractFramePtr frame)
 {
     if (!cx->realm()->isDebuggee()) {
         return ResumeMode::Continue;
     }
     return slowPathOnDebuggerStatement(cx, frame);
 }
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -999,16 +999,26 @@ Debugger::slowPathOnEnterFrame(JSContext
 
       default:
         MOZ_CRASH("bad Debugger::onEnterFrame resume mode");
     }
 
     return resumeMode;
 }
 
+/* static */ ResumeMode
+Debugger::slowPathOnResumeFrame(JSContext* cx, AbstractFramePtr frame)
+{
+    // Don't count on this method to be called every time a generator is
+    // resumed! This is called only if the frame's debuggee bit is set,
+    // i.e. the script has breakpoints or the frame is stepping.
+    return slowPathOnEnterFrame(cx, frame);
+}
+
+
 static void
 DebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp* fop, AbstractFramePtr frame,
                                                      NativeObject* frameobj);
 
 /*
  * RAII class to mark a generator as "running" temporarily while running
  * debugger code.
  *
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -843,16 +843,17 @@ class Debugger : private mozilla::Linked
     void updateObservesAsmJSOnDebuggees(IsObserving observing);
     void updateObservesBinarySourceDebuggees(IsObserving observing);
 
     JSObject* getHook(Hook hook) const;
     bool hasAnyLiveHooks(JSRuntime* rt) const;
 
     static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx, HandleScript script);
     static ResumeMode slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
+    static ResumeMode slowPathOnResumeFrame(JSContext* cx, AbstractFramePtr frame);
     static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame,
                                                   jsbytecode* pc, bool ok);
     static MOZ_MUST_USE bool slowPathOnNewGenerator(JSContext* cx, AbstractFramePtr frame,
                                                     Handle<GeneratorObject*> genObj);
     static ResumeMode slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
     static ResumeMode slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
     static void slowPathOnNewScript(JSContext* cx, HandleScript script);
     static void slowPathOnNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
@@ -975,16 +976,33 @@ class Debugger : private mozilla::Linked
     /*
      * Announce to the debugger that the context has entered a new JavaScript
      * frame, |frame|. Call whatever hooks have been registered to observe new
      * frames.
      */
     static inline ResumeMode onEnterFrame(JSContext* cx, AbstractFramePtr frame);
 
     /*
+     * Like onEnterFrame, but for resuming execution of a generator or async
+     * function. `frame` is a new baseline or interpreter frame, but abstractly
+     * it can be identified with a particular generator frame that was
+     * suspended earlier.
+     *
+     * There is no separate user-visible Debugger.onResumeFrame hook; this
+     * fires .onEnterFrame (again, since we're re-entering the frame).
+     *
+     * Unfortunately, the interpreter and the baseline JIT arrange for this to
+     * be called in different ways. The interpreter calls it from JSOP_RESUME,
+     * immediately after pushing the resumed frame; the JIT calls it from
+     * JSOP_DEBUGAFTERYIELD, just after the generator resumes. The difference
+     * should not be user-visible.
+     */
+    static inline ResumeMode onResumeFrame(JSContext* cx, AbstractFramePtr frame);
+
+    /*
      * Announce to the debugger a |debugger;| statement on has been
      * encountered on the youngest JS frame on |cx|. Call whatever hooks have
      * been registered to observe this.
      *
      * Note that this method is called for all |debugger;| statements,
      * regardless of the frame's debuggee-ness.
      */
     static inline ResumeMode onDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -4571,17 +4571,17 @@ CASE(JSOP_RESUME)
         }
         SET_SCRIPT(generatorScript);
 
         TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
         TraceLoggerEvent scriptEvent(TraceLogger_Scripts, script);
         TraceLogStartEvent(logger, scriptEvent);
         TraceLogStartEvent(logger, TraceLogger_Interpreter);
 
-        switch (Debugger::onEnterFrame(cx, REGS.fp())) {
+        switch (Debugger::onResumeFrame(cx, REGS.fp())) {
           case ResumeMode::Continue:
             break;
           case ResumeMode::Throw:
           case ResumeMode::Terminate:
             goto error;
           case ResumeMode::Return:
             MOZ_ASSERT_IF(REGS.fp()->callee().isGenerator(),  // as opposed to an async function
                           gen->isClosed());