Loading js/src/jsinterp.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -465,8 +465,11 @@ js::ExecuteKernel(JSContext *cx, JSScript *script_, JSObject &scopeChain, const bool ok = RunScript(cx, script, fp); if (fp->isStrictEvalFrame()) if (fp->isStrictEvalFrame()) { if (cx->compartment->debugMode()) cx->runtime->debugScopes->onPopStrictEvalScope(fp); js_PutCallObject(fp, fp->callObj()); } Probes::stopExecution(cx, script); Loading js/src/vm/ScopeObject.cpp +112 −18 Original line number Diff line number Diff line Loading @@ -1468,7 +1468,8 @@ js_IsDebugScopeSlow(const JSObject *obj) DebugScopes::DebugScopes(JSRuntime *rt) : proxiedScopes(rt), missingScopes(rt) missingScopes(rt), liveScopes(rt) {} DebugScopes::~DebugScopes() Loading @@ -1479,7 +1480,8 @@ DebugScopes::~DebugScopes() bool DebugScopes::init() { if (!proxiedScopes.init() || if (!liveScopes.init() || !proxiedScopes.init() || !missingScopes.init()) { return false; Loading @@ -1505,6 +1507,18 @@ DebugScopes::sweep() if (!IsObjectMarked(&e.front().value)) e.removeFront(); } /* * Since liveScopes includes entries for suspended generator frames which * may be collected when the generator becomes unreachable we must sweep * liveScopes for dead generator frames. */ for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) { if (JS_IsAboutToBeFinalized(e.front().key)) { JS_ASSERT(e.front().value->isGeneratorFrame()); e.removeFront(); } } } /* Loading Loading @@ -1574,7 +1588,15 @@ DebugScopes::onPopCall(StackFrame *fp) if (fp->isYielding()) return; if (!fp->fun()->isHeavyweight()) { if (fp->fun()->isHeavyweight()) { /* * When a frame finishes executing in mjit code, the epilogue is called * once from the return and once when the frame is popped. * TODO: bug 659577 will remove this (with HAS_CALL_OBJ). */ if (fp->hasCallObj()) liveScopes.remove(&fp->scopeChain()->asCall()); } else { JS_ASSERT(!fp->hasCallObj()); if (MissingScopeMap::Ptr p = missingScopes.lookup(ScopeIter(fp))) { js_PutCallObject(fp, p->value->scope().asCall()); Loading @@ -1587,7 +1609,9 @@ void DebugScopes::onPopBlock(JSContext *cx, StackFrame *fp) { StaticBlockObject &block = *fp->maybeBlockChain(); if (!block.needsClone()) { if (block.needsClone()) { liveScopes.remove(&fp->scopeChain()->asClonedBlock()); } else { JS_ASSERT(!fp->scopeChain()->isBlock() || fp->scopeChain()->asClonedBlock().staticBlock() != block); if (MissingScopeMap::Ptr p = missingScopes.lookup(ScopeIter(fp))) { Loading @@ -1597,11 +1621,36 @@ DebugScopes::onPopBlock(JSContext *cx, StackFrame *fp) } } void DebugScopes::onPopWith(StackFrame *fp) { liveScopes.remove(&fp->scopeChain()->asWith()); } void DebugScopes::onPopStrictEvalScope(StackFrame *fp) { liveScopes.remove(&fp->scopeChain()->asCall()); } void DebugScopes::onGeneratorFrameChange(StackFrame *from, StackFrame *to) { for (ScopeIter toIter(to); !toIter.done(); toIter = toIter.enclosing()) { if (!toIter.hasScopeObject()) { if (toIter.hasScopeObject()) { /* * Not only must we correctly replace mappings [scope -> from] with * mappings [scope -> to], but we must add [scope -> to] if it * doesn't already exist so that if we need to proxy a generator's * scope while it is suspended, we can find its frame (which would * otherwise not be found by AllFramesIter). */ LiveScopeMap::AddPtr livePtr = liveScopes.lookupForAdd(&toIter.scope()); if (livePtr) livePtr->value = to; else liveScopes.add(livePtr, &toIter.scope(), to); } else { if (MissingScopeMap::Ptr p = missingScopes.lookup(ScopeIter(toIter, from))) { DebugScopeObject &debugScope = *p->value; ScopeObject &scope = debugScope.scope(); Loading @@ -1627,6 +1676,59 @@ DebugScopes::onCompartmentLeaveDebugMode(JSCompartment *c) if (e.front().key.fp()->compartment() == c) e.removeFront(); } for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) { if (e.front().key->compartment() == c) e.removeFront(); } } bool DebugScopes::updateLiveScopes(JSContext *cx) { JS_CHECK_RECURSION(cx, return false); /* * Note that we must always update the top frame's scope objects' entries * in liveScopes because we can't be sure code hasn't run in that frame to * change the scope chain since we were last called. The fp->prevUpToDate() * flag indicates whether the scopes of frames older than fp are already * included in liveScopes. It might seem simpler to have fp instead carry a * flag indicating whether fp itself is accurately described, but then we * would need to clear that flag whenever fp ran code. By storing the 'up * to date' bit for fp->prev() in fp, simply popping fp effectively clears * the flag for us, at exactly the time when execution resumes fp->prev(). */ for (AllFramesIter i(cx->runtime->stackSpace); !i.done(); ++i) { StackFrame *fp = i.fp(); if (fp->isDummyFrame() || fp->scopeChain()->compartment() != cx->compartment) continue; for (ScopeIter si(fp); !si.done(); si = si.enclosing()) { if (si.hasScopeObject() && !liveScopes.put(&si.scope(), fp)) return false; } if (fp->prevUpToDate()) return true; JS_ASSERT(fp->compartment()->debugMode()); fp->setPrevUpToDate(); } return true; } StackFrame * DebugScopes::hasLiveFrame(ScopeObject &scope) { if (LiveScopeMap::Ptr p = liveScopes.lookup(&scope)) { JS_ASSERT_IF(scope.isClonedBlock(), p->value == js_LiveFrameIfGenerator(scope.maybeStackFrame())); JS_ASSERT_IF(scope.isCall(), p->value == scope.maybeStackFrame()); return p->value; } JS_ASSERT_IF(!scope.isWith(), !scope.maybeStackFrame()); return NULL; } /*****************************************************************************/ Loading Loading @@ -1746,20 +1848,9 @@ GetDebugScope(JSContext *cx, JSObject &obj) return &obj; } /* * If 'scope' is a 'with' block, then the chain is fully reified from that * point outwards, and there's no point in bothering with a ScopeIter. If * |scope| has an associated stack frame, we can get more detailed scope * chain information from that. * Note: all this frame hackery will be removed by bug 659577. */ ScopeObject &scope = obj.asScope(); if (!scope.isWith() && scope.maybeStackFrame()) { StackFrame *fp = scope.maybeStackFrame(); if (scope.isClonedBlock()) fp = js_LiveFrameIfGenerator(fp); if (StackFrame *fp = cx->runtime->debugScopes->hasLiveFrame(scope)) return GetDebugScope(cx, ScopeIter(fp, scope)); } return GetDebugScopeForScope(cx, scope, ScopeIter(scope.enclosingScope())); } Loading @@ -1782,6 +1873,8 @@ js::GetDebugScopeForFunction(JSContext *cx, JSFunction *fun) { assertSameCompartment(cx, fun); JS_ASSERT(cx->compartment->debugMode()); if (!cx->runtime->debugScopes->updateLiveScopes(cx)) return NULL; return GetDebugScope(cx, *fun->environment()); } Loading @@ -1789,6 +1882,7 @@ JSObject * js::GetDebugScopeForFrame(JSContext *cx, StackFrame *fp) { assertSameCompartment(cx, fp); /* Unfortunately, we cannot JS_ASSERT(debugMode); see CanUseDebugScopeMaps. */ if (CanUseDebugScopeMaps(cx) && !cx->runtime->debugScopes->updateLiveScopes(cx)) return NULL; return GetDebugScope(cx, ScopeIter(fp)); } js/src/vm/ScopeObject.h +22 −1 Original line number Diff line number Diff line Loading @@ -422,9 +422,25 @@ class DebugScopes * The map from live frames which have optimized-away scopes to the * corresponding debug scopes. */ typedef HashMap<ScopeIter, DebugScopeObject *, ScopeIter, RuntimeAllocPolicy> MissingScopeMap; typedef HashMap<ScopeIter, DebugScopeObject *, ScopeIter, RuntimeAllocPolicy> MissingScopeMap; MissingScopeMap missingScopes; /* * The map from scope objects of live frames to the live frame. This map * updated lazily whenever the debugger needs the information. In between * two lazy updates, liveScopes becomes incomplete (but not invalid, onPop* * removes scopes as they are popped). Thus, two consecutive debugger lazy * updates of liveScopes need only fill in the new scopes. */ typedef HashMap<ScopeObject *, StackFrame *, DefaultHasher<ScopeObject *>, RuntimeAllocPolicy> LiveScopeMap; LiveScopeMap liveScopes; public: DebugScopes(JSRuntime *rt); ~DebugScopes(); Loading @@ -439,12 +455,17 @@ class DebugScopes DebugScopeObject *hasDebugScope(JSContext *cx, ScopeIter si) const; bool addDebugScope(JSContext *cx, ScopeIter si, DebugScopeObject &debugScope); bool updateLiveScopes(JSContext *cx); StackFrame *hasLiveFrame(ScopeObject &scope); /* * In debug-mode, these must be called whenever exiting a call/block or * when activating/yielding a generator. */ void onPopCall(StackFrame *fp); void onPopBlock(JSContext *cx, StackFrame *fp); void onPopWith(StackFrame *fp); void onPopStrictEvalScope(StackFrame *fp); void onGeneratorFrameChange(StackFrame *from, StackFrame *to); void onCompartmentLeaveDebugMode(JSCompartment *c); }; Loading js/src/vm/Stack.cpp +3 −0 Original line number Diff line number Diff line Loading @@ -259,6 +259,9 @@ StackFrame::popBlock(JSContext *cx) void StackFrame::popWith(JSContext *cx) { if (cx->compartment->debugMode()) cx->runtime->debugScopes->onPopWith(this); setScopeChain(scopeChain()->asWith().enclosingScope()); } Loading js/src/vm/Stack.h +12 −1 Original line number Diff line number Diff line Loading @@ -345,7 +345,10 @@ class StackFrame /* Method JIT state */ DOWN_FRAMES_EXPANDED = 0x100000, /* inlining in down frames has been expanded */ LOWERED_CALL_APPLY = 0x200000 /* Pushed by a lowered call/apply */ LOWERED_CALL_APPLY = 0x200000, /* Pushed by a lowered call/apply */ /* Debugger state */ PREV_UP_TO_DATE = 0x400000 /* see DebugScopes::updateLiveScopes */ }; private: Loading Loading @@ -1089,6 +1092,14 @@ class StackFrame return !!(flags_ & DEBUGGER); } bool prevUpToDate() const { return !!(flags_ & PREV_UP_TO_DATE); } void setPrevUpToDate() { flags_ |= PREV_UP_TO_DATE; } bool hasOverflowArgs() const { return !!(flags_ & OVERFLOW_ARGS); } Loading Loading
js/src/jsinterp.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -465,8 +465,11 @@ js::ExecuteKernel(JSContext *cx, JSScript *script_, JSObject &scopeChain, const bool ok = RunScript(cx, script, fp); if (fp->isStrictEvalFrame()) if (fp->isStrictEvalFrame()) { if (cx->compartment->debugMode()) cx->runtime->debugScopes->onPopStrictEvalScope(fp); js_PutCallObject(fp, fp->callObj()); } Probes::stopExecution(cx, script); Loading
js/src/vm/ScopeObject.cpp +112 −18 Original line number Diff line number Diff line Loading @@ -1468,7 +1468,8 @@ js_IsDebugScopeSlow(const JSObject *obj) DebugScopes::DebugScopes(JSRuntime *rt) : proxiedScopes(rt), missingScopes(rt) missingScopes(rt), liveScopes(rt) {} DebugScopes::~DebugScopes() Loading @@ -1479,7 +1480,8 @@ DebugScopes::~DebugScopes() bool DebugScopes::init() { if (!proxiedScopes.init() || if (!liveScopes.init() || !proxiedScopes.init() || !missingScopes.init()) { return false; Loading @@ -1505,6 +1507,18 @@ DebugScopes::sweep() if (!IsObjectMarked(&e.front().value)) e.removeFront(); } /* * Since liveScopes includes entries for suspended generator frames which * may be collected when the generator becomes unreachable we must sweep * liveScopes for dead generator frames. */ for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) { if (JS_IsAboutToBeFinalized(e.front().key)) { JS_ASSERT(e.front().value->isGeneratorFrame()); e.removeFront(); } } } /* Loading Loading @@ -1574,7 +1588,15 @@ DebugScopes::onPopCall(StackFrame *fp) if (fp->isYielding()) return; if (!fp->fun()->isHeavyweight()) { if (fp->fun()->isHeavyweight()) { /* * When a frame finishes executing in mjit code, the epilogue is called * once from the return and once when the frame is popped. * TODO: bug 659577 will remove this (with HAS_CALL_OBJ). */ if (fp->hasCallObj()) liveScopes.remove(&fp->scopeChain()->asCall()); } else { JS_ASSERT(!fp->hasCallObj()); if (MissingScopeMap::Ptr p = missingScopes.lookup(ScopeIter(fp))) { js_PutCallObject(fp, p->value->scope().asCall()); Loading @@ -1587,7 +1609,9 @@ void DebugScopes::onPopBlock(JSContext *cx, StackFrame *fp) { StaticBlockObject &block = *fp->maybeBlockChain(); if (!block.needsClone()) { if (block.needsClone()) { liveScopes.remove(&fp->scopeChain()->asClonedBlock()); } else { JS_ASSERT(!fp->scopeChain()->isBlock() || fp->scopeChain()->asClonedBlock().staticBlock() != block); if (MissingScopeMap::Ptr p = missingScopes.lookup(ScopeIter(fp))) { Loading @@ -1597,11 +1621,36 @@ DebugScopes::onPopBlock(JSContext *cx, StackFrame *fp) } } void DebugScopes::onPopWith(StackFrame *fp) { liveScopes.remove(&fp->scopeChain()->asWith()); } void DebugScopes::onPopStrictEvalScope(StackFrame *fp) { liveScopes.remove(&fp->scopeChain()->asCall()); } void DebugScopes::onGeneratorFrameChange(StackFrame *from, StackFrame *to) { for (ScopeIter toIter(to); !toIter.done(); toIter = toIter.enclosing()) { if (!toIter.hasScopeObject()) { if (toIter.hasScopeObject()) { /* * Not only must we correctly replace mappings [scope -> from] with * mappings [scope -> to], but we must add [scope -> to] if it * doesn't already exist so that if we need to proxy a generator's * scope while it is suspended, we can find its frame (which would * otherwise not be found by AllFramesIter). */ LiveScopeMap::AddPtr livePtr = liveScopes.lookupForAdd(&toIter.scope()); if (livePtr) livePtr->value = to; else liveScopes.add(livePtr, &toIter.scope(), to); } else { if (MissingScopeMap::Ptr p = missingScopes.lookup(ScopeIter(toIter, from))) { DebugScopeObject &debugScope = *p->value; ScopeObject &scope = debugScope.scope(); Loading @@ -1627,6 +1676,59 @@ DebugScopes::onCompartmentLeaveDebugMode(JSCompartment *c) if (e.front().key.fp()->compartment() == c) e.removeFront(); } for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) { if (e.front().key->compartment() == c) e.removeFront(); } } bool DebugScopes::updateLiveScopes(JSContext *cx) { JS_CHECK_RECURSION(cx, return false); /* * Note that we must always update the top frame's scope objects' entries * in liveScopes because we can't be sure code hasn't run in that frame to * change the scope chain since we were last called. The fp->prevUpToDate() * flag indicates whether the scopes of frames older than fp are already * included in liveScopes. It might seem simpler to have fp instead carry a * flag indicating whether fp itself is accurately described, but then we * would need to clear that flag whenever fp ran code. By storing the 'up * to date' bit for fp->prev() in fp, simply popping fp effectively clears * the flag for us, at exactly the time when execution resumes fp->prev(). */ for (AllFramesIter i(cx->runtime->stackSpace); !i.done(); ++i) { StackFrame *fp = i.fp(); if (fp->isDummyFrame() || fp->scopeChain()->compartment() != cx->compartment) continue; for (ScopeIter si(fp); !si.done(); si = si.enclosing()) { if (si.hasScopeObject() && !liveScopes.put(&si.scope(), fp)) return false; } if (fp->prevUpToDate()) return true; JS_ASSERT(fp->compartment()->debugMode()); fp->setPrevUpToDate(); } return true; } StackFrame * DebugScopes::hasLiveFrame(ScopeObject &scope) { if (LiveScopeMap::Ptr p = liveScopes.lookup(&scope)) { JS_ASSERT_IF(scope.isClonedBlock(), p->value == js_LiveFrameIfGenerator(scope.maybeStackFrame())); JS_ASSERT_IF(scope.isCall(), p->value == scope.maybeStackFrame()); return p->value; } JS_ASSERT_IF(!scope.isWith(), !scope.maybeStackFrame()); return NULL; } /*****************************************************************************/ Loading Loading @@ -1746,20 +1848,9 @@ GetDebugScope(JSContext *cx, JSObject &obj) return &obj; } /* * If 'scope' is a 'with' block, then the chain is fully reified from that * point outwards, and there's no point in bothering with a ScopeIter. If * |scope| has an associated stack frame, we can get more detailed scope * chain information from that. * Note: all this frame hackery will be removed by bug 659577. */ ScopeObject &scope = obj.asScope(); if (!scope.isWith() && scope.maybeStackFrame()) { StackFrame *fp = scope.maybeStackFrame(); if (scope.isClonedBlock()) fp = js_LiveFrameIfGenerator(fp); if (StackFrame *fp = cx->runtime->debugScopes->hasLiveFrame(scope)) return GetDebugScope(cx, ScopeIter(fp, scope)); } return GetDebugScopeForScope(cx, scope, ScopeIter(scope.enclosingScope())); } Loading @@ -1782,6 +1873,8 @@ js::GetDebugScopeForFunction(JSContext *cx, JSFunction *fun) { assertSameCompartment(cx, fun); JS_ASSERT(cx->compartment->debugMode()); if (!cx->runtime->debugScopes->updateLiveScopes(cx)) return NULL; return GetDebugScope(cx, *fun->environment()); } Loading @@ -1789,6 +1882,7 @@ JSObject * js::GetDebugScopeForFrame(JSContext *cx, StackFrame *fp) { assertSameCompartment(cx, fp); /* Unfortunately, we cannot JS_ASSERT(debugMode); see CanUseDebugScopeMaps. */ if (CanUseDebugScopeMaps(cx) && !cx->runtime->debugScopes->updateLiveScopes(cx)) return NULL; return GetDebugScope(cx, ScopeIter(fp)); }
js/src/vm/ScopeObject.h +22 −1 Original line number Diff line number Diff line Loading @@ -422,9 +422,25 @@ class DebugScopes * The map from live frames which have optimized-away scopes to the * corresponding debug scopes. */ typedef HashMap<ScopeIter, DebugScopeObject *, ScopeIter, RuntimeAllocPolicy> MissingScopeMap; typedef HashMap<ScopeIter, DebugScopeObject *, ScopeIter, RuntimeAllocPolicy> MissingScopeMap; MissingScopeMap missingScopes; /* * The map from scope objects of live frames to the live frame. This map * updated lazily whenever the debugger needs the information. In between * two lazy updates, liveScopes becomes incomplete (but not invalid, onPop* * removes scopes as they are popped). Thus, two consecutive debugger lazy * updates of liveScopes need only fill in the new scopes. */ typedef HashMap<ScopeObject *, StackFrame *, DefaultHasher<ScopeObject *>, RuntimeAllocPolicy> LiveScopeMap; LiveScopeMap liveScopes; public: DebugScopes(JSRuntime *rt); ~DebugScopes(); Loading @@ -439,12 +455,17 @@ class DebugScopes DebugScopeObject *hasDebugScope(JSContext *cx, ScopeIter si) const; bool addDebugScope(JSContext *cx, ScopeIter si, DebugScopeObject &debugScope); bool updateLiveScopes(JSContext *cx); StackFrame *hasLiveFrame(ScopeObject &scope); /* * In debug-mode, these must be called whenever exiting a call/block or * when activating/yielding a generator. */ void onPopCall(StackFrame *fp); void onPopBlock(JSContext *cx, StackFrame *fp); void onPopWith(StackFrame *fp); void onPopStrictEvalScope(StackFrame *fp); void onGeneratorFrameChange(StackFrame *from, StackFrame *to); void onCompartmentLeaveDebugMode(JSCompartment *c); }; Loading
js/src/vm/Stack.cpp +3 −0 Original line number Diff line number Diff line Loading @@ -259,6 +259,9 @@ StackFrame::popBlock(JSContext *cx) void StackFrame::popWith(JSContext *cx) { if (cx->compartment->debugMode()) cx->runtime->debugScopes->onPopWith(this); setScopeChain(scopeChain()->asWith().enclosingScope()); } Loading
js/src/vm/Stack.h +12 −1 Original line number Diff line number Diff line Loading @@ -345,7 +345,10 @@ class StackFrame /* Method JIT state */ DOWN_FRAMES_EXPANDED = 0x100000, /* inlining in down frames has been expanded */ LOWERED_CALL_APPLY = 0x200000 /* Pushed by a lowered call/apply */ LOWERED_CALL_APPLY = 0x200000, /* Pushed by a lowered call/apply */ /* Debugger state */ PREV_UP_TO_DATE = 0x400000 /* see DebugScopes::updateLiveScopes */ }; private: Loading Loading @@ -1089,6 +1092,14 @@ class StackFrame return !!(flags_ & DEBUGGER); } bool prevUpToDate() const { return !!(flags_ & PREV_UP_TO_DATE); } void setPrevUpToDate() { flags_ |= PREV_UP_TO_DATE; } bool hasOverflowArgs() const { return !!(flags_ & OVERFLOW_ARGS); } Loading