Bug 673125: Maintain a list of active js::Interrupt frames, their FrameRegs, and their interruptors. r=jorendorff.
authorJim Blandy <jimb@mozilla.com>
Tue, 23 Aug 2011 14:44:03 -0500
changeset 75857 2b0b39d1a4858d5a98584d23d4a139c464af8d37
parent 75856 64d97a61fe681034ca819e65fb7d21654081e450
child 75858 337dc46b17a6f0de38213b6962e83b017705be93
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersjorendorff
bugs673125
milestone9.0a1
Bug 673125: Maintain a list of active js::Interrupt frames, their FrameRegs, and their interruptors. r=jorendorff. The comment atop InterpreterFrames explains why this is needed, although it is only used by later patches in the series.
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsinterp.cpp
js/src/jsinterp.h
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -106,17 +106,18 @@ ThreadData::ThreadData()
     onTraceCompartment(NULL),
     recordingCompartment(NULL),
     profilingCompartment(NULL),
     maxCodeCacheBytes(DEFAULT_JIT_CACHE_SIZE),
 #endif
     waiveGCQuota(false),
     dtoaState(NULL),
     nativeStackBase(GetNativeStackBase()),
-    pendingProxyOperation(NULL)
+    pendingProxyOperation(NULL),
+    interpreterFrames(NULL)
 {
 }
 
 ThreadData::~ThreadData()
 {
     if (dtoaState)
         js_DestroyDtoaState(dtoaState);
 }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -221,16 +221,22 @@ struct ThreadData {
         gsnCache.purge();
 
         /* FIXME: bug 506341. */
         propertyCache.purge(cx);
     }
 
     /* This must be called with the GC lock held. */
     void triggerOperationCallback(JSRuntime *rt);
+
+    /*
+     * Frames currently running in js::Interpret. See InterpreterFrames for
+     * details.
+     */
+    InterpreterFrames *interpreterFrames;
 };
 
 } /* namespace js */
 
 #ifdef JS_THREADSAFE
 
 /*
  * Structure uniquely representing a thread.  It holds thread-private data
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1513,16 +1513,40 @@ CanIncDecWithoutOverflow(int32_t i)
     (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) ||                       \
     __SUNPRO_C >= 0x570)
 #  define JS_THREADED_INTERP 1
 # else
 #  define JS_THREADED_INTERP 0
 # endif
 #endif
 
+template<typename T>
+class GenericInterruptEnabler : public InterpreterFrames::InterruptEnablerBase {
+  public:
+    GenericInterruptEnabler(T *variable, T value) : variable(variable), value(value) { }
+    void enableInterrupts() const { *variable = value; }
+
+  private:
+    T *variable;
+    T value;
+};
+
+inline InterpreterFrames::InterpreterFrames(JSContext *cx, FrameRegs *regs, 
+                                            const InterruptEnablerBase &enabler)
+  : context(cx), regs(regs), enabler(enabler)
+{
+    older = JS_THREAD_DATA(cx)->interpreterFrames;
+    JS_THREAD_DATA(cx)->interpreterFrames = this;
+}
+ 
+inline InterpreterFrames::~InterpreterFrames()
+{
+    JS_THREAD_DATA(context)->interpreterFrames = older;
+}
+
 /*
  * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
  * single-thread DEBUG js shell testing to verify property cache hits.
  */
 #if defined DEBUG && !defined JS_THREADSAFE
 
 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry)                \
     JS_BEGIN_MACRO                                                            \
@@ -1700,17 +1724,18 @@ Interpret(JSContext *cx, StackFrame *ent
 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)              \
         JS_EXTENSION &&interrupt,
 # include "jsopcode.tbl"
 # undef OPDEF
     };
 
     register void * const *jumpTable = normalJumpTable;
 
-# define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
+    typedef GenericInterruptEnabler<void * const *> InterruptEnabler;
+    InterruptEnabler interruptEnabler(&jumpTable, interruptJumpTable);
 
 # ifdef JS_TRACER
 #  define CHECK_RECORDER()                                                    \
     JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
 # else
 #  define CHECK_RECORDER()  ((void)0)
 # endif
 
