Issue #1952 - m-c 1371593: Avoid repeated string atomizations when retrieving the unresolved name of a bound function

This commit is contained in:
Martok 2022-07-01 12:24:00 +02:00 • committed by roytam1
commit fc6c505da8
2 changed files with 40 additions and 14 deletions

View file

@ -1019,7 +1019,7 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
if (fun->explicitName()) { if (fun->explicitName()) {
if (!out.append(' ')) if (!out.append(' '))
return false; return false;
if (fun->isBoundFunction()) { if (fun->isBoundFunction() && !fun->hasBoundFunctionNamePrefix()) {
if (!out.append(cx->names().boundWithSpace)) if (!out.append(cx->names().boundWithSpace))
return false; return false;
} }
@ -1352,20 +1352,23 @@ JSFunction::getUnresolvedName(JSContext* cx, HandleFunction fun, MutableHandleAt
return true; return true;
} }
if (fun->isBoundFunction()) { if (fun->isBoundFunction() && !fun->hasBoundFunctionNamePrefix()) {
// Bound functions are never unnamed. // Bound functions are never unnamed.
MOZ_ASSERT(name); MOZ_ASSERT(name);
StringBuffer sb(cx); if (name->length() > 0) {
if (!sb.append(cx->names().boundWithSpace) || !sb.append(name)) StringBuffer sb(cx);
return false; if (!sb.append(cx->names().boundWithSpace) || !sb.append(name))
return false;
JSAtom* boundName = sb.finishAtom(); name = sb.finishAtom();
if (!boundName) if (!name)
return false; return false;
} else {
name = cx->names().boundWithSpace;
}
v.set(boundName); fun->setPrefixedBoundFunctionName(name);
return true;
} }
v.set(name != nullptr ? name : cx->names().empty); v.set(name != nullptr ? name : cx->names().empty);

View file

@ -59,6 +59,9 @@ class JSFunction : public js::NativeObject
BOUND_FUN = 0x0008, /* function was created with Function.prototype.bind. */ BOUND_FUN = 0x0008, /* function was created with Function.prototype.bind. */
HAS_GUESSED_ATOM = 0x0020, /* function had no explicit name, but a HAS_GUESSED_ATOM = 0x0020, /* function had no explicit name, but a
name was guessed for it anyway */ name was guessed for it anyway */
HAS_BOUND_FUNCTION_NAME_PREFIX = 0x0020, /* bound functions reuse the HAS_GUESSED_ATOM
flag to track if atom_ already contains the
"bound " function name prefix */
LAMBDA = 0x0040, /* function comes from a FunctionExpression, ArrowFunction, or LAMBDA = 0x0040, /* function comes from a FunctionExpression, ArrowFunction, or
Function() call (not a FunctionDeclaration or nonstandard Function() call (not a FunctionDeclaration or nonstandard
function-statement) */ function-statement) */
@ -99,8 +102,8 @@ class JSFunction : public js::NativeObject
INTERPRETED_GENERATOR_OR_ASYNC = INTERPRETED, INTERPRETED_GENERATOR_OR_ASYNC = INTERPRETED,
NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME, NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME,
STABLE_ACROSS_CLONES = CONSTRUCTOR | HAS_GUESSED_ATOM | LAMBDA | STABLE_ACROSS_CLONES = CONSTRUCTOR | LAMBDA | SELF_HOSTED | HAS_COMPILE_TIME_NAME |
SELF_HOSTED | HAS_COMPILE_TIME_NAME | FUNCTION_KIND_MASK FUNCTION_KIND_MASK
}; };
static_assert((INTERPRETED | INTERPRETED_LAZY) == js::JS_FUNCTION_INTERPRETED_BITS, static_assert((INTERPRETED | INTERPRETED_LAZY) == js::JS_FUNCTION_INTERPRETED_BITS,
@ -186,10 +189,20 @@ class JSFunction : public js::NativeObject
bool isAsmJSNative() const { return kind() == AsmJS; } bool isAsmJSNative() const { return kind() == AsmJS; }
/* Possible attributes of an interpreted function: */ /* Possible attributes of an interpreted function: */
bool hasCompileTimeName() const { return flags() & HAS_COMPILE_TIME_NAME; }
bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; }
bool isLambda() const { return flags() & LAMBDA; }
bool isBoundFunction() const { return flags() & BOUND_FUN; } bool isBoundFunction() const { return flags() & BOUND_FUN; }
bool hasCompileTimeName() const { return flags() & HAS_COMPILE_TIME_NAME; }
bool hasGuessedAtom() const {
static_assert(HAS_GUESSED_ATOM == HAS_BOUND_FUNCTION_NAME_PREFIX,
"HAS_GUESSED_ATOM is unused for bound functions");
return (flags() & (HAS_GUESSED_ATOM | BOUND_FUN)) == HAS_GUESSED_ATOM;
}
bool hasBoundFunctionNamePrefix() const {
static_assert(HAS_BOUND_FUNCTION_NAME_PREFIX == HAS_GUESSED_ATOM,
"HAS_BOUND_FUNCTION_NAME_PREFIX is only used for bound functions");
MOZ_ASSERT(isBoundFunction());
return flags() & HAS_BOUND_FUNCTION_NAME_PREFIX;
}
bool isLambda() const { return flags() & LAMBDA; }
bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; } bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; }
bool hasScript() const { return flags() & INTERPRETED; } bool hasScript() const { return flags() & INTERPRETED; }
@ -347,16 +360,26 @@ class JSFunction : public js::NativeObject
MOZ_ASSERT(atom); MOZ_ASSERT(atom);
MOZ_ASSERT(!hasCompileTimeName()); MOZ_ASSERT(!hasCompileTimeName());
MOZ_ASSERT(!hasGuessedAtom()); MOZ_ASSERT(!hasGuessedAtom());
MOZ_ASSERT(!isBoundFunction());
MOZ_ASSERT(js::AtomIsMarked(zone(), atom));
atom_ = atom; atom_ = atom;
flags_ |= HAS_GUESSED_ATOM; flags_ |= HAS_GUESSED_ATOM;
} }
void clearGuessedAtom() { void clearGuessedAtom() {
MOZ_ASSERT(hasGuessedAtom()); MOZ_ASSERT(hasGuessedAtom());
MOZ_ASSERT(!isBoundFunction());
MOZ_ASSERT(atom_); MOZ_ASSERT(atom_);
atom_ = nullptr; atom_ = nullptr;
flags_ &= ~HAS_GUESSED_ATOM; flags_ &= ~HAS_GUESSED_ATOM;
} }
void setPrefixedBoundFunctionName(JSAtom* atom) {
MOZ_ASSERT(!hasBoundFunctionNamePrefix());
MOZ_ASSERT(atom);
flags_ |= HAS_BOUND_FUNCTION_NAME_PREFIX;
atom_ = atom;
}
/* uint16_t representation bounds number of call object dynamic slots. */ /* uint16_t representation bounds number of call object dynamic slots. */
enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) }; enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };