Issue #2271 - Use declared names of self-hosted functions for cloning

Ensure that cloning a self-hosted function always has access to the declared
name from the source and that it doesn't get lost on successive clones.
This is done by storing the declared name in an extended slot on rename, and
cloning it with the function.

Based-on: m-c 1546232
This commit is contained in:
Martok 2023-07-13 02:47:15 +02:00 • committed by roytam1
commit 76052fcda8
5 changed files with 62 additions and 47 deletions

View file

@ -52,11 +52,6 @@
// stored. // stored.
#define LAZY_FUNCTION_NAME_SLOT 0 #define LAZY_FUNCTION_NAME_SLOT 0
// The extended slot which contains a boolean value that indicates whether
// that the canonical name of the self-hosted builtins is set in self-hosted
// global. This slot is used only in debug build.
#define HAS_SELFHOSTED_CANONICAL_NAME_SLOT 0
// Stores the length for bound functions, so the .length property doesn't need // Stores the length for bound functions, so the .length property doesn't need
// to be resolved eagerly. // to be resolved eagerly.
#define BOUND_FUN_LENGTH_SLOT 1 #define BOUND_FUN_LENGTH_SLOT 1

View file

@ -2807,9 +2807,6 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
gc::AllocKind allocKind = gc::AllocKind::FUNCTION; gc::AllocKind allocKind = gc::AllocKind::FUNCTION;
JSFunction::Flags flags; JSFunction::Flags flags;
#ifdef DEBUG
bool isGlobalSelfHostedBuiltin = false;
#endif
switch (kind) { switch (kind) {
case FunctionSyntaxKind::Expression: case FunctionSyntaxKind::Expression:
flags = (generatorKind == NotGenerator && asyncKind == SyncFunction flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
@ -2846,12 +2843,9 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
break; break;
default: default:
MOZ_ASSERT(kind == FunctionSyntaxKind::Statement); MOZ_ASSERT(kind == FunctionSyntaxKind::Statement);
#ifdef DEBUG
if (options().selfHostingMode && !pc->isFunctionBox()) { if (options().selfHostingMode && !pc->isFunctionBox()) {
isGlobalSelfHostedBuiltin = true;
allocKind = gc::AllocKind::FUNCTION_EXTENDED; allocKind = gc::AllocKind::FUNCTION_EXTENDED;
} }
#endif
flags = (generatorKind == NotGenerator && asyncKind == SyncFunction flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
? JSFunction::INTERPRETED_NORMAL ? JSFunction::INTERPRETED_NORMAL
: JSFunction::INTERPRETED_GENERATOR_OR_ASYNC); : JSFunction::INTERPRETED_GENERATOR_OR_ASYNC);
@ -2867,10 +2861,6 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
return nullptr; return nullptr;
if (options().selfHostingMode) { if (options().selfHostingMode) {
fun->setIsSelfHostedBuiltin(); fun->setIsSelfHostedBuiltin();
#ifdef DEBUG
if (isGlobalSelfHostedBuiltin)
fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(false));
#endif
} }
return fun; return fun;
} }

View file

