Bug 1001368 - Fix UnwindScope logic in BaselineHandleException to account for Debugger. (r=jandem)
authorShu-yu Guo <shu@rfrn.org>
Tue, 29 Apr 2014 21:57:36 -0700
changeset 180937 a0619a71579dac10fdfb2bcbcf8a1059f952f6ac
parent 180936 ef50feec881a82d7ad3a195734dba9f389088734
child 180938 10e37b92e195e1fa441c1ae76d1a9c976c66be00
push id42895
push usershu@rfrn.org
push dateWed, 30 Apr 2014 04:57:45 +0000
treeherdermozilla-inbound@10e37b92e195 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1001368
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1001368 - Fix UnwindScope logic in BaselineHandleException to account for Debugger. (r=jandem)
js/src/jit/BaselineFrame.h
js/src/jit/IonFrames.cpp
js/src/jit/VMFunctions.cpp
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -58,35 +58,42 @@ class BaselineFrame
         // Frame has profiler entry pushed.
         HAS_PUSHED_SPS_FRAME = 1 << 8,
 
         // Frame has over-recursed on an early check.
         OVER_RECURSED    = 1 << 9,
 
         // Frame has a BaselineRecompileInfo stashed in the scratch value
         // slot. See PatchBaselineFramesForDebugMOde.
-        HAS_DEBUG_MODE_OSR_INFO = 1 << 10
+        HAS_DEBUG_MODE_OSR_INFO = 1 << 10,
+
+        // Frame has had its scope chain unwound to a pc during exception
+        // handling that is different from its current pc.
+        //
+        // This flag is intended for use in the DebugEpilogue. Once it is set,
+        // the only way to clear it is to pop the frame. Do *not* set this if
+        // we will resume execution on the frame, such as in a catch or
+        // finally block.
+        HAS_UNWOUND_SCOPE_OVERRIDE_PC = 1 << 11
     };
 
   protected: // Silence Clang warning about unused private fields.
     // We need to split the Value into 2 fields of 32 bits, otherwise the C++
     // compiler may add some padding between the fields.
     uint32_t loScratchValue_;
     uint32_t hiScratchValue_;
-    uint32_t loReturnValue_;        // If HAS_RVAL, the frame's return value.
+    uint32_t loReturnValue_;              // If HAS_RVAL, the frame's return value.
     uint32_t hiReturnValue_;
     uint32_t frameSize_;
-    JSObject *scopeChain_;          // Scope chain (always initialized).
-    JSScript *evalScript_;          // If isEvalFrame(), the current eval script.
-    ArgumentsObject *argsObj_;      // If HAS_ARGS_OBJ, the arguments object.
-    void *hookData_;                // If HAS_HOOK_DATA, debugger call hook data.
+    JSObject *scopeChain_;                // Scope chain (always initialized).
+    JSScript *evalScript_;                // If isEvalFrame(), the current eval script.
+    ArgumentsObject *argsObj_;            // If HAS_ARGS_OBJ, the arguments object.
+    void *hookData_;                      // If HAS_HOOK_DATA, debugger call hook data.
+    uint32_t unwoundScopeOverrideOffset_; // If HAS_UNWOUND_SCOPE_OVERRIDE_PC.
     uint32_t flags_;
-#if JS_BITS_PER_WORD == 32
-    uint32_t padding_;              // Pad to 8-byte alignment.
-#endif
 
   public:
     // Distance between the frame pointer and the frame header (return address).
     // This is the old frame pointer saved in the prologue.
     static const uint32_t FramePointerOffset = sizeof(void *);
 
     bool initForOsr(InterpreterFrame *fp, uint32_t numStackValues);
 
@@ -315,16 +322,32 @@ class BaselineFrame
 
     void setDebugModeOSRInfo(BaselineDebugModeOSRInfo *info) {
         flags_ |= HAS_DEBUG_MODE_OSR_INFO;
         *reinterpret_cast<BaselineDebugModeOSRInfo **>(&loScratchValue_) = info;
     }
 
     void deleteDebugModeOSRInfo();
 
