Bug 1620853 - Add BaseScript::allowRelazify to control relazification. r=jandem

Replace the DoNotRelazify flag with AllowRelazify. This flag is set during
delazification (self-hosted-cloning or normal) of a script if
isRelazifiable(). This avoids relying on if JSScript::lazyScript is non-null,
which is important because that field will be removed soon. We also inline
JSScript::canRelazify() into its caller.

Note that previous uses of the DoNotRelazify flag simply clear the new flag
and things work as expected, even if an associated LazyScript exists.

The XDR code is updated to rely on this AllowRelazify flag to decide if the
LazyScript should be transcoded. This gives more predictable behaviour
between the debugger and XDR.

Differential Revision: https://phabricator.services.mozilla.com/D65905

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Ted Campbell 2020-03-09 13:40:20 +00:00
Родитель 800061898b
Коммит f5d6c649a3
7 изменённых файлов: 66 добавлений и 49 удалений

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

@ -682,7 +682,7 @@ static bool RelazifyFunctions(JSContext* cx, unsigned argc, Value* vp) {
// Disable relazification of all scripts on stack. It is a pervasive
// assumption in the engine that running scripts still have bytecode.
for (AllScriptFramesIter i(cx); !i.done(); ++i) {
i.script()->setDoNotRelazify();
i.script()->clearAllowRelazify();
}
cx->runtime()->allowRelazificationForTesting = true;
@ -1122,8 +1122,7 @@ static bool IsRelazifiableFunction(JSContext* cx, unsigned argc, Value* vp) {
JSFunction* fun = &args[0].toObject().as<JSFunction>();
args.rval().setBoolean(fun->hasBytecode() &&
fun->nonLazyScript()->maybeLazyScript() &&
fun->nonLazyScript()->isRelazifiable());
fun->nonLazyScript()->allowRelazify());
return true;
}

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

@ -0,0 +1,22 @@
class WithConstructor {
constructor() {}
}
class DefaultConstructor {
}
class WithConstructorDerived extends DefaultConstructor {
constructor() {
super()
}
}
class DefaultConstructorDerived extends DefaultConstructor {
}
relazifyFunctions();
let a = new WithConstructor;
let b = new DefaultConstructor;
let c = new WithConstructorDerived;
let d = new DefaultConstructorDerived;

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

