Issue #2271 - Separate cloning of native and interpreted functions

Separate code paths make it easier to follow and specialize than a single
one-size-fits-all function.

Based-on: m-c 1405766, 1411954
This commit is contained in:
Martok 2023-07-13 02:37:07 +02:00 committed by roytam1
commit 3eb7729d04
7 changed files with 108 additions and 68 deletions

View file

@ -3530,9 +3530,6 @@ CreateNonSyntacticEnvironmentChain(JSContext* cx, AutoObjectVector& envChain,
static bool
IsFunctionCloneable(HandleFunction fun)
{
if (!fun->isInterpreted())
return true;
// If a function was compiled with non-global syntactic environments on
// the environment chain, we could have baked in EnvironmentCoordinates
// into the script. We cannot clone it without breaking the compiler's
@ -3570,6 +3567,11 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, Handle
return nullptr;
}
if (fun->isNative()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
return nullptr;
}
if (!IsFunctionCloneable(fun)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
return nullptr;
@ -3580,21 +3582,6 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, Handle
return nullptr;
}
if (IsAsmJSModule(fun)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
return nullptr;
}
if (IsWrappedAsyncFunction(fun)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
return nullptr;
}
if (IsWrappedAsyncGenerator(fun)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
return nullptr;
}
if (CanReuseScriptForClone(cx->compartment(), fun, env)) {
// If the script is to be reused, either the script can already handle
// non-syntactic scopes, or there is only the standard global lexical

View file

@ -50,6 +50,7 @@
#include "vm/StringBuffer.h"
#include "vm/WrapperObject.h"
#include "vm/Xdr.h"
#include "wasm/AsmJS.h"
#include "jsscriptinlines.h"
@ -2026,6 +2027,8 @@ bool
js::CanReuseScriptForClone(JSCompartment* compartment, HandleFunction fun,
HandleObject newParent)
{
MOZ_ASSERT(fun->isInterpreted());
if (compartment != fun->compartment() ||
fun->isSingleton() ||
ObjectGroup::useSingletonForClone(fun))
@ -2044,12 +2047,11 @@ js::CanReuseScriptForClone(JSCompartment* compartment, HandleFunction fun,
if (IsSyntacticEnvironment(newParent))
return true;
// We need to clone the script if we're interpreted and not already marked
// as having a non-syntactic scope. If we're lazy, go ahead and clone the
// script; see the big comment at the end of CopyScriptInternal for the
// explanation of what's going on there.
return !fun->isInterpreted() ||
(fun->hasScript() && fun->nonLazyScript()->hasNonSyntacticScope());
// We need to clone the script if we're not already marked as having a
// non-syntactic scope. If we're lazy, go ahead and clone the script; see
// the big comment at the end of CopyScriptInternal for the explanation of
// what's going on there.
return fun->hasScript() && fun->nonLazyScript()->hasNonSyntacticScope();
}
static inline JSFunction*
@ -2096,6 +2098,7 @@ js::CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject enc
HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
MOZ_ASSERT(fun->isInterpreted());
MOZ_ASSERT(!fun->isBoundFunction());
MOZ_ASSERT(CanReuseScriptForClone(cx->compartment(), fun, enclosingEnv));
@ -2106,13 +2109,12 @@ js::CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject enc
if (fun->hasScript()) {
clone->initScript(fun->nonLazyScript());
clone->initEnvironment(enclosingEnv);
} else if (fun->isInterpretedLazy()) {
} else {
MOZ_ASSERT(fun->isInterpretedLazy());
MOZ_ASSERT(fun->compartment() == clone->compartment());
LazyScript* lazy = fun->lazyScriptOrNull();
clone->initLazyScript(lazy);
clone->initEnvironment(enclosingEnv);
} else {
clone->initNative(fun->native(), fun->jitInfo());
}
/*
@ -2130,25 +2132,19 @@ js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject enclo
HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
MOZ_ASSERT(fun->isInterpreted());
MOZ_ASSERT(!fun->isBoundFunction());
JSScript::AutoDelazify funScript(cx);
if (fun->isInterpreted()) {
funScript = fun;
if (!funScript)
return nullptr;
}
JSScript::AutoDelazify funScript(cx, fun);
if (!funScript)
return nullptr;
RootedFunction clone(cx, NewFunctionClone(cx, fun, SingletonObject, allocKind, proto));
if (!clone)
return nullptr;
if (fun->hasScript()) {
clone->initScript(nullptr);
clone->initEnvironment(enclosingEnv);
} else {
clone->initNative(fun->native(), fun->jitInfo());
}
clone->initScript(nullptr);
clone->initEnvironment(enclosingEnv);
/*
* Across compartments or if we have to introduce a non-syntactic scope we
@ -2165,21 +2161,57 @@ js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject enclo
newScope->hasOnChain(ScopeKind::NonSyntactic));
#endif
if (clone->isInterpreted()) {
RootedScript script(cx, fun->nonLazyScript());
MOZ_ASSERT(script->compartment() == fun->compartment());
MOZ_ASSERT(cx->compartment() == clone->compartment(),
"Otherwise we could relazify clone below!");
RootedScript script(cx, fun->nonLazyScript());
MOZ_ASSERT(script->compartment() == fun->compartment());
MOZ_ASSERT(cx->compartment() == clone->compartment(),
"Otherwise we could relazify clone below!");
RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, newScope, clone, script));
if (!clonedScript)
return nullptr;
Debugger::onNewScript(cx, clonedScript);
}
RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, newScope, clone, script));
if (!clonedScript)
return nullptr;
Debugger::onNewScript(cx, clonedScript);
return clone;
}
JSFunction*
js::CloneAsmJSModuleFunction(JSContext* cx, HandleFunction fun)
{
MOZ_ASSERT(fun->isNative());
MOZ_ASSERT(IsAsmJSModule(fun));
MOZ_ASSERT(fun->isExtended());
MOZ_ASSERT(cx->compartment() == fun->compartment());
JSFunction* clone = NewFunctionClone(cx, fun, GenericObject, AllocKind::FUNCTION_EXTENDED,
/* proto = */ nullptr);
if (!clone)
return nullptr;
MOZ_ASSERT(fun->native() == InstantiateAsmJS);
MOZ_ASSERT(!fun->jitInfo());
clone->initNative(InstantiateAsmJS, nullptr);
clone->setGroup(fun->group());
return clone;
}
JSFunction*
js::CloneSelfHostingIntrinsic(JSContext* cx, HandleFunction fun)
{
MOZ_ASSERT(fun->isNative());
MOZ_ASSERT(fun->compartment()->isSelfHosting);
MOZ_ASSERT(!fun->isExtended());
MOZ_ASSERT(cx->compartment() != fun->compartment());
JSFunction* clone = NewFunctionClone(cx, fun, SingletonObject, AllocKind::FUNCTION,
/* proto = */ nullptr);
if (!clone)
return nullptr;
clone->initNative(fun->native(), fun->jitInfo());
return clone;
}
/*
* Return an atom for use as the name of a builtin method with the given
* property id.

View file

@ -824,6 +824,12 @@ CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
gc::AllocKind kind = gc::AllocKind::FUNCTION,
HandleObject proto = nullptr);
extern JSFunction*
CloneAsmJSModuleFunction(JSContext* cx, HandleFunction fun);
extern JSFunction*
CloneSelfHostingIntrinsic(JSContext* cx, HandleFunction fun);
} // namespace js
inline js::FunctionExtended*

View file

@ -4302,7 +4302,13 @@ js::Lambda(JSContext* cx, HandleFunction fun, HandleObject parent)
{
MOZ_ASSERT(!fun->isArrow());
RootedObject clone(cx, CloneFunctionObjectIfNotSingleton(cx, fun, parent));
JSFunction* clone;
if (fun->isNative()) {
MOZ_ASSERT(IsAsmJSModule(fun));
clone = CloneAsmJSModuleFunction(cx, fun);
} else {
clone = CloneFunctionObjectIfNotSingleton(cx, fun, parent);
}
if (!clone)
return nullptr;

View file

@ -2909,23 +2909,29 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject)
RootedObject clone(cx);
if (selfHostedObject->is<JSFunction>()) {
RootedFunction selfHostedFunction(cx, &selfHostedObject->as<JSFunction>());
bool hasName = selfHostedFunction->explicitName() != nullptr;
if (selfHostedFunction->isInterpreted()) {
bool hasName = selfHostedFunction->explicitName() != nullptr;
// Arrow functions use the first extended slot for their lexical |this| value.
MOZ_ASSERT(!selfHostedFunction->isArrow());
js::gc::AllocKind kind = hasName
? gc::AllocKind::FUNCTION_EXTENDED
: selfHostedFunction->getAllocKind();
MOZ_ASSERT(!CanReuseScriptForClone(cx->compartment(), selfHostedFunction, cx->global()));
Rooted<LexicalEnvironmentObject*> globalLexical(cx, &cx->global()->lexicalEnvironment());
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
clone = CloneFunctionAndScript(cx, selfHostedFunction, globalLexical, emptyGlobalScope,
kind);
// To be able to re-lazify the cloned function, its name in the
// self-hosting compartment has to be stored on the clone.
if (clone && hasName) {
clone->as<JSFunction>().setExtendedSlot(LAZY_FUNCTION_NAME_SLOT,
StringValue(selfHostedFunction->explicitName()));
// Arrow functions use the first extended slot for their lexical |this| value.
MOZ_ASSERT(!selfHostedFunction->isArrow());
js::gc::AllocKind kind = hasName
? gc::AllocKind::FUNCTION_EXTENDED
: selfHostedFunction->getAllocKind();
Handle<GlobalObject*> global = cx->global();
Rooted<LexicalEnvironmentObject*> globalLexical(cx, &global->lexicalEnvironment());
RootedScope emptyGlobalScope(cx, &global->emptyGlobalScope());
MOZ_ASSERT(!CanReuseScriptForClone(cx->compartment(), selfHostedFunction, global));
clone = CloneFunctionAndScript(cx, selfHostedFunction, globalLexical, emptyGlobalScope,
kind);
// To be able to re-lazify the cloned function, its name in the
// self-hosting compartment has to be stored on the clone.
if (clone && hasName) {
clone->as<JSFunction>().setExtendedSlot(LAZY_FUNCTION_NAME_SLOT,
StringValue(selfHostedFunction->explicitName()));
}
} else {
clone = CloneSelfHostingIntrinsic(cx, selfHostedFunction);
}
} else if (selfHostedObject->is<RegExpObject>()) {
RegExpObject& reobj = selfHostedObject->as<RegExpObject>();

View file

@ -8100,8 +8100,8 @@ AsmJSModuleFunctionToModule(JSFunction* fun)
}
// Implements the semantics of an asm.js module function that has been successfully validated.
static bool
InstantiateAsmJS(JSContext* cx, unsigned argc, JS::Value* vp)
bool
js::InstantiateAsmJS(JSContext* cx, unsigned argc, JS::Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);

View file

@ -56,6 +56,9 @@ IsAsmJSFunction(JSFunction* fun);
extern bool
IsAsmJSStrictModeModuleOrFunction(JSFunction* fun);
extern bool
InstantiateAsmJS(JSContext* cx, unsigned argc, JS::Value* vp);
// asm.js testing natives:
extern bool