Skip to content
Permalink
Browse files
[JSC] Align Function#name behavior with spec
https://bugs.webkit.org/show_bug.cgi?id=247725

Reviewed by Yusuke Suzuki.

This patch fixes two bugs in our Function#name implementation, which were reported in the same BZ issue
(each is due to misalignment between object literals and class bodies -- one issue in each direction):

1. `class C { async ['f']() {} }`
   Async class methods with computed names were given the function name "async",
   because we were storing the async identifier *anytime* we encountered it.

2. `{ 0: () => {} }`
   Object literal function properties with Number literal names were given an empty function name (as would
   be correct behavior for Array). Incidentally, BigInt literal names were already behaving properly here.

* JSTests/stress/function-name-property.js: Added.
* Source/JavaScriptCore/parser/Parser.cpp:
(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parseProperty):

Canonical link: https://commits.webkit.org/257114@main
  • Loading branch information
rkirsling committed Nov 29, 2022
1 parent 557946b commit c99e9b77dcb663545aed3758bacae744a6b3ecd9
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 5 deletions.
@@ -0,0 +1,24 @@
function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error(`expected ${expected} but got ${actual}`);
}

shouldBe(({ f: () => {} }).f.name, 'f');
shouldBe(({ f: function () {} }).f.name, 'f');
shouldBe(({ ['f']: () => {} }).f.name, 'f');
shouldBe(({ ['f']: function () {} }).f.name, 'f');
shouldBe(({ async f() {} }).f.name, 'f');
shouldBe(({ async ['f']() {} }).f.name, 'f');
shouldBe((class { f() {} }).prototype.f.name, 'f');
shouldBe((class { ['f']() {} }).prototype.f.name, 'f');
shouldBe((class { async f() {} }).prototype.f.name, 'f');
shouldBe((class { async ['f']() {} }).prototype.f.name, 'f');

shouldBe([() => {}][0].name, '');
shouldBe([function () {}][0].name, '');
shouldBe(({ 0: () => {} })[0].name, '0');
shouldBe(({ 0: function () {} })[0].name, '0');
shouldBe(({ [0]: () => {} })[0].name, '0');
shouldBe(({ [0]: function () {} })[0].name, '0');
shouldBe((class { 0() {} }).prototype[0].name, '0');
shouldBe((class { [0]() {} }).prototype[0].name, '0');
@@ -3067,11 +3067,12 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
case IDENT:
if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) {
if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) {
ident = m_token.m_data.ident;
next();
// We match SEMICOLON as a special case for a field called 'async' without initializer.
if (match(OPENPAREN) || match(COLON) || match(SEMICOLON) || match(EQUAL) || m_lexer->hasLineTerminatorBeforeToken())
if (match(OPENPAREN) || match(COLON) || match(SEMICOLON) || match(EQUAL) || m_lexer->hasLineTerminatorBeforeToken()) {
ident = &m_vm.propertyNames->async;
break;
}
if (UNLIKELY(consume(TIMES)))
parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
else
@@ -4598,12 +4599,11 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeB
}
case DOUBLE:
case INTEGER: {
double propertyName = m_token.m_data.doubleValue;
const Identifier& ident = m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM&>(m_vm), m_token.m_data.doubleValue);
next();

if (match(OPENPAREN)) {
SetForScope innerParseMode(m_parseMode, parseMode);
const Identifier& ident = m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM&>(m_vm), propertyName);
auto method = parsePropertyMethod(context, &ident);
propagateError();
return context.createProperty(&ident, method, PropertyNode::Constant, SuperBinding::Needed, InferName::Allowed, ClassElementTag::No);
@@ -4614,7 +4614,7 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeB
TreeExpression node = parseAssignmentExpression(context);
failIfFalse(node, "Cannot parse expression for property declaration");
context.setEndOffset(node, m_lexer->currentOffset());
return context.createProperty(const_cast<VM&>(m_vm), m_parserArena, propertyName, node, PropertyNode::Constant, SuperBinding::NotNeeded, ClassElementTag::No);
return context.createProperty(&ident, node, PropertyNode::Constant, SuperBinding::NotNeeded, InferName::Allowed, ClassElementTag::No);
}
case BIGINT: {
const Identifier* ident = m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast<VM&>(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix);

0 comments on commit c99e9b7

Please sign in to comment.