Bug 934799 - Part 1: Lazify delazifying lazy scripts in debug mode. (r=jimb)

This commit is contained in:
Shu-yu Guo 2013-11-12 10:51:11 -08:00
Родитель d24c4271e0
Коммит 8a45a24b32
6 изменённых файлов: 110 добавлений и 59 удалений

Просмотреть файл

@ -141,8 +141,7 @@ CanLazilyParse(ExclusiveContext *cx, const ReadOnlyCompileOptions &options)
{ {
return options.canLazilyParse && return options.canLazilyParse &&
options.compileAndGo && options.compileAndGo &&
options.sourcePolicy == CompileOptions::SAVE_SOURCE && options.sourcePolicy == CompileOptions::SAVE_SOURCE;
!cx->compartment()->debugMode();
} }
void void

Просмотреть файл

@ -1788,7 +1788,9 @@ BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext *cx)
RootedFunction function(cx, script->function()); RootedFunction function(cx, script->function());
CallNewScriptHook(cx->asJSContext(), script, function); CallNewScriptHook(cx->asJSContext(), script, function);
if (!parent) { // 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) {
GlobalObject *compileAndGoGlobal = nullptr; GlobalObject *compileAndGoGlobal = nullptr;
if (script->compileAndGo) if (script->compileAndGo)
compileAndGoGlobal = &script->global(); compileAndGoGlobal = &script->global();

Просмотреть файл

@ -673,20 +673,20 @@ CreateLazyScriptsForCompartment(JSContext *cx)
{ {
AutoObjectVector lazyFunctions(cx); AutoObjectVector lazyFunctions(cx);
// Find all root lazy functions in the compartment: those which have not been // Find all live lazy scripts in the compartment, and via them all root
// compiled and which have a source object, indicating that their parent has // lazy functions in the compartment: those which have not been compiled
// been compiled. // and which have a source object, indicating that their parent has been
for (gc::CellIter i(cx->zone(), JSFunction::FinalizeKind); !i.done(); i.next()) { // compiled.
JSObject *obj = i.get<JSObject>(); for (gc::CellIter i(cx->zone(), gc::FINALIZE_LAZY_SCRIPT); !i.done(); i.next()) {
if (obj->compartment() == cx->compartment() && obj->is<JSFunction>()) { LazyScript *lazy = i.get<LazyScript>();
JSFunction *fun = &obj->as<JSFunction>(); JSFunction *fun = lazy->function();
if (fun->isInterpretedLazy()) { if (fun->compartment() == cx->compartment() &&
LazyScript *lazy = fun->lazyScriptOrNull(); lazy->sourceObject() && !lazy->maybeScript())
if (lazy && lazy->sourceObject() && !lazy->maybeScript()) { {
if (!lazyFunctions.append(fun)) MOZ_ASSERT(fun->isInterpretedLazy());
return false; MOZ_ASSERT(lazy == fun->lazyScriptOrNull());
} if (!lazyFunctions.append(fun))
} return false;
} }
} }
@ -708,19 +708,16 @@ CreateLazyScriptsForCompartment(JSContext *cx)
return false; return false;
} }
// Repoint any clones of the original functions to their new script. return true;
for (gc::CellIter i(cx->zone(), JSFunction::FinalizeKind); !i.done(); i.next()) { }
JSObject *obj = i.get<JSObject>();
if (obj->compartment() == cx->compartment() && obj->is<JSFunction>()) {
JSFunction *fun = &obj->as<JSFunction>();
if (fun->isInterpretedLazy()) {
LazyScript *lazy = fun->lazyScriptOrNull();
if (lazy && lazy->maybeScript())
fun->existingScript();
}
}
}
bool
JSCompartment::ensureDelazifyScriptsForDebugMode(JSContext *cx)
{
MOZ_ASSERT(cx->compartment() == this);
if ((debugModeBits & DebugNeedDelazification) && !CreateLazyScriptsForCompartment(cx))
return false;
debugModeBits &= ~DebugNeedDelazification;
return true; return true;
} }
@ -728,7 +725,7 @@ bool
JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidation &invalidate) JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidation &invalidate)
{ {
bool enabledBefore = debugMode(); bool enabledBefore = debugMode();
bool enabledAfter = (debugModeBits & ~unsigned(DebugFromC)) || b; bool enabledAfter = (debugModeBits & DebugModeFromMask & ~DebugFromC) || b;
// Debug mode can be enabled only when no scripts from the target // 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 // compartment are on the stack. It would even be incorrect to discard just
@ -747,11 +744,9 @@ JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidatio
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_IDLE); JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_IDLE);
return false; return false;
} }
if (enabledAfter && !CreateLazyScriptsForCompartment(cx))
return false;
} }
debugModeBits = (debugModeBits & ~unsigned(DebugFromC)) | (b ? DebugFromC : 0); debugModeBits = (debugModeBits & ~DebugFromC) | (b ? DebugFromC : 0);
JS_ASSERT(debugMode() == enabledAfter); JS_ASSERT(debugMode() == enabledAfter);
if (enabledBefore != enabledAfter) { if (enabledBefore != enabledAfter) {
updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate); updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate);
@ -801,8 +796,6 @@ JSCompartment::addDebuggee(JSContext *cx,
Rooted<GlobalObject*> global(cx, globalArg); Rooted<GlobalObject*> global(cx, globalArg);
bool wasEnabled = debugMode(); bool wasEnabled = debugMode();
if (!wasEnabled && !CreateLazyScriptsForCompartment(cx))
return false;
if (!debuggees.put(global)) { if (!debuggees.put(global)) {
js_ReportOutOfMemory(cx); js_ReportOutOfMemory(cx);
return false; return false;

Просмотреть файл

@ -278,7 +278,13 @@ struct JSCompartment
js::WeakMapBase *gcWeakMapList; js::WeakMapBase *gcWeakMapList;
private: private:
enum { DebugFromC = 1, DebugFromJS = 2 }; enum {
DebugFromC = 1 << 0,
DebugFromJS = 1 << 1,
DebugNeedDelazification = 1 << 2
};
static const unsigned DebugModeFromMask = DebugFromC | DebugFromJS;
unsigned debugModeBits; // see debugMode() below unsigned debugModeBits; // see debugMode() below
@ -356,13 +362,34 @@ struct JSCompartment
* by Debugger objects. Therefore debugModeBits has the DebugFromC bit set * by Debugger objects. Therefore debugModeBits has the DebugFromC bit set
* if the C API wants debug mode and the DebugFromJS bit set if debuggees * if the C API wants debug mode and the DebugFromJS bit set if debuggees
* is non-empty. * 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; } bool debugMode() const {
return !!(debugModeBits & DebugModeFromMask);
}
/* True if any scripts from this compartment are on the JS stack. */ /* True if any scripts from this compartment are on the JS stack. */
bool hasScriptsOnStack(); 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: private:
/* This is called only when debugMode() has just toggled. */ /* This is called only when debugMode() has just toggled. */
void updateForDebugMode(js::FreeOp *fop, js::AutoDebugModeInvalidation &invalidate); void updateForDebugMode(js::FreeOp *fop, js::AutoDebugModeInvalidation &invalidate);

Просмотреть файл

@ -3043,6 +3043,8 @@ LazyScript::Create(ExclusiveContext *cx, HandleFunction fun,
if (!res) if (!res)
return nullptr; return nullptr;
cx->compartment()->scheduleDelazificationForDebugMode();
return new (res) LazyScript(fun, table, numFreeVariables, numInnerFunctions, version, return new (res) LazyScript(fun, table, numFreeVariables, numInnerFunctions, version,
begin, end, lineno, column); begin, end, lineno, column);
} }

Просмотреть файл

@ -97,6 +97,25 @@ ReportMoreArgsNeeded(JSContext *cx, const char *name, unsigned required)
return false; 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) \ #define REQUIRE_ARGC(name, n) \
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \
if (argc < (n)) \ if (argc < (n)) \
@ -686,6 +705,9 @@ Debugger::wrapDebuggeeValue(JSContext *cx, MutableHandleValue vp)
if (vp.isObject()) { if (vp.isObject()) {
RootedObject obj(cx, &vp.toObject()); RootedObject obj(cx, &vp.toObject());
if (obj->is<JSFunction>() && !EnsureFunctionHasScript(cx, &obj->as<JSFunction>()))
return false;
ObjectWeakMap::AddPtr p = objects.lookupForAdd(obj); ObjectWeakMap::AddPtr p = objects.lookupForAdd(obj);
if (p) { if (p) {
vp.setObject(*p->value); vp.setObject(*p->value);
@ -2403,10 +2425,14 @@ class Debugger::ScriptQuery {
if (!prepareQuery()) if (!prepareQuery())
return false; return false;
JSCompartment *singletonComp = nullptr;
if (compartments.count() == 1)
singletonComp = compartments.all().front();
/* Search each compartment for debuggee scripts. */ /* Search each compartment for debuggee scripts. */
vector = v; vector = v;
oom = false; oom = false;
IterateScripts(cx->runtime(), nullptr, this, considerScript); IterateScripts(cx->runtime(), singletonComp, this, considerScript);
if (oom) { if (oom) {
js_ReportOutOfMemory(cx); js_ReportOutOfMemory(cx);
return false; return false;
@ -2476,10 +2502,21 @@ class Debugger::ScriptQuery {
/* Indicates whether OOM has occurred while matching. */ /* Indicates whether OOM has occurred while matching. */
bool oom; 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|. */ /* Arrange for this ScriptQuery to match only scripts that run in |global|. */
bool matchSingleGlobal(GlobalObject *global) { bool matchSingleGlobal(GlobalObject *global) {
JS_ASSERT(compartments.count() == 0); JS_ASSERT(compartments.count() == 0);
if (!compartments.put(global->compartment())) { if (!addCompartment(global->compartment())) {
js_ReportOutOfMemory(cx); js_ReportOutOfMemory(cx);
return false; return false;
} }
@ -2494,7 +2531,7 @@ class Debugger::ScriptQuery {
JS_ASSERT(compartments.count() == 0); JS_ASSERT(compartments.count() == 0);
/* Build our compartment set from the debugger's set of debuggee globals. */ /* Build our compartment set from the debugger's set of debuggee globals. */
for (GlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) { for (GlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) {
if (!compartments.put(r.front()->compartment())) { if (!addCompartment(r.front()->compartment())) {
js_ReportOutOfMemory(cx); js_ReportOutOfMemory(cx);
return false; return false;
} }
@ -2937,8 +2974,10 @@ DebuggerScript_getChildScripts(JSContext *cx, unsigned argc, Value *vp)
for (uint32_t i = script->innerObjectsStart(); i < objects->length; i++) { for (uint32_t i = script->innerObjectsStart(); i < objects->length; i++) {
obj = objects->vector[i]; obj = objects->vector[i];
if (obj->is<JSFunction>()) { if (obj->is<JSFunction>()) {
fun = static_cast<JSFunction *>(obj.get()); fun = &obj->as<JSFunction>();
funScript = fun->nonLazyScript(); funScript = GetOrCreateFunctionScript(cx, fun);
if (!funScript)
return false;
s = dbg->wrapScript(cx, funScript); s = dbg->wrapScript(cx, funScript);
if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s))) if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s)))
return false; return false;
@ -4659,14 +4698,9 @@ DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp)
result->ensureDenseInitializedLength(cx, 0, fun->nargs); result->ensureDenseInitializedLength(cx, 0, fun->nargs);
if (fun->isInterpreted()) { if (fun->isInterpreted()) {
RootedScript script(cx); RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
if (!script)
{ return false;
AutoCompartment ac(cx, fun);
script = fun->getOrCreateScript(cx);
if (!script)
return false;
}
JS_ASSERT(fun->nargs == script->bindings.numArgs()); JS_ASSERT(fun->nargs == script->bindings.numArgs());
@ -4708,15 +4742,9 @@ DebuggerObject_getScript(JSContext *cx, unsigned argc, Value *vp)
return true; return true;
} }
RootedScript script(cx); RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
if (!script)
{ return false;
AutoCompartment ac(cx, obj);
script = fun->getOrCreateScript(cx);
if (!script)
return false;
}
/* Only hand out debuggee scripts. */ /* Only hand out debuggee scripts. */
if (!dbg->observesScript(script)) { if (!dbg->observesScript(script)) {