@ -1629,15 +1629,14 @@ static bool DelazifyCanonicalScriptedFunction(JSContext* cx,
// stored on the function again in case of re-lazification.
// Only functions without inner functions are re-lazified.
script->setLazyScript(lazy);
script->setAllowRelazify();
} else if (lazy->isWrappedByDebugger()) {
// Associate the LazyScript with the JSScript even though the latter
// can't be relazified. This is needed to ensure the Debugger can find
// the LazyScript when wrapping the JSScript. Mark the script as
// unrelazifiable so we don't get confused later.
// the LazyScript when wrapping the JSScript.
//
// See Debugger::wrapVariantReferent.
script->setLazyScript(lazy);
script->setDoNotRelazify(true);
}
// XDR the newly delazified function.
@ -1736,26 +1735,20 @@ void JSFunction::maybeRelazify(JSRuntime* rt) {
return;
}
// Don't relazify functions with JIT code.
// Check the script's eligibility.
JSScript* script = nonLazyScript();
if (!script->canRelazify()) {
if (!script->allowRelazify()) {
return;
}
MOZ_ASSERT(script->isRelazifiable());
// To delazify self-hosted builtins we need the name of the function
// to clone. This name is stored in the first extended slot.
if (isSelfHostedBuiltin() &&
(!isExtended() || !getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).isString())) {
// There must not be any JIT code attached since the relazification process
// does not know how to discard it. In general, the GC should discard most JIT
// code before attempting relazification.
if (script->hasJitScript()) {
return;
}
MOZ_ASSERT(!script->isAsync() && !script->isGenerator(),
"Generator resume code in the JITs assumes non-lazy function");
MOZ_ASSERT(!script->isDefaultClassConstructor(),
"default class constructors are built-in, but have their "
"self-hosted flag cleared");
BaseScript* lazy = script->maybeLazyScript();
if (lazy) {
u.scripted.s.script_ = lazy;

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

@ -1144,7 +1144,10 @@ XDRResult js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
if (!sourceObjectArg) {
xdrFlags |= OwnSource;
}
if (script->maybeLazyScript()) {
// If script allows relazifying back to a LazyScript we must save that lazy
// script. If the lazy script only existed for the Debugger, we do not
// preserve it.
if (script->allowRelazify()) {
xdrFlags |= HasLazyScript;
}
}
@ -1289,6 +1292,7 @@ XDRResult js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
if (mode == XDR_DECODE) {
script->setLazyScript(lazy);
script->setAllowRelazify();
}
}
@ -1407,6 +1411,11 @@ void JSScript::setDefaultClassConstructorSpan(
// Since this script has been changed to point into the user's source, we
// can clear its self-hosted flag, allowing Debugger to see it.
clearFlag(ImmutableFlags::SelfHosted);
// Even though the script was cloned from the self-hosted template, we cannot
// delazify back to a SelfHostedLazyScript. The script is no longer marked as
// SelfHosted either.
clearAllowRelazify();
}
bool JSScript::initScriptCounts(JSContext* cx) {
@ -5647,8 +5656,8 @@ void JSScript::AutoDelazify::holdScript(JS::HandleFunction fun) {
JSAutoRealm ar(cx_, fun);
script_ = JSFunction::getOrCreateScript(cx_, fun);
if (script_) {
oldDoNotRelazify_ = script_->hasFlag(MutableFlags::DoNotRelazify);
script_->setDoNotRelazify(true);
oldAllowRelazify_ = script_->allowRelazify();
script_->clearAllowRelazify();
}
}
}
@ -5658,7 +5667,7 @@ void JSScript::AutoDelazify::dropScript() {
// Don't touch script_ if it's in the self-hosting realm, see the comment
// in holdScript.
if (script_ && !script_->realm()->isSelfHostingRealm()) {
script_->setDoNotRelazify(oldDoNotRelazify_);
script_->setAllowRelazify(oldAllowRelazify_);
}
script_ = nullptr;
}

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

@ -2240,7 +2240,7 @@ setterLevel: \
// Access the flag for whether this script has a DebugScript in its realm's
// map. This should only be used by the DebugScript class.
MUTABLE_FLAG_GETTER_SETTER(hasDebugScript, HasDebugScript)
MUTABLE_FLAG_GETTER_SETTER(doNotRelazify, DoNotRelazify)
MUTABLE_FLAG_GETTER_SETTER(allowRelazify, AllowRelazify)
MUTABLE_FLAG_GETTER_SETTER(failedBoundsCheck, FailedBoundsCheck)
MUTABLE_FLAG_GETTER_SETTER(failedShapeGuard, FailedShapeGuard)
MUTABLE_FLAG_GETTER_SETTER(hadFrequentBailouts, HadFrequentBailouts)
@ -2702,26 +2702,12 @@ class JSScript : public js::BaseScript {
// inner-functions) should not be relazified as their Scopes may be part
// of another scope-chain.
// - Generators and async functions may be re-entered in complex ways so
// don't discard bytecode.
// don't discard bytecode. The JIT resume code assumes this.
// - Functions with template literals must always return the same object
// instance so must not discard it by relazifying.
return !hasInnerFunctions() && !hasDirectEval() && !isGenerator() &&
!isAsync() && !hasCallSiteObj();
}
bool canRelazify() const {
// In order to actually relazify we must satisfy additional runtime
// conditions:
// - The lazy form must still exist. This is either the original LazyScript
// or the self-hosted script that we cloned from.
// - There must not be any JIT code attached since the relazification
// process does not know how to discard it. In general, the GC should
// discard most JIT code before attempting relazification.
// - Specific subsystems (such as the Debugger) may disable scripts for
// their own reasons.
bool lazyAvailable = selfHosted() || u.lazyScript;
return isRelazifiable() && lazyAvailable && !hasJitScript() &&
!doNotRelazify();
}
void setLazyScript(js::LazyScript* lazy) { u.lazyScript = lazy; }
js::LazyScript* maybeLazyScript() { return u.lazyScript; }
@ -3059,11 +3045,11 @@ class JSScript : public js::BaseScript {
class AutoDelazify {
JS::RootedScript script_;
JSContext* cx_;
bool oldDoNotRelazify_;
bool oldAllowRelazify_ = false;
public:
explicit AutoDelazify(JSContext* cx, JS::HandleFunction fun = nullptr)
: script_(cx), cx_(cx), oldDoNotRelazify_(false) {
: script_(cx), cx_(cx) {
holdScript(fun);
}

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

@ -3111,10 +3111,21 @@ bool JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx,
sourceObject)) {
return false;
}
MOZ_ASSERT(targetFun->hasBytecode());
RootedScript targetScript(cx, targetFun->nonLazyScript());
// Relazifiable self-hosted function may be relazified later into a
// SelfHostedLazyScript. It is important to note that this only applies to
// named self-hosted entry points (that use this clone method). Inner
// functions clones used by self-hosted are never relazified, even if they
// would be able to in normal script.
if (targetScript->isRelazifiable()) {
targetScript->setAllowRelazify();
}
MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs());
MOZ_ASSERT(sourceScript->hasRest() == targetFun->baseScript()->hasRest());
MOZ_ASSERT(sourceScript->hasRest() == targetScript->hasRest());
MOZ_ASSERT(targetFun->strict(), "Self-hosted builtins must be strict");
// The target function might have been relazified after its flags changed.

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

@ -201,13 +201,10 @@ enum class MutableScriptFlagsEnum : uint32_t {
// Script has an entry in Realm::debugScriptMap.
HasDebugScript = 1 << 13,
// Do not relazify this script. This is used by the relazify() testing
// function for scripts that are on the stack and also by the AutoDelazify
// RAII class. Usually we don't relazify functions in compartments with
// scripts on the stack, but the relazify() testing function overrides that,
// and sometimes we're working with a cross-compartment function and need to
// keep it from relazifying.
DoNotRelazify = 1 << 14,
// Script has an associated LazyScript that may be used for relazification.
// This flag may be used to prevent relazification from happening.
// NOTE: Must check for isRelazifiable() before setting this flag.
AllowRelazify = 1 << 14,
// IonMonkey compilation hints.