@@ -1733,18 +1758,18 @@ Interpret(JSContext *cx, StackFrame *ent
                                 DO_OP();
 
 # define END_EMPTY_CASES
 
 #else /* !JS_THREADED_INTERP */
 
     register intN switchMask = 0;
     intN switchOp;
-
-# define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
+    typedef GenericInterruptEnabler<intN> InterruptEnabler;
+    InterruptEnabler interruptEnabler(&switchMask, -1);
 
 # ifdef JS_TRACER
 #  define CHECK_RECORDER()                                                    \
     JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
 # else
 #  define CHECK_RECORDER()  ((void)0)
 # endif
 
@@ -1769,16 +1794,18 @@ Interpret(JSContext *cx, StackFrame *ent
 # define END_CASE_LEN4      len = 4; goto advance_pc;
 # define END_CASE_LEN5      len = 5; goto advance_pc;
 # define END_VARLEN_CASE    goto advance_pc;
 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
 # define END_EMPTY_CASES    goto advance_pc_by_one;
 
 #endif /* !JS_THREADED_INTERP */
 
+#define ENABLE_INTERRUPTS() (interruptEnabler.enableInterrupts())
+
 #define LOAD_ATOM(PCOFF, atom)                                                \
     JS_BEGIN_MACRO                                                            \
         JS_ASSERT(regs.fp()->hasImacropc()                                    \
                   ? atoms == rt->atomState.commonAtomsStart() &&              \
                     GET_INDEX(regs.pc + PCOFF) < js_common_atom_count         \
                   : (size_t)(atoms - script->atomMap.vector) <                \
                     (size_t)(script->atomMap.length -                         \
                              GET_INDEX(regs.pc + PCOFF)));                    \
@@ -1950,16 +1977,22 @@ Interpret(JSContext *cx, StackFrame *ent
 #define CHECK_INTERRUPT_HANDLER()                                             \
     JS_BEGIN_MACRO                                                            \
         if (cx->debugHooks->interruptHook)                                    \
             ENABLE_INTERRUPTS();                                              \
     JS_END_MACRO
 
     FrameRegs regs = cx->regs();
 
+    /*
+     * Help Debugger find frames running scripts that it has put in
+     * single-step mode.
+     */
+    InterpreterFrames interpreterFrame(cx, &regs, interruptEnabler);
+
     /* Repoint cx->regs to a local variable for faster access. */
     struct InterpExitGuard {
         JSContext *cx;
         const FrameRegs &regs;
         FrameRegs *prevContextRegs;
         InterpExitGuard(JSContext *cx, FrameRegs &regs)
           : cx(cx), regs(regs), prevContextRegs(&cx->regs()) {
             cx->stack.repointRegs(&regs);
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -301,16 +301,62 @@ ValueToId(JSContext *cx, const Value &v,
  */
 extern const Value &
 GetUpvar(JSContext *cx, uintN level, UpvarCookie cookie);
 
 /* Search the call stack for the nearest frame with static level targetLevel. */
 extern StackFrame *
 FindUpvarFrame(JSContext *cx, uintN targetLevel);
 
+/*
+ * A linked list of the |FrameRegs regs;| variables belonging to all
+ * js::Interpret C++ frames on this thread's stack.
+ *
+ * Note that this is *not* a list of all JS frames running under the
+ * interpreter; that would include inlined frames, whose FrameRegs are
+ * saved in various pieces in various places. Rather, this lists each
+ * js::Interpret call's live 'regs'; when control returns to that call, it
+ * will resume execution with this 'regs' instance.
+ *
+ * When Debugger puts a script in single-step mode, all js::Interpret
+ * invocations that might be presently running that script must have
+ * interrupts enabled. It's not practical to simply check
+ * script->stepModeEnabled() at each point some callee could have changed
+ * it, because there are so many places js::Interpret could possibly cause
+ * JavaScript to run: each place an object might be coerced to a primitive
+ * or a number, for example. So instead, we simply expose a list of the
+ * 'regs' those frames are using, and let Debugger tweak the affected
+ * js::Interpret frames when an onStep handler is established.
+ *
+ * Elements of this list are allocated within the js::Interpret stack
+ * frames themselves; the list is headed by this thread's js::ThreadData.
+ */
+class InterpreterFrames {
+  public:
+    class InterruptEnablerBase {
+      public:
+        virtual void enableInterrupts() const = 0;
+    };
+
+    InterpreterFrames(JSContext *cx, FrameRegs *regs, const InterruptEnablerBase &enabler);
+    ~InterpreterFrames();
+
+    /* If this js::Interpret frame is running |script|, enable interrupts. */
+    void enableInterruptsIfRunning(JSScript *script) {
+        if (script == regs->fp()->script())
+            enabler.enableInterrupts();
+    }
+
+  private:
+    JSContext *context;
+    FrameRegs *regs;
+    const InterruptEnablerBase &enabler;
+    InterpreterFrames *older;
+};
+
 } /* namespace js */
 
 /*
  * JS_LONE_INTERPRET indicates that the compiler should see just the code for
  * the js_Interpret function when compiling jsinterp.cpp. The rest of the code
  * from the file should be visible only when compiling jsinvoke.cpp. It allows
  * platform builds to optimize selectively js_Interpret when the granularity
  * of the optimizations with the given compiler is a compilation unit.