+    jsbytecode *unwoundScopeOverridePc() {
+        MOZ_ASSERT(flags_ & HAS_UNWOUND_SCOPE_OVERRIDE_PC);
+        return script()->offsetToPC(unwoundScopeOverrideOffset_);
+    }
+
+    jsbytecode *getUnwoundScopeOverridePc() {
+        if (flags_ & HAS_UNWOUND_SCOPE_OVERRIDE_PC)
+            return unwoundScopeOverridePc();
+        return nullptr;
+    }
+
+    void setUnwoundScopeOverridePc(jsbytecode *pc) {
+        flags_ |= HAS_UNWOUND_SCOPE_OVERRIDE_PC;
+        unwoundScopeOverrideOffset_ = script()->pcToOffset(pc);
+    }
+
     void trace(JSTracer *trc, JitFrameIterator &frame);
 
     bool isFunctionFrame() const {
         return CalleeTokenIsFunction(calleeToken());
     }
     bool isGlobalFrame() const {
         return !CalleeTokenIsFunction(calleeToken());
     }
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -222,16 +222,23 @@ void
 JitFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const
 {
     JS_ASSERT(isBaselineJS());
     JSScript *script = this->script();
     if (scriptRes)
         *scriptRes = script;
     uint8_t *retAddr = returnAddressToFp();
 
+    // If we have unwound the scope due to exception handling to a different
+    // pc, the frame should behave as if it were settled on that pc.
+    if (jsbytecode *overridePc = baselineFrame()->getUnwoundScopeOverridePc()) {
+        *pcRes = overridePc;
+        return;
+    }
+
     // If we are in the middle of a recompile handler, get the real return
     // address as stashed in the RecompileInfo.
     if (BaselineDebugModeOSRInfo *info = baselineFrame()->getDebugModeOSRInfo())
         retAddr = info->resumeAddr;
 
     if (pcRes) {
         // If the return address is into the prologue entry address or just
         // after the debug prologue, then assume start of script.
@@ -515,18 +522,29 @@ HandleExceptionBaseline(JSContext *cx, c
         // Skip if the try note's stack depth exceeds the frame's stack depth.
         // See the big comment in TryNoteIter::settle for more info.
         JS_ASSERT(frame.baselineFrame()->numValueSlots() >= script->nfixed());
         size_t stackDepth = frame.baselineFrame()->numValueSlots() - script->nfixed();
         if (tn->stackDepth > stackDepth)
             continue;
 
         // Unwind scope chain (pop block objects).
-        if (cx->isExceptionPending())
-            UnwindScope(cx, si, script->main() + tn->start);
+        if (cx->isExceptionPending()) {
+            jsbytecode *unwindPc = script->main() + tn->start;
+            UnwindScope(cx, si, unwindPc);
+
+            // If we still need to call DebugEpilogue, we must remember the pc
+            // we unwound the scope chain to, as it will be out of sync with
+            // the frame's actual pc.
+            if (tn->kind != JSTRY_CATCH && tn->kind != JSTRY_FINALLY &&
+                cx->compartment()->debugMode() && !*calledDebugEpilogue)
+            {
+                frame.baselineFrame()->setUnwoundScopeOverridePc(unwindPc);
+            }
+        }
 
         // Compute base pointer and stack pointer.
         rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
         rfe->stackPointer = rfe->framePointer - BaselineFrame::Size() -
             (script->nfixed() + tn->stackDepth) * sizeof(Value);
 
         switch (tn->kind) {
           case JSTRY_CATCH:
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -772,17 +772,19 @@ DebugPrologue(JSContext *cx, BaselineFra
     }
 }
 
 bool
 DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok)
 {
     // Unwind scope chain to stack depth 0.
     ScopeIter si(frame, pc, cx);
-    UnwindScope(cx, si, frame->script()->main());
+    jsbytecode *unwindPc = frame->script()->main();
+    UnwindScope(cx, si, unwindPc);
+    frame->setUnwoundScopeOverridePc(unwindPc);
 
     // If ScriptDebugEpilogue 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);
 
     if (frame->isNonEvalFunctionFrame()) {
         JS_ASSERT_IF(ok, frame->hasReturnValue());