@ -1251,7 +1251,7 @@ JSFunction::infallibleIsDefaultClassConstructor(JSContext* cx) const
bool isDefault = false; bool isDefault = false;
if (isInterpretedLazy()) { if (isInterpretedLazy()) {
JSAtom* name = &getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->asAtom(); JSAtom* name = GetSelfHostedFunctionName(const_cast<JSFunction*>(this));
isDefault = name == cx->names().DefaultDerivedClassConstructor || isDefault = name == cx->names().DefaultDerivedClassConstructor ||
name == cx->names().DefaultBaseClassConstructor; name == cx->names().DefaultBaseClassConstructor;
} else { } else {
@ -1271,7 +1271,7 @@ JSFunction::isDerivedClassConstructor()
// There is only one plausible lazy self-hosted derived // There is only one plausible lazy self-hosted derived
// constructor. // constructor.
if (isSelfHostedBuiltin()) { if (isSelfHostedBuiltin()) {
JSAtom* name = &getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->asAtom(); JSAtom* name = GetSelfHostedFunctionName(this);
// This function is called from places without access to a // This function is called from places without access to a
// JSContext. Trace some plumbing to get what we want. // JSContext. Trace some plumbing to get what we want.
@ -1530,7 +1530,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
/* Lazily cloned self-hosted script. */ /* Lazily cloned self-hosted script. */
MOZ_ASSERT(fun->isSelfHostedBuiltin()); MOZ_ASSERT(fun->isSelfHostedBuiltin());
RootedAtom funAtom(cx, &fun->getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->asAtom()); RootedAtom funAtom(cx, GetSelfHostedFunctionName(fun));
if (!funAtom) if (!funAtom)
return false; return false;
Rooted<PropertyName*> funName(cx, funAtom->asPropertyName()); Rooted<PropertyName*> funName(cx, funAtom->asPropertyName());
@ -1569,9 +1569,7 @@ JSFunction::maybeRelazify(JSRuntime* rt)
return; return;
// To delazify self-hosted builtins we need the name of the function // To delazify self-hosted builtins we need the name of the function
// to clone. This name is stored in the first extended slot. Since // to clone. This name is stored in the first extended slot.
// that slot is sometimes also used for other purposes, make sure it
// contains a string.
if (isSelfHostedBuiltin() && if (isSelfHostedBuiltin() &&
(!isExtended() || !getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).isString())) (!isExtended() || !getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).isString()))
{ {
@ -1589,7 +1587,7 @@ JSFunction::maybeRelazify(JSRuntime* rt)
} else { } else {
MOZ_ASSERT(isSelfHostedBuiltin()); MOZ_ASSERT(isSelfHostedBuiltin());
MOZ_ASSERT(isExtended()); MOZ_ASSERT(isExtended());
MOZ_ASSERT(getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->isAtom()); MOZ_ASSERT(GetSelfHostedFunctionName(this));
} }
comp->scheduleDelazificationForDebugger(); comp->scheduleDelazificationForDebugger();

View file

@ -903,6 +903,22 @@ intrinsic_NewRegExpStringIterator(JSContext* cx, unsigned argc, Value* vp)
return true; return true;
} }
JSAtom*
js::GetSelfHostedFunctionName(JSFunction* fun)
{
Value name = fun->getExtendedSlot(LAZY_FUNCTION_NAME_SLOT);
if (!name.isString()) {
return nullptr;
}
return &name.toString()->asAtom();
}
static void
SetSelfHostedFunctionName(JSFunction* fun, JSAtom* name)
{
fun->setExtendedSlot(LAZY_FUNCTION_NAME_SLOT, StringValue(name));
}
static bool static bool
intrinsic_SetCanonicalName(JSContext* cx, unsigned argc, Value* vp) intrinsic_SetCanonicalName(JSContext* cx, unsigned argc, Value* vp)
{ {
@ -915,10 +931,18 @@ intrinsic_SetCanonicalName(JSContext* cx, unsigned argc, Value* vp)
if (!atom) if (!atom)
return false; return false;
// _SetCanonicalName can only be called on top-level function declarations.
MOZ_ASSERT(fun->kind() == JSFunction::NormalFunction);
MOZ_ASSERT(!fun->isLambda());
// It's an error to call _SetCanonicalName multiple times.
MOZ_ASSERT(!GetSelfHostedFunctionName(fun));
// Set the lazy function name so we can later retrieve the script from the
// self-hosting global.
SetSelfHostedFunctionName(fun, fun->explicitName());
fun->setAtom(atom); fun->setAtom(atom);
#ifdef DEBUG
fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(true));
#endif
args.rval().setUndefined(); args.rval().setUndefined();
return true; return true;
} }
@ -2910,13 +2934,11 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject)
if (selfHostedObject->is<JSFunction>()) { if (selfHostedObject->is<JSFunction>()) {
RootedFunction selfHostedFunction(cx, &selfHostedObject->as<JSFunction>()); RootedFunction selfHostedFunction(cx, &selfHostedObject->as<JSFunction>());
if (selfHostedFunction->isInterpreted()) { if (selfHostedFunction->isInterpreted()) {
bool hasName = selfHostedFunction->explicitName() != nullptr;
// Arrow functions use the first extended slot for their lexical |this| value. // Arrow functions use the first extended slot for their lexical |this| value.
MOZ_ASSERT(!selfHostedFunction->isArrow()); // And methods use the first extended slot for their home-object.
js::gc::AllocKind kind = hasName // We only expect to see normal functions here.
? gc::AllocKind::FUNCTION_EXTENDED MOZ_ASSERT(selfHostedFunction->kind() == JSFunction::NormalFunction);
: selfHostedFunction->getAllocKind(); js::gc::AllocKind kind = selfHostedFunction->getAllocKind();
Handle<GlobalObject*> global = cx->global(); Handle<GlobalObject*> global = cx->global();
Rooted<LexicalEnvironmentObject*> globalLexical(cx, &global->lexicalEnvironment()); Rooted<LexicalEnvironmentObject*> globalLexical(cx, &global->lexicalEnvironment());
@ -2925,10 +2947,16 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject)
clone = CloneFunctionAndScript(cx, selfHostedFunction, globalLexical, emptyGlobalScope, clone = CloneFunctionAndScript(cx, selfHostedFunction, globalLexical, emptyGlobalScope,
kind); kind);
// To be able to re-lazify the cloned function, its name in the // To be able to re-lazify the cloned function, its name in the
// self-hosting compartment has to be stored on the clone. // self-hosting compartment has to be stored on the clone. Re-lazification
if (clone && hasName) { // is only possible if this isn't a function expression.
clone->as<JSFunction>().setExtendedSlot(LAZY_FUNCTION_NAME_SLOT, if (clone && !selfHostedFunction->isLambda()) {
StringValue(selfHostedFunction->explicitName())); // If |_SetCanonicalName| was called on the function, the self-hosted
// name is stored in the extended slot.
JSAtom* name = GetSelfHostedFunctionName(selfHostedFunction);
if (!name) {
name = selfHostedFunction->explicitName();
}
SetSelfHostedFunctionName(&clone->as<JSFunction>(), name);
} }
} else { } else {
clone = CloneSelfHostingIntrinsic(cx, selfHostedFunction); clone = CloneSelfHostingIntrinsic(cx, selfHostedFunction);
@ -3016,7 +3044,7 @@ JSRuntime::createLazySelfHostedFunctionClone(JSContext* cx, HandlePropertyName s
if (!selfHostedFun->isClassConstructor() && !selfHostedFun->hasGuessedAtom() && if (!selfHostedFun->isClassConstructor() && !selfHostedFun->hasGuessedAtom() &&
selfHostedFun->explicitName() != selfHostedName) selfHostedFun->explicitName() != selfHostedName)
{ {
MOZ_ASSERT(selfHostedFun->getExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT).toBoolean()); MOZ_ASSERT(GetSelfHostedFunctionName(selfHostedFun) == selfHostedName);
funName = selfHostedFun->explicitName(); funName = selfHostedFun->explicitName();
} }
@ -3025,7 +3053,7 @@ JSRuntime::createLazySelfHostedFunctionClone(JSContext* cx, HandlePropertyName s
if (!fun) if (!fun)
return false; return false;
fun->setIsSelfHostedBuiltin(); fun->setIsSelfHostedBuiltin();
fun->setExtendedSlot(LAZY_FUNCTION_NAME_SLOT, StringValue(selfHostedName)); SetSelfHostedFunctionName(fun, selfHostedName);
return true; return true;
} }
@ -3111,7 +3139,7 @@ JSRuntime::assertSelfHostedFunctionHasCanonicalName(JSContext* cx, HandlePropert
#ifdef DEBUG #ifdef DEBUG
JSFunction* selfHostedFun = getUnclonedSelfHostedFunction(cx, name); JSFunction* selfHostedFun = getUnclonedSelfHostedFunction(cx, name);
MOZ_ASSERT(selfHostedFun); MOZ_ASSERT(selfHostedFun);
MOZ_ASSERT(selfHostedFun->getExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT).toBoolean()); MOZ_ASSERT(GetSelfHostedFunctionName(selfHostedFun) == name);
#endif #endif
} }
@ -3133,15 +3161,6 @@ js::IsSelfHostedFunctionWithName(JSFunction* fun, JSAtom* name)
return fun->isSelfHostedBuiltin() && GetSelfHostedFunctionName(fun) == name; return fun->isSelfHostedBuiltin() && GetSelfHostedFunctionName(fun) == name;
} }
JSAtom*
js::GetSelfHostedFunctionName(JSFunction* fun)
{
Value name = fun->getExtendedSlot(LAZY_FUNCTION_NAME_SLOT);
if (!name.isString())
return nullptr;
return &name.toString()->asAtom();
}
static_assert(JSString::MAX_LENGTH <= INT32_MAX, static_assert(JSString::MAX_LENGTH <= INT32_MAX,
"StringIteratorNext in builtin/String.js assumes the stored index " "StringIteratorNext in builtin/String.js assumes the stored index "
"into the string is an Int32Value"); "into the string is an Int32Value");

View file

@ -22,6 +22,19 @@ namespace js {
bool bool
IsSelfHostedFunctionWithName(JSFunction* fun, JSAtom* name); IsSelfHostedFunctionWithName(JSFunction* fun, JSAtom* name);
/*
* Returns the name of the function's binding in the self-hosted global.
*
* This returns a non-null value only when:
* * This is a top level function declaration in the self-hosted global.
* * And either:
* * This function is not cloned and `_SetCanonicalName` has been called to
* set a different function name.
* * This function is cloned.
*
* For functions not cloned and not `_SetCanonicalName`ed, use
* `fun->explicitName()` instead.
*/
JSAtom* JSAtom*
GetSelfHostedFunctionName(JSFunction* fun); GetSelfHostedFunctionName(JSFunction* fun);