mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 934799 - Part 1: Lazify delazifying lazy scripts in debug mode. (r=jimb)
This commit is contained in:
parent
e3e688d737
commit
fa52de51e4
@ -141,8 +141,7 @@ CanLazilyParse(ExclusiveContext *cx, const ReadOnlyCompileOptions &options)
|
||||
{
|
||||
return options.canLazilyParse &&
|
||||
options.compileAndGo &&
|
||||
options.sourcePolicy == CompileOptions::SAVE_SOURCE &&
|
||||
!cx->compartment()->debugMode();
|
||||
options.sourcePolicy == CompileOptions::SAVE_SOURCE;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1774,7 +1774,9 @@ BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext *cx)
|
||||
|
||||
RootedFunction function(cx, 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;
|
||||
if (script->compileAndGo)
|
||||
compileAndGoGlobal = &script->global();
|
||||
|
@ -675,22 +675,22 @@ CreateLazyScriptsForCompartment(JSContext *cx)
|
||||
{
|
||||
AutoObjectVector lazyFunctions(cx);
|
||||
|
||||
// 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<JSObject>();
|
||||
if (obj->compartment() == cx->compartment() && obj->is<JSFunction>()) {
|
||||
JSFunction *fun = &obj->as<JSFunction>();
|
||||
if (fun->isInterpretedLazy()) {
|
||||
LazyScript *lazy = fun->lazyScriptOrNull();
|
||||
if (lazy && lazy->sourceObject() && !lazy->maybeScript()) {
|
||||
// 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<LazyScript>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create scripts for each lazy function, updating the list of functions to
|
||||
// process with any newly exposed inner functions in created scripts.
|
||||
@ -710,19 +710,16 @@ 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<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();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::ensureDelazifyScriptsForDebugMode(JSContext *cx)
|
||||
{
|
||||
MOZ_ASSERT(cx->compartment() == this);
|
||||
if ((debugModeBits & DebugNeedDelazification) && !CreateLazyScriptsForCompartment(cx))
|
||||
return false;
|
||||
debugModeBits &= ~DebugNeedDelazification;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -730,7 +727,7 @@ bool
|
||||
JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidation &invalidate)
|
||||
{
|
||||
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
|
||||
// compartment are on the stack. It would even be incorrect to discard just
|
||||
@ -749,11 +746,9 @@ 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 & ~unsigned(DebugFromC)) | (b ? DebugFromC : 0);
|
||||
debugModeBits = (debugModeBits & ~DebugFromC) | (b ? DebugFromC : 0);
|
||||
JS_ASSERT(debugMode() == enabledAfter);
|
||||
if (enabledBefore != enabledAfter) {
|
||||
updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate);
|
||||
@ -803,8 +798,6 @@ JSCompartment::addDebuggee(JSContext *cx,
|
||||
Rooted<GlobalObject*> global(cx, globalArg);
|
||||
|
||||
bool wasEnabled = debugMode();
|
||||
if (!wasEnabled && !CreateLazyScriptsForCompartment(cx))
|
||||
return false;
|
||||
if (!debuggees.put(global)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
|
@ -278,7 +278,13 @@ struct JSCompartment
|
||||
js::WeakMapBase *gcWeakMapList;
|
||||
|
||||
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
|
||||
|
||||
@ -356,13 +362,34 @@ 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; }
|
||||
bool debugMode() const {
|
||||
return !!(debugModeBits & DebugModeFromMask);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
|
@ -3110,6 +3110,8 @@ 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);
|
||||
}
|
||||
|
@ -98,6 +98,25 @@ 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)) \
|
||||
@ -688,6 +707,9 @@ Debugger::wrapDebuggeeValue(JSContext *cx, MutableHandleValue vp)
|
||||
if (vp.isObject()) {
|
||||
RootedObject obj(cx, &vp.toObject());
|
||||
|
||||
if (obj->is<JSFunction>() && !EnsureFunctionHasScript(cx, &obj->as<JSFunction>()))
|
||||
return false;
|
||||
|
||||
DependentAddPtr<ObjectWeakMap> p(cx, objects, obj);
|
||||
if (p) {
|
||||
vp.setObject(*p->value);
|
||||
@ -2406,10 +2428,14 @@ 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(), nullptr, this, considerScript);
|
||||
IterateScripts(cx->runtime(), singletonComp, this, considerScript);
|
||||
if (oom) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
@ -2479,10 +2505,21 @@ 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 (!compartments.put(global->compartment())) {
|
||||
if (!addCompartment(global->compartment())) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
@ -2497,7 +2534,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 (!compartments.put(r.front()->compartment())) {
|
||||
if (!addCompartment(r.front()->compartment())) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
@ -2939,8 +2976,10 @@ 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<JSFunction>()) {
|
||||
fun = static_cast<JSFunction *>(obj.get());
|
||||
funScript = fun->nonLazyScript();
|
||||
fun = &obj->as<JSFunction>();
|
||||
funScript = GetOrCreateFunctionScript(cx, fun);
|
||||
if (!funScript)
|
||||
return false;
|
||||
s = dbg->wrapScript(cx, funScript);
|
||||
if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s)))
|
||||
return false;
|
||||
@ -4685,14 +4724,9 @@ DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp)
|
||||
result->ensureDenseInitializedLength(cx, 0, fun->nargs);
|
||||
|
||||
if (fun->isInterpreted()) {
|
||||
RootedScript script(cx);
|
||||
|
||||
{
|
||||
AutoCompartment ac(cx, fun);
|
||||
script = fun->getOrCreateScript(cx);
|
||||
RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
|
||||
if (!script)
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_ASSERT(fun->nargs == script->bindings.numArgs());
|
||||
|
||||
@ -4734,15 +4768,9 @@ DebuggerObject_getScript(JSContext *cx, unsigned argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedScript script(cx);
|
||||
|
||||
{
|
||||
AutoCompartment ac(cx, obj);
|
||||
|
||||
script = fun->getOrCreateScript(cx);
|
||||
RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
|
||||
if (!script)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Only hand out debuggee scripts. */
|
||||
if (!dbg->observesScript(script)) {
|
||||
|
Loading…
Reference in New Issue
Block a user