From 3dbc5bd2fa13c27c277a67271a5bf11414f26ce6 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Mon, 11 Nov 2013 16:53:59 -0800 Subject: [PATCH] Backed out 7 changesets (bug 935228, bug 936143, bug 935470, bug 933882, bug 934799) for breaking ASAN browser-chrome tests on a CLOSED TREE Backed out changeset ae6f2151610f (bug 934799) Backed out changeset 82495f0c5da2 (bug 934799) Backed out changeset 77be849d81e7 (bug 935228) Backed out changeset 555e5759fe5f (bug 935470) Backed out changeset ce4011f33422 (bug 933882) Backed out changeset e13e98eab890 (bug 936143) Backed out changeset fb230c191a88 (bug 936143) --- js/public/GCAPI.h | 1 + js/src/frontend/BytecodeCompiler.cpp | 3 +- js/src/frontend/BytecodeEmitter.cpp | 4 +- js/src/frontend/Parser.cpp | 3 - .../tests/debug/Script-getLineOffsets-07.js | 22 ---- .../jit-test/tests/debug/Script-startLine.js | 20 +-- js/src/jit/BaselineCompiler.cpp | 3 - js/src/jit/BaselineJIT.cpp | 4 - js/src/jit/BaselineJIT.h | 13 +- js/src/jit/Ion.cpp | 70 +---------- js/src/jscompartment.cpp | 107 +++++++++------- js/src/jscompartment.h | 103 ++++----------- js/src/jsgc.cpp | 5 + js/src/jsscript.cpp | 28 +++-- js/src/vm/Debugger.cpp | 118 +++++++----------- js/src/vm/Debugger.h | 4 +- js/src/vm/Interpreter.cpp | 87 +++++++------ js/src/vm/OldDebugAPI.cpp | 21 ++-- 18 files changed, 214 insertions(+), 402 deletions(-) delete mode 100644 js/src/jit-test/tests/debug/Script-getLineOffsets-07.js diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 190b7810602..31ce5d3d984 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -25,6 +25,7 @@ namespace JS { D(TOO_MUCH_MALLOC) \ D(ALLOC_TRIGGER) \ D(DEBUG_GC) \ + D(DEBUG_MODE_GC) \ D(TRANSPLANT) \ D(RESET) \ D(OUT_OF_NURSERY) \ diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index a9b5396967f..c0fc473f71b 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -142,8 +142,7 @@ CanLazilyParse(ExclusiveContext *cx, const ReadOnlyCompileOptions &options) return options.canLazilyParse && options.compileAndGo && options.sourcePolicy == CompileOptions::SAVE_SOURCE && - !(cx->compartment()->debugMode() && - cx->compartment()->runtimeFromAnyThread()->debugHooks.newScriptHook); + !cx->compartment()->debugMode(); } void diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 0e8e879a632..c99d9b04be9 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1757,9 +1757,7 @@ BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext *cx) RootedFunction function(cx, script->function()); CallNewScriptHook(cx->asJSContext(), script, function); - // Lazy scripts are never top level (despite always being invoked with a - // nullptr parent), and so the hook should never be fired. - if (emitterMode != LazyFunction && !parent) { + if (!parent) { GlobalObject *compileAndGoGlobal = nullptr; if (script->compileAndGo) compileAndGoGlobal = &script->global(); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 9f5d0e60f9b..4c9db27f557 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2121,9 +2121,6 @@ Parser::functionArgsAndBody(ParseNode *pn, HandleFunction fun, // Advance this parser over tokens processed by the syntax parser. parser->tokenStream.tell(&position); tokenStream.seek(position, parser->tokenStream); - - // Update the end position of the parse node. - pn->pn_pos.end = tokenStream.currentToken().pos.end; } if (!addFreeVariablesFromLazyFunction(fun, pc)) diff --git a/js/src/jit-test/tests/debug/Script-getLineOffsets-07.js b/js/src/jit-test/tests/debug/Script-getLineOffsets-07.js deleted file mode 100644 index e7b82aa63d0..00000000000 --- a/js/src/jit-test/tests/debug/Script-getLineOffsets-07.js +++ /dev/null @@ -1,22 +0,0 @@ -// Lazy scripts should correctly report line offsets - -var g = newGlobal(); -var dbg = new Debugger(); - -g.eval("// Header comment\n" + // <- line 6 in this file - "\n" + - "\n" + - "function f(n) {\n" + // <- line 9 in this file - " var foo = '!';\n" + - "}"); - -dbg.addDebuggee(g); -var scripts = dbg.findScripts(); -var found = false; -for (var i = 0; i < scripts.length; i++) { - found = found || scripts[i].startLine == 6; - // Nothing should have offsets for the deffun on line 9 if lazy scripts - // correctly update the position. - assertEq(scripts[i].getLineOffsets(9).length, 0); -} -assertEq(found, true); diff --git a/js/src/jit-test/tests/debug/Script-startLine.js b/js/src/jit-test/tests/debug/Script-startLine.js index 4d3c4995010..9801230f6e9 100644 --- a/js/src/jit-test/tests/debug/Script-startLine.js +++ b/js/src/jit-test/tests/debug/Script-startLine.js @@ -8,11 +8,9 @@ dbg.onDebuggerStatement = function (frame) { assertEq(typeof frame.script.url, 'string'); }; -function test(f, manualCount) { +function test(f) { start = count = g.first = g.last = undefined; f(); - if (manualCount) - g.last = g.first + manualCount - 1; assertEq(start, g.first); assertEq(count, g.last + 1 - g.first); print(start, count); @@ -43,19 +41,3 @@ g.eval("function f2() {\n" + "}\n"); test(g.f2); test(g.f2); - -// Having a last = Error().lineNumber forces a setline srcnote, so test a -// function that ends with newline srcnotes. -g.eval("/* Any copyright is dedicated to the Public Domain.\n" + - " http://creativecommons.org/publicdomain/zero/1.0/ */\n" + - "\n" + - "function secondCall() { first = Error().lineNumber;\n" + - " debugger;\n" + - " // Comment\n" + - " eval(\"42;\");\n" + - " function foo() {}\n" + - " if (true) {\n" + - " foo();\n" + // <- this is +6 and must be within the extent - " }\n" + - "}"); -test(g.secondCall, 7); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 3384677b81f..3f11881ff48 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -251,9 +251,6 @@ BaselineCompiler::compile() bytecodeMap[script->nTypeSets] = 0; } - if (script->compartment()->debugMode()) - baselineScript->setDebugMode(); - return Method_Compiled; } diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 3ed6220bd45..1fe214a8e8a 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -746,10 +746,6 @@ BaselineScript::toggleDebugTraps(JSScript *script, jsbytecode *pc) { JS_ASSERT(script->baselineScript() == this); - // Only scripts compiled for debug mode have toggled calls. - if (!debugMode()) - return; - SrcNoteLineScanner scanner(script->notes(), script->lineno); JSRuntime *rt = script->runtimeFromMainThread(); diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index 473edf18711..d9c8960a4fc 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -132,11 +132,7 @@ struct BaselineScript // Flag set when the script contains any writes to its on-stack // (rather than call object stored) arguments. - MODIFIES_ARGUMENTS = 1 << 2, - - // Flag set when compiled for use for debug mode. Handles various - // Debugger hooks and compiles toggled calls for traps. - DEBUG_MODE = 1 << 3 + MODIFIES_ARGUMENTS = 1 << 2 }; private: @@ -205,13 +201,6 @@ struct BaselineScript return flags_ & MODIFIES_ARGUMENTS; } - void setDebugMode() { - flags_ |= DEBUG_MODE; - } - bool debugMode() const { - return flags_ & DEBUG_MODE; - } - uint32_t prologueOffset() const { return prologueOffset_; } diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index df1069bc449..7a960a84d1c 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2354,20 +2354,15 @@ InvalidateActivation(FreeOp *fop, uint8_t *ionTop, bool invalidateAll) IonSpew(IonSpew_Invalidate, "END invalidating activation"); } -static inline void -StopOffThreadCompilation(JSCompartment *comp) -{ - if (comp->jitCompartment()) { - CancelOffThreadIonCompile(comp, nullptr); - FinishAllOffThreadCompilations(comp->jitCompartment()); - } -} - void jit::InvalidateAll(FreeOp *fop, Zone *zone) { - for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) - StopOffThreadCompilation(comp); + for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) { + if (!comp->jitCompartment()) + continue; + CancelOffThreadIonCompile(comp, nullptr); + FinishAllOffThreadCompilations(comp->jitCompartment()); + } for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter) { if (iter.activation()->compartment()->zone() == zone) { @@ -2714,56 +2709,3 @@ jit::TraceIonScripts(JSTracer* trc, JSScript *script) if (script->hasBaselineScript()) jit::BaselineScript::Trace(trc, script->baselineScript()); } - -AutoDebugModeInvalidation::~AutoDebugModeInvalidation() -{ - MOZ_ASSERT(!!comp_ != !!zone_); - - if (needInvalidation_ == NoNeed) - return; - - // Invalidate the stack if any compartments toggled from on->off, because - // we allow scripts to be on stack when turning off debug mode. - bool invalidateStack = needInvalidation_ == ToggledOff; - Zone *zone = zone_ ? zone_ : comp_->zone(); - JSRuntime *rt = zone->runtimeFromMainThread(); - FreeOp *fop = rt->defaultFreeOp(); - - if (comp_) { - StopOffThreadCompilation(comp_); - } else { - for (CompartmentsInZoneIter comp(zone_); !comp.done(); comp.next()) - StopOffThreadCompilation(comp); - } - - if (invalidateStack) { - jit::MarkActiveBaselineScripts(zone); - - for (JitActivationIterator iter(rt); !iter.done(); ++iter) { - JSCompartment *comp = iter.activation()->compartment(); - if ((comp_ && comp_ == comp) || - (zone_ && zone_ == comp->zone() && comp->principals)) - { - IonContext ictx(rt); - AutoFlushCache afc("AutoDebugModeInvalidation", rt->jitRuntime()); - IonSpew(IonSpew_Invalidate, "Invalidating frames for debug mode toggle"); - InvalidateActivation(fop, iter.jitTop(), true); - } - } - } - - for (gc::CellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { - JSScript *script = i.get(); - if ((comp_ && script->compartment() == comp_) || - (zone_ && script->compartment()->principals)) - { - FinishInvalidation(fop, script); - FinishDiscardBaselineScript(fop, script); - script->clearAnalysis(); - script->resetUseCount(); - } else { - if (script->hasBaselineScript()) - script->baselineScript()->resetActive(); - } - } -} diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index e06b2183a2a..7b60b0ce1de 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -673,20 +673,20 @@ CreateLazyScriptsForCompartment(JSContext *cx) { AutoObjectVector lazyFunctions(cx); - // Find all live lazy scripts in the compartment, and via them all root - // lazy functions in the compartment: those which have not been compiled - // and which have a source object, indicating that their parent has been - // compiled. - for (gc::CellIter i(cx->zone(), gc::FINALIZE_LAZY_SCRIPT); !i.done(); i.next()) { - LazyScript *lazy = i.get(); - JSFunction *fun = lazy->function(); - if (fun->compartment() == cx->compartment() && - lazy->sourceObject() && !lazy->maybeScript()) - { - MOZ_ASSERT(fun->isInterpretedLazy()); - MOZ_ASSERT(lazy == fun->lazyScriptOrNull()); - if (!lazyFunctions.append(fun)) - return false; + // Find all root lazy functions in the compartment: those which have not been + // compiled and which have a source object, indicating that their parent has + // been compiled. + for (gc::CellIter i(cx->zone(), JSFunction::FinalizeKind); !i.done(); i.next()) { + JSObject *obj = i.get(); + if (obj->compartment() == cx->compartment() && obj->is()) { + JSFunction *fun = &obj->as(); + if (fun->isInterpretedLazy()) { + LazyScript *lazy = fun->lazyScriptOrNull(); + if (lazy && lazy->sourceObject() && !lazy->maybeScript()) { + if (!lazyFunctions.append(fun)) + return false; + } + } } } @@ -708,24 +708,27 @@ CreateLazyScriptsForCompartment(JSContext *cx) return false; } + // Repoint any clones of the original functions to their new script. + for (gc::CellIter i(cx->zone(), JSFunction::FinalizeKind); !i.done(); i.next()) { + JSObject *obj = i.get(); + if (obj->compartment() == cx->compartment() && obj->is()) { + JSFunction *fun = &obj->as(); + if (fun->isInterpretedLazy()) { + LazyScript *lazy = fun->lazyScriptOrNull(); + if (lazy && lazy->maybeScript()) + fun->existingScript(); + } + } + } + return true; } bool -JSCompartment::ensureDelazifyScriptsForDebugMode(JSContext *cx) -{ - MOZ_ASSERT(cx->compartment() == this); - if ((debugModeBits & DebugNeedDelazification) && !CreateLazyScriptsForCompartment(cx)) - return false; - debugModeBits &= ~DebugNeedDelazification; - return true; -} - -bool -JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidation &invalidate) +JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeGC &dmgc) { bool enabledBefore = debugMode(); - bool enabledAfter = (debugModeBits & DebugModeFromMask & ~DebugFromC) || b; + bool enabledAfter = (debugModeBits & ~unsigned(DebugFromC)) || b; // Debug mode can be enabled only when no scripts from the target // compartment are on the stack. It would even be incorrect to discard just @@ -744,12 +747,14 @@ JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidatio JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_IDLE); return false; } + if (enabledAfter && !CreateLazyScriptsForCompartment(cx)) + return false; } - debugModeBits = (debugModeBits & ~DebugFromC) | (b ? DebugFromC : 0); + debugModeBits = (debugModeBits & ~unsigned(DebugFromC)) | (b ? DebugFromC : 0); JS_ASSERT(debugMode() == enabledAfter); if (enabledBefore != enabledAfter) { - updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate); + updateForDebugMode(cx->runtime()->defaultFreeOp(), dmgc); if (!enabledAfter) DebugScopes::onCompartmentLeaveDebugMode(this); } @@ -757,7 +762,7 @@ JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidatio } void -JSCompartment::updateForDebugMode(FreeOp *fop, AutoDebugModeInvalidation &invalidate) +JSCompartment::updateForDebugMode(FreeOp *fop, AutoDebugModeGC &dmgc) { JSRuntime *rt = runtimeFromMainThread(); @@ -767,42 +772,52 @@ JSCompartment::updateForDebugMode(FreeOp *fop, AutoDebugModeInvalidation &invali } #ifdef JS_ION - MOZ_ASSERT(invalidate.isFor(this)); JS_ASSERT_IF(debugMode(), !hasScriptsOnStack()); - // Invalidate all JIT code since debug mode invalidates assumptions made - // by the JIT. + // When we change a compartment's debug mode, whether we're turning it + // on or off, we must always throw away all analyses: debug mode + // affects various aspects of the analysis, which then get baked into + // SSA results, which affects code generation in complicated ways. We + // must also throw away all JIT code, as its soundness depends on the + // analyses. // - // The AutoDebugModeInvalidation argument makes sure we can't forget to - // invalidate, but it is also important not to run any scripts in this - // compartment until the invalidate is destroyed. That is the caller's - // responsibility. - invalidate.scheduleInvalidation(debugMode()); + // It suffices to do a garbage collection cycle or to finish the + // ongoing GC cycle. The necessary cleanup happens in + // JSCompartment::sweep. + // + // dmgc makes sure we can't forget to GC, but it is also important not + // to run any scripts in this compartment until the dmgc is destroyed. + // That is the caller's responsibility. + if (!rt->isHeapBusy()) + dmgc.scheduleGC(zone()); #endif } bool JSCompartment::addDebuggee(JSContext *cx, js::GlobalObject *global) { - AutoDebugModeInvalidation invalidate(this); - return addDebuggee(cx, global, invalidate); + AutoDebugModeGC dmgc(cx->runtime()); + return addDebuggee(cx, global, dmgc); } bool JSCompartment::addDebuggee(JSContext *cx, GlobalObject *globalArg, - AutoDebugModeInvalidation &invalidate) + AutoDebugModeGC &dmgc) { Rooted global(cx, globalArg); bool wasEnabled = debugMode(); + if (!wasEnabled && !CreateLazyScriptsForCompartment(cx)) + return false; if (!debuggees.put(global)) { js_ReportOutOfMemory(cx); return false; } debugModeBits |= DebugFromJS; - if (!wasEnabled) - updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate); + if (!wasEnabled) { + updateForDebugMode(cx->runtime()->defaultFreeOp(), dmgc); + } return true; } @@ -811,14 +826,14 @@ JSCompartment::removeDebuggee(FreeOp *fop, js::GlobalObject *global, js::GlobalObjectSet::Enum *debuggeesEnum) { - AutoDebugModeInvalidation invalidate(this); - return removeDebuggee(fop, global, invalidate, debuggeesEnum); + AutoDebugModeGC dmgc(fop->runtime()); + return removeDebuggee(fop, global, dmgc, debuggeesEnum); } void JSCompartment::removeDebuggee(FreeOp *fop, js::GlobalObject *global, - AutoDebugModeInvalidation &invalidate, + AutoDebugModeGC &dmgc, js::GlobalObjectSet::Enum *debuggeesEnum) { bool wasEnabled = debugMode(); @@ -832,7 +847,7 @@ JSCompartment::removeDebuggee(FreeOp *fop, debugModeBits &= ~DebugFromJS; if (wasEnabled && !debugMode()) { DebugScopes::onCompartmentLeaveDebugMode(this); - updateForDebugMode(fop, invalidate); + updateForDebugMode(fop, dmgc); } } } diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index ccd2e9871cc..8ba61165eb9 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -110,7 +110,7 @@ struct TypeInferenceSizes; } namespace js { -class AutoDebugModeInvalidation; +class AutoDebugModeGC; class ArrayBufferObject; class DebugScopes; class WeakMapBase; @@ -278,13 +278,7 @@ struct JSCompartment js::WeakMapBase *gcWeakMapList; private: - enum { - DebugFromC = 1 << 0, - DebugFromJS = 1 << 1, - DebugNeedDelazification = 1 << 2 - }; - - static const unsigned DebugModeFromMask = DebugFromC | DebugFromJS; + enum { DebugFromC = 1, DebugFromJS = 2 }; unsigned debugModeBits; // see debugMode() below @@ -362,49 +356,27 @@ struct JSCompartment * by Debugger objects. Therefore debugModeBits has the DebugFromC bit set * if the C API wants debug mode and the DebugFromJS bit set if debuggees * is non-empty. - * - * When toggling on, DebugNeedDelazification is set to signal that - * Debugger methods which depend on seeing all scripts (like findScripts) - * need to delazify the scripts in the compartment first. */ - bool debugMode() const { - return !!(debugModeBits & DebugModeFromMask); - } + bool debugMode() const { return !!debugModeBits; } /* True if any scripts from this compartment are on the JS stack. */ bool hasScriptsOnStack(); - /* - * Schedule the compartment to be delazified. Called from - * LazyScript::Create. - */ - void scheduleDelazificationForDebugMode() { - debugModeBits |= DebugNeedDelazification; - } - - /* - * If we scheduled delazification for turning on debug mode, delazify all - * scripts. - */ - bool ensureDelazifyScriptsForDebugMode(JSContext *cx); - private: - /* This is called only when debugMode() has just toggled. */ - void updateForDebugMode(js::FreeOp *fop, js::AutoDebugModeInvalidation &invalidate); + void updateForDebugMode(js::FreeOp *fop, js::AutoDebugModeGC &dmgc); public: js::GlobalObjectSet &getDebuggees() { return debuggees; } bool addDebuggee(JSContext *cx, js::GlobalObject *global); bool addDebuggee(JSContext *cx, js::GlobalObject *global, - js::AutoDebugModeInvalidation &invalidate); + js::AutoDebugModeGC &dmgc); void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global, js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global, - js::AutoDebugModeInvalidation &invalidate, + js::AutoDebugModeGC &dmgc, js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); - bool setDebugModeFromC(JSContext *cx, bool b, - js::AutoDebugModeInvalidation &invalidate); + bool setDebugModeFromC(JSContext *cx, bool b, js::AutoDebugModeGC &dmgc); void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JSObject *handler); void clearTraps(js::FreeOp *fop); @@ -450,56 +422,29 @@ JSRuntime::isAtomsZone(JS::Zone *zone) } // For use when changing the debug mode flag on one or more compartments. -// Invalidate and discard JIT code since debug mode breaks JIT assumptions. +// Do not run scripts in any compartment that is scheduled for GC using this +// object. See comment in updateForDebugMode. // -// AutoDebugModeInvalidation has two modes: compartment or zone -// invalidation. While it is correct to always use compartment invalidation, -// if you know ahead of time you need to invalidate a whole zone, it is faster -// to invalidate the zone. -// -// Compartment invalidation only invalidates scripts belonging to that -// compartment. -// -// Zone invalidation invalidates all scripts belonging to non-special -// (i.e. those with principals) compartments of the zone. -// -// FIXME: Remove entirely once bug 716647 lands. -// -class js::AutoDebugModeInvalidation +class js::AutoDebugModeGC { - JSCompartment *comp_; - JS::Zone *zone_; - - enum { - NoNeed = 0, - ToggledOn = 1, - ToggledOff = 2 - } needInvalidation_; - + JSRuntime *rt; + bool needGC; public: - explicit AutoDebugModeInvalidation(JSCompartment *comp) - : comp_(comp), zone_(nullptr), needInvalidation_(NoNeed) - { } + explicit AutoDebugModeGC(JSRuntime *rt) : rt(rt), needGC(false) {} - explicit AutoDebugModeInvalidation(JS::Zone *zone) - : comp_(nullptr), zone_(zone), needInvalidation_(NoNeed) - { } - - ~AutoDebugModeInvalidation(); - - bool isFor(JSCompartment *comp) { - if (comp_) - return comp == comp_; - return comp->zone() == zone_; + ~AutoDebugModeGC() { + // Under some circumstances (say, in the midst of an animation), + // the garbage collector may try to retain JIT code and analyses. + // The DEBUG_MODE_GC reason forces the collector to always throw + // everything away, as required for debug mode transitions. + if (needGC) + GC(rt, GC_NORMAL, JS::gcreason::DEBUG_MODE_GC); } - void scheduleInvalidation(bool debugMode) { - // If we are scheduling invalidation for multiple compartments, they - // must all agree on the toggle. This is so we can decide if we need - // to invalidate on-stack scripts. - MOZ_ASSERT_IF(needInvalidation_ != NoNeed, - needInvalidation_ == debugMode ? ToggledOn : ToggledOff); - needInvalidation_ = debugMode ? ToggledOn : ToggledOff; + void scheduleGC(Zone *zone) { + JS_ASSERT(!rt->isHeapBusy()); + PrepareZoneForGC(zone); + needGC = true; } }; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index aca2a867140..31bb66112d1 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4620,8 +4620,13 @@ ShouldCleanUpEverything(JSRuntime *rt, JS::gcreason::Reason reason, JSGCInvocati // During shutdown, we must clean everything up, for the sake of leak // detection. When a runtime has no contexts, or we're doing a GC before a // shutdown CC, those are strong indications that we're shutting down. + // + // DEBUG_MODE_GC indicates we're discarding code because the debug mode + // has changed; debug mode affects the results of bytecode analysis, so + // we need to clear everything away. return reason == JS::gcreason::DESTROY_RUNTIME || reason == JS::gcreason::SHUTDOWN_CC || + reason == JS::gcreason::DEBUG_MODE_GC || gckind == GC_SHRINK; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 9cad05fa329..b326f8db75f 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -2204,19 +2204,29 @@ JS_FRIEND_API(unsigned) js_GetScriptLineExtent(JSScript *script) { unsigned lineno = script->lineno; - unsigned maxLineNo = lineno; + unsigned maxLineNo = 0; + bool counting = true; for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { SrcNoteType type = (SrcNoteType) SN_TYPE(sn); - if (type == SRC_SETLINE) + if (type == SRC_SETLINE) { + if (maxLineNo < lineno) + maxLineNo = lineno; lineno = (unsigned) js_GetSrcNoteOffset(sn, 0); - else if (type == SRC_NEWLINE) - lineno++; - - if (maxLineNo < lineno) - maxLineNo = lineno; + counting = true; + if (maxLineNo < lineno) + maxLineNo = lineno; + else + counting = false; + } else if (type == SRC_NEWLINE) { + if (counting) + lineno++; + } } - return 1 + maxLineNo - script->lineno; + if (maxLineNo > lineno) + lineno = maxLineNo; + + return 1 + lineno - script->lineno; } void @@ -2994,8 +3004,6 @@ LazyScript::Create(ExclusiveContext *cx, HandleFunction fun, if (!res) return nullptr; - cx->compartment()->scheduleDelazificationForDebugMode(); - return new (res) LazyScript(fun, table, numFreeVariables, numInnerFunctions, version, begin, end, lineno, column); } diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 86390af428d..65e2c5cc2e9 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -97,25 +97,6 @@ ReportMoreArgsNeeded(JSContext *cx, const char *name, unsigned required) return false; } -static inline bool -EnsureFunctionHasScript(JSContext *cx, JSFunction *fun) -{ - if (fun->isInterpretedLazy()) { - AutoCompartment ac(cx, fun); - return !!fun->getOrCreateScript(cx); - } - return true; -} - -static inline JSScript * -GetOrCreateFunctionScript(JSContext *cx, JSFunction *fun) -{ - MOZ_ASSERT(fun->isInterpreted()); - if (!EnsureFunctionHasScript(cx, fun)) - return nullptr; - return fun->nonLazyScript(); -} - #define REQUIRE_ARGC(name, n) \ JS_BEGIN_MACRO \ if (argc < (n)) \ @@ -705,9 +686,6 @@ Debugger::wrapDebuggeeValue(JSContext *cx, MutableHandleValue vp) if (vp.isObject()) { RootedObject obj(cx, &vp.toObject()); - if (obj->is() && !EnsureFunctionHasScript(cx, &obj->as())) - return false; - ObjectWeakMap::AddPtr p = objects.lookupForAdd(obj); if (p) { vp.setObject(*p->value); @@ -1145,8 +1123,6 @@ Debugger::slowPathOnNewScript(JSContext *cx, HandleScript script, GlobalObject * JSTrapStatus Debugger::onTrap(JSContext *cx, MutableHandleValue vp) { - MOZ_ASSERT(cx->compartment()->debugMode()); - ScriptFrameIter iter(cx); RootedScript script(cx, iter.script()); Rooted scriptGlobal(cx, &script->global()); @@ -1973,20 +1949,16 @@ bool Debugger::addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp) { THIS_DEBUGGER(cx, argc, vp, "addAllGlobalsAsDebuggees", args, dbg); - for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) { - // Invalidate a zone at a time to avoid doing a zone-wide CellIter - // per compartment. - AutoDebugModeInvalidation invalidate(zone); - for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) { - if (c == dbg->object->compartment() || c->options().invisibleToDebugger()) - continue; - c->zone()->scheduledForDestruction = false; - GlobalObject *global = c->maybeGlobal(); - if (global) { - Rooted rg(cx, global); - if (!dbg->addDebuggeeGlobal(cx, rg, invalidate)) - return false; - } + AutoDebugModeGC dmgc(cx->runtime()); + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { + if (c == dbg->object->compartment() || c->options().invisibleToDebugger()) + continue; + c->zone()->scheduledForDestruction = false; + GlobalObject *global = c->maybeGlobal(); + if (global) { + Rooted rg(cx, global); + if (!dbg->addDebuggeeGlobal(cx, rg, dmgc)) + return false; } } @@ -2012,9 +1984,9 @@ bool Debugger::removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp) { THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg); + AutoDebugModeGC dmgc(cx->runtime()); for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) - dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), e.front(), nullptr, &e); - + dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), e.front(), dmgc, nullptr, &e); args.rval().setUndefined(); return true; } @@ -2145,14 +2117,14 @@ Debugger::construct(JSContext *cx, unsigned argc, Value *vp) bool Debugger::addDebuggeeGlobal(JSContext *cx, Handle global) { - AutoDebugModeInvalidation invalidate(global->compartment()); - return addDebuggeeGlobal(cx, global, invalidate); + AutoDebugModeGC dmgc(cx->runtime()); + return addDebuggeeGlobal(cx, global, dmgc); } bool Debugger::addDebuggeeGlobal(JSContext *cx, Handle global, - AutoDebugModeInvalidation &invalidate) + AutoDebugModeGC &dmgc) { if (debuggees.has(global)) return true; @@ -2218,7 +2190,7 @@ Debugger::addDebuggeeGlobal(JSContext *cx, } else { if (global->getDebuggers()->length() > 1) return true; - if (debuggeeCompartment->addDebuggee(cx, global, invalidate)) + if (debuggeeCompartment->addDebuggee(cx, global, dmgc)) return true; /* Maintain consistency on error. */ @@ -2235,13 +2207,13 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global, GlobalObjectSet::Enum *compartmentEnum, GlobalObjectSet::Enum *debugEnum) { - AutoDebugModeInvalidation invalidate(global->compartment()); - return removeDebuggeeGlobal(fop, global, invalidate, compartmentEnum, debugEnum); + AutoDebugModeGC dmgc(fop->runtime()); + return removeDebuggeeGlobal(fop, global, dmgc, compartmentEnum, debugEnum); } void Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global, - AutoDebugModeInvalidation &invalidate, + AutoDebugModeGC &dmgc, GlobalObjectSet::Enum *compartmentEnum, GlobalObjectSet::Enum *debugEnum) { @@ -2297,7 +2269,7 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global, * global cannot be rooted on the stack without a cx. */ if (v->empty()) - global->compartment()->removeDebuggee(fop, global, invalidate, compartmentEnum); + global->compartment()->removeDebuggee(fop, global, dmgc, compartmentEnum); } /* @@ -2425,14 +2397,10 @@ class Debugger::ScriptQuery { if (!prepareQuery()) return false; - JSCompartment *singletonComp = nullptr; - if (compartments.count() == 1) - singletonComp = compartments.all().front(); - /* Search each compartment for debuggee scripts. */ vector = v; oom = false; - IterateScripts(cx->runtime(), singletonComp, this, considerScript); + IterateScripts(cx->runtime(), nullptr, this, considerScript); if (oom) { js_ReportOutOfMemory(cx); return false; @@ -2502,21 +2470,10 @@ class Debugger::ScriptQuery { /* Indicates whether OOM has occurred while matching. */ bool oom; - bool addCompartment(JSCompartment *comp) { - { - // All scripts in the debuggee compartment must be visible, so - // delazify everything. - AutoCompartment ac(cx, comp); - if (!comp->ensureDelazifyScriptsForDebugMode(cx)) - return false; - } - return compartments.put(comp); - } - /* Arrange for this ScriptQuery to match only scripts that run in |global|. */ bool matchSingleGlobal(GlobalObject *global) { JS_ASSERT(compartments.count() == 0); - if (!addCompartment(global->compartment())) { + if (!compartments.put(global->compartment())) { js_ReportOutOfMemory(cx); return false; } @@ -2531,7 +2488,7 @@ class Debugger::ScriptQuery { JS_ASSERT(compartments.count() == 0); /* Build our compartment set from the debugger's set of debuggee globals. */ for (GlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) { - if (!addCompartment(r.front()->compartment())) { + if (!compartments.put(r.front()->compartment())) { js_ReportOutOfMemory(cx); return false; } @@ -2974,10 +2931,8 @@ DebuggerScript_getChildScripts(JSContext *cx, unsigned argc, Value *vp) for (uint32_t i = script->innerObjectsStart(); i < objects->length; i++) { obj = objects->vector[i]; if (obj->is()) { - fun = &obj->as(); - funScript = GetOrCreateFunctionScript(cx, fun); - if (!funScript) - return false; + fun = static_cast(obj.get()); + funScript = fun->nonLazyScript(); s = dbg->wrapScript(cx, funScript); if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s))) return false; @@ -4698,9 +4653,14 @@ DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp) result->ensureDenseInitializedLength(cx, 0, fun->nargs); if (fun->isInterpreted()) { - RootedScript script(cx, GetOrCreateFunctionScript(cx, fun)); - if (!script) - return false; + RootedScript script(cx); + + { + AutoCompartment ac(cx, fun); + script = fun->getOrCreateScript(cx); + if (!script) + return false; + } JS_ASSERT(fun->nargs == script->bindings.numArgs()); @@ -4742,9 +4702,15 @@ DebuggerObject_getScript(JSContext *cx, unsigned argc, Value *vp) return true; } - RootedScript script(cx, GetOrCreateFunctionScript(cx, fun)); - if (!script) - return false; + RootedScript script(cx); + + { + AutoCompartment ac(cx, obj); + + script = fun->getOrCreateScript(cx); + if (!script) + return false; + } /* Only hand out debuggee scripts. */ if (!dbg->observesScript(script)) { diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 336d4100706..f766e21ad1a 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -230,12 +230,12 @@ class Debugger : private mozilla::LinkedListElement bool addDebuggeeGlobal(JSContext *cx, Handle obj); bool addDebuggeeGlobal(JSContext *cx, Handle obj, - AutoDebugModeInvalidation &invalidate); + AutoDebugModeGC &dmgc); void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global, GlobalObjectSet::Enum *compartmentEnum, GlobalObjectSet::Enum *debugEnum); void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global, - AutoDebugModeInvalidation &invalidate, + AutoDebugModeGC &dmgc, GlobalObjectSet::Enum *compartmentEnum, GlobalObjectSet::Enum *debugEnum); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index c5e54ef2844..3aeeadb0e0e 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1412,55 +1412,52 @@ CASE(EnableInterruptsPseudoOpcode) moreInterrupts = true; } - if (cx->compartment()->debugMode()) { - JSInterruptHook hook = cx->runtime()->debugHooks.interruptHook; - if (hook || script->stepModeEnabled()) { - RootedValue rval(cx); - JSTrapStatus status = JSTRAP_CONTINUE; - if (hook) - status = hook(cx, script, REGS.pc, rval.address(), - cx->runtime()->debugHooks.interruptHookData); - if (status == JSTRAP_CONTINUE && script->stepModeEnabled()) - status = Debugger::onSingleStep(cx, &rval); - switch (status) { - case JSTRAP_ERROR: - goto error; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - REGS.fp()->setReturnValue(rval); - interpReturnOK = true; - goto forced_return; - case JSTRAP_THROW: - cx->setPendingException(rval); - goto error; - default:; - } - moreInterrupts = true; + JSInterruptHook hook = cx->runtime()->debugHooks.interruptHook; + if (hook || script->stepModeEnabled()) { + RootedValue rval(cx); + JSTrapStatus status = JSTRAP_CONTINUE; + if (hook) + status = hook(cx, script, REGS.pc, rval.address(), cx->runtime()->debugHooks.interruptHookData); + if (status == JSTRAP_CONTINUE && script->stepModeEnabled()) + status = Debugger::onSingleStep(cx, &rval); + switch (status) { + case JSTRAP_ERROR: + goto error; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + REGS.fp()->setReturnValue(rval); + interpReturnOK = true; + goto forced_return; + case JSTRAP_THROW: + cx->setPendingException(rval); + goto error; + default:; } + moreInterrupts = true; + } - if (script->hasAnyBreakpointsOrStepMode()) - moreInterrupts = true; + if (script->hasAnyBreakpointsOrStepMode()) + moreInterrupts = true; - if (script->hasBreakpointsAt(REGS.pc)) { - RootedValue rval(cx); - JSTrapStatus status = Debugger::onTrap(cx, &rval); - switch (status) { - case JSTRAP_ERROR: - goto error; - case JSTRAP_RETURN: - REGS.fp()->setReturnValue(rval); - interpReturnOK = true; - goto forced_return; - case JSTRAP_THROW: - cx->setPendingException(rval); - goto error; - default: - break; - } - JS_ASSERT(status == JSTRAP_CONTINUE); - JS_ASSERT(rval.isInt32() && rval.toInt32() == op); + if (script->hasBreakpointsAt(REGS.pc)) { + RootedValue rval(cx); + JSTrapStatus status = Debugger::onTrap(cx, &rval); + switch (status) { + case JSTRAP_ERROR: + goto error; + case JSTRAP_RETURN: + REGS.fp()->setReturnValue(rval); + interpReturnOK = true; + goto forced_return; + case JSTRAP_THROW: + cx->setPendingException(rval); + goto error; + default: + break; } + JS_ASSERT(status == JSTRAP_CONTINUE); + JS_ASSERT(rval.isInt32() && rval.toInt32() == op); } JS_ASSERT(activation.opMask() == EnableInterruptsPseudoOpcode); diff --git a/js/src/vm/OldDebugAPI.cpp b/js/src/vm/OldDebugAPI.cpp index 6c66524bb84..0e8f0e33cc0 100644 --- a/js/src/vm/OldDebugAPI.cpp +++ b/js/src/vm/OldDebugAPI.cpp @@ -170,16 +170,13 @@ js::DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc) JS_FRIEND_API(bool) JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug) { - for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) { - // Invalidate a zone at a time to avoid doing a zone-wide CellIter - // per compartment. - AutoDebugModeInvalidation invalidate(zone); - for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) { - // Ignore special compartments (atoms, JSD compartments) - if (c->principals) { - if (!c->setDebugModeFromC(cx, !!debug, invalidate)) - return false; - } + AutoDebugModeGC dmgc(cx->runtime()); + + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { + // Ignore special compartments (atoms, JSD compartments) + if (c->principals) { + if (!c->setDebugModeFromC(cx, !!debug, dmgc)) + return false; } } return true; @@ -188,8 +185,8 @@ JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug) JS_FRIEND_API(bool) JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, bool debug) { - AutoDebugModeInvalidation invalidate(comp); - return comp->setDebugModeFromC(cx, !!debug, invalidate); + AutoDebugModeGC dmgc(cx->runtime()); + return comp->setDebugModeFromC(cx, !!debug, dmgc); } static bool