From f695eb815808358fa7aeb4997a1eb4fa90b7e131 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Tue, 23 Aug 2011 14:44:22 -0500 Subject: [PATCH] Bug 673125: Have each JSScript maintain both a count and a flag indicating whether it should be in single-step mode. r=jorendorff. The rationale is explained in the comments in jsscript.h. --HG-- extra : rebase_source : 0d537b172ce70c049269b1f09380f0eeabec9c92 --- js/src/jsdbgapi.cpp | 33 +-------------------- js/src/jsdbgapi.h | 4 --- js/src/jsinterp.h | 3 +- js/src/jsscript.cpp | 56 +++++++++++++++++++++++++++++++++++ js/src/jsscript.h | 46 +++++++++++++++++++++++++++- js/src/methodjit/Compiler.cpp | 4 +-- 6 files changed, 106 insertions(+), 40 deletions(-) diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 89be6ff7ef57..4ef229108480 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -81,9 +81,6 @@ #include "jsautooplen.h" -#include "methodjit/MethodJIT.h" -#include "methodjit/Retcon.h" - #ifdef __APPLE__ #include "sharkctl.h" #endif @@ -154,34 +151,6 @@ JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug) return comp->setDebugModeFromC(cx, !!debug); } -JS_FRIEND_API(JSBool) -js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep) -{ - assertSameCompartment(cx, script); - -#ifdef JS_METHODJIT - if (!script->singleStepMode == !singleStep) - return JS_TRUE; -#endif - - JS_ASSERT_IF(singleStep, cx->compartment->debugMode()); - -#ifdef JS_METHODJIT - /* request the next recompile to inject single step interrupts */ - script->singleStepMode = !!singleStep; - - js::mjit::JITScript *jit = script->jitNormal ? script->jitNormal : script->jitCtor; - if (jit && script->singleStepMode != jit->singleStepMode) { - js::mjit::Recompiler recompiler(cx, script); - if (!recompiler.recompile()) { - script->singleStepMode = !singleStep; - return JS_FALSE; - } - } -#endif - return JS_TRUE; -} - static JSBool CheckDebugMode(JSContext *cx) { @@ -205,7 +174,7 @@ JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep) if (!CheckDebugMode(cx)) return JS_FALSE; - return js_SetSingleStepMode(cx, script, singleStep); + return script->setStepModeFlag(cx, singleStep); } jsbytecode * diff --git a/js/src/jsdbgapi.h b/js/src/jsdbgapi.h index 5c888bbbffd3..edc31f39fd67 100644 --- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -131,10 +131,6 @@ JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug); JS_FRIEND_API(JSBool) JS_SetDebugMode(JSContext *cx, JSBool debug); -/* Turn on single step mode. Requires debug mode. */ -extern JS_FRIEND_API(JSBool) -js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep); - /* Turn on single step mode. */ extern JS_PUBLIC_API(JSBool) JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep); diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 2557675ae7eb..91ee517d10c5 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -345,11 +345,12 @@ class InterpreterFrames { enabler.enableInterrupts(); } + InterpreterFrames *older; + private: JSContext *context; FrameRegs *regs; const InterruptEnablerBase &enabler; - InterpreterFrames *older; }; } /* namespace js */ diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 77fd4ee2a9ef..4c6f56a4ce53 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -69,6 +69,7 @@ #include "jsxdrapi.h" #endif #include "methodjit/MethodJIT.h" +#include "methodjit/Retcon.h" #include "vm/Debugger.h" #include "jsobjinlines.h" @@ -1829,3 +1830,58 @@ JSScript::copyClosedSlotsTo(JSScript *other) { memcpy(other->closedSlots, closedSlots, nClosedArgs + nClosedVars); } + +bool +JSScript::recompileForStepMode(JSContext *cx) +{ +#ifdef JS_METHODJIT + js::mjit::JITScript *jit = jitNormal ? jitNormal : jitCtor; + if (jit && stepModeEnabled() != jit->singleStepMode) { + js::mjit::Recompiler recompiler(cx, this); + return recompiler.recompile(); + } +#endif + return true; +} + +bool +JSScript::tryNewStepMode(JSContext *cx, uint32 newValue) +{ + uint32 prior = stepMode; + stepMode = newValue; + + if (!prior != !newValue) { + /* Step mode has been enabled or disabled. Alert the methodjit. */ + if (!recompileForStepMode(cx)) { + stepMode = prior; + return false; + } + + if (newValue) { + /* Step mode has been enabled. Alert the interpreter. */ + InterpreterFrames *frames; + for (frames = JS_THREAD_DATA(cx)->interpreterFrames; frames; frames = frames->older) + frames->enableInterruptsIfRunning(this); + } + } + return true; +} + +bool +JSScript::setStepModeFlag(JSContext *cx, bool step) +{ + return tryNewStepMode(cx, (stepMode & stepCountMask) | (step ? stepFlagMask : 0)); +} + +bool +JSScript::changeStepModeCount(JSContext *cx, int delta) +{ + assertSameCompartment(cx, this); + JS_ASSERT_IF(delta > 0, cx->compartment->debugMode()); + + uint32 count = stepMode & stepCountMask; + JS_ASSERT(((count + delta) & stepCountMask) == count + delta); + return tryNewStepMode(cx, + (stepMode & stepFlagMask) | + ((count + delta) & stepCountMask)); +} diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 5029769ef9e3..67a0c87258d7 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -462,6 +462,15 @@ struct JSScript { private: size_t callCount_; /* Number of times the script has been called. */ + /* + * When non-zero, compile script in single-step mode. The top bit is set and + * cleared by setStepMode, as used by JSD. The lower bits are a count, + * adjusted by changeStepModeCount, used by the Debugger object. Only + * when the bit is clear and the count is zero may we compile the script + * without single-step support. + */ + uint32 stepMode; + /* * Offsets to various array structures from the end of this script, or * JSScript::INVALID_OFFSET if the array has length 0. @@ -492,7 +501,6 @@ struct JSScript { bool hasSingletons:1; /* script has singleton objects */ #ifdef JS_METHODJIT bool debugMode:1; /* script was compiled in debug mode */ - bool singleStepMode:1; /* compile script in single-step mode */ #endif jsbytecode *main; /* main entry point, after predef'ing prolog */ @@ -679,6 +687,42 @@ struct JSScript { } void copyClosedSlotsTo(JSScript *other); + + private: + static const uint32 stepFlagMask = 0x80000000U; + static const uint32 stepCountMask = 0x7fffffffU; + + /* + * Attempt to recompile with or without single-stepping support, as directed + * by stepModeEnabled(). + */ + bool recompileForStepMode(JSContext *cx); + + /* Attempt to change this->stepMode to |newValue|. */ + bool tryNewStepMode(JSContext *cx, uint32 newValue); + + public: + /* + * Set or clear the single-step flag. If the flag is set or the count + * (adjusted by changeStepModeCount) is non-zero, then the script is in + * single-step mode. (JSD uses an on/off-style interface; Debugger uses a + * count-style interface.) + */ + bool setStepModeFlag(JSContext *cx, bool step); + + /* + * Increment or decrement the single-step count. If the count is non-zero or + * the flag (set by setStepModeFlag) is set, then the script is in + * single-step mode. (JSD uses an on/off-style interface; Debugger uses a + * count-style interface.) + */ + bool changeStepModeCount(JSContext *cx, int delta); + + bool stepModeEnabled() { return !!stepMode; } + +#ifdef DEBUG + uint32 stepModeCount() { return stepMode & stepCountMask; } +#endif }; #define SHARP_NSLOTS 2 /* [#array, #depth] slots if the script diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index c7a1572a6967..62721cbcd4be 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -474,7 +474,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp) jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size()); jit->invokeEntry = result; - jit->singleStepMode = script->singleStepMode; + jit->singleStepMode = script->stepModeEnabled(); if (fun) { jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress(); jit->fastEntry = fullCode.locationOf(invokeLabel).executableAddress(); @@ -903,7 +903,7 @@ mjit::Compiler::generateMethod() op = JSOp(*PC); trap |= stubs::JSTRAP_TRAP; } - if (script->singleStepMode && scanner.firstOpInLine(PC - script->code)) + if (script->stepModeEnabled() && scanner.firstOpInLine(PC - script->code)) trap |= stubs::JSTRAP_SINGLESTEP; if (cx->hasRunOption(JSOPTION_PCCOUNT) && script->pcCounters) {