Commit a9e47079 authored by caitp's avatar caitp Committed by Commit bot

[parser] improve inferred function names for async arrow functions

No longer include the "async" keyword, or an async arrow function's single
identifier parameter as part of its inferred name.

BUG=v8:5281, v8:4483
R=adamk@chromium.org, littledan@chromium.org, marja@chromium.org

Review-Url: https://codereview.chromium.org/2235423003
Cr-Commit-Position: refs/heads/master@{#38627}
parent 4e8ebeb0
...@@ -44,6 +44,11 @@ void FuncNameInferrer::PushVariableName(const AstRawString* name) { ...@@ -44,6 +44,11 @@ void FuncNameInferrer::PushVariableName(const AstRawString* name) {
} }
} }
void FuncNameInferrer::RemoveAsyncKeywordFromEnd() {
DCHECK(names_stack_.length() > 0);
DCHECK(names_stack_.last().name->IsOneByteEqualTo("async"));
names_stack_.RemoveLast();
}
const AstString* FuncNameInferrer::MakeNameFromStack() { const AstString* FuncNameInferrer::MakeNameFromStack() {
return MakeNameFromStackHelper(0, ast_value_factory_->empty_string()); return MakeNameFromStackHelper(0, ast_value_factory_->empty_string());
......
...@@ -16,6 +16,8 @@ class AstString; ...@@ -16,6 +16,8 @@ class AstString;
class AstValueFactory; class AstValueFactory;
class FunctionLiteral; class FunctionLiteral;
enum class InferName { Yes, No };
// FuncNameInferrer is a stateful class that is used to perform name // FuncNameInferrer is a stateful class that is used to perform name
// inference for anonymous functions during static analysis of source code. // inference for anonymous functions during static analysis of source code.
// Inference is performed in cases when an anonymous function is assigned // Inference is performed in cases when an anonymous function is assigned
...@@ -71,6 +73,8 @@ class FuncNameInferrer : public ZoneObject { ...@@ -71,6 +73,8 @@ class FuncNameInferrer : public ZoneObject {
} }
} }
void RemoveAsyncKeywordFromEnd();
// Infers a function name and leaves names collection state. // Infers a function name and leaves names collection state.
void Infer() { void Infer() {
DCHECK(IsOpen()); DCHECK(IsOpen());
......
...@@ -2321,12 +2321,17 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN, ...@@ -2321,12 +2321,17 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
accept_IN, &arrow_formals_classifier, CHECK_OK); accept_IN, &arrow_formals_classifier, CHECK_OK);
} }
if (is_async && peek_any_identifier() && PeekAhead() == Token::ARROW) { if (is_async && this->IsIdentifier(expression) && peek_any_identifier() &&
PeekAhead() == Token::ARROW) {
// async Identifier => AsyncConciseBody // async Identifier => AsyncConciseBody
IdentifierT name = IdentifierT name =
ParseAndClassifyIdentifier(&arrow_formals_classifier, CHECK_OK); ParseAndClassifyIdentifier(&arrow_formals_classifier, CHECK_OK);
expression = this->ExpressionFromIdentifier(name, position(), expression = this->ExpressionFromIdentifier(
scanner()->location().end_pos); name, position(), scanner()->location().end_pos, InferName::No);
if (fni_) {
// Remove `async` keyword from inferred name stack.
fni_->RemoveAsyncKeywordFromEnd();
}
} }
if (peek() == Token::ARROW) { if (peek() == Token::ARROW) {
...@@ -2882,10 +2887,13 @@ ParserBase<Traits>::ParseLeftHandSideExpression( ...@@ -2882,10 +2887,13 @@ ParserBase<Traits>::ParseLeftHandSideExpression(
} }
Scanner::Location spread_pos; Scanner::Location spread_pos;
typename Traits::Type::ExpressionList args; typename Traits::Type::ExpressionList args;
if (V8_UNLIKELY(is_async)) { if (V8_UNLIKELY(is_async && this->IsIdentifier(result))) {
ExpressionClassifier async_classifier(this); ExpressionClassifier async_classifier(this);
args = ParseArguments(&spread_pos, true, &async_classifier, CHECK_OK); args = ParseArguments(&spread_pos, true, &async_classifier, CHECK_OK);
if (peek() == Token::ARROW) { if (peek() == Token::ARROW) {
if (fni_) {
fni_->RemoveAsyncKeywordFromEnd();
}
ValidateBindingPattern(&async_classifier, CHECK_OK); ValidateBindingPattern(&async_classifier, CHECK_OK);
if (!async_classifier.is_valid_async_arrow_formal_parameters()) { if (!async_classifier.is_valid_async_arrow_formal_parameters()) {
ReportClassifierError( ReportClassifierError(
......
...@@ -766,8 +766,11 @@ Literal* ParserTraits::ExpressionFromLiteral(Token::Value token, int pos, ...@@ -766,8 +766,11 @@ Literal* ParserTraits::ExpressionFromLiteral(Token::Value token, int pos,
Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name, Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name,
int start_position, int start_position,
int end_position) { int end_position,
if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name); InferName infer) {
if (infer == InferName::Yes && parser_->fni_ != NULL) {
parser_->fni_->PushVariableName(name);
}
return parser_->NewUnresolved(name, start_position, end_position); return parser_->NewUnresolved(name, start_position, end_position);
} }
......
...@@ -557,7 +557,8 @@ class ParserTraits { ...@@ -557,7 +557,8 @@ class ParserTraits {
Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner, Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner,
AstNodeFactory* factory); AstNodeFactory* factory);
Expression* ExpressionFromIdentifier(const AstRawString* name, Expression* ExpressionFromIdentifier(const AstRawString* name,
int start_position, int end_position); int start_position, int end_position,
InferName = InferName::Yes);
Expression* ExpressionFromString(int pos, Scanner* scanner, Expression* ExpressionFromString(int pos, Scanner* scanner,
AstNodeFactory* factory); AstNodeFactory* factory);
Expression* GetIterator(Expression* iterable, AstNodeFactory* factory, Expression* GetIterator(Expression* iterable, AstNodeFactory* factory,
......
...@@ -830,9 +830,9 @@ class PreParserTraits { ...@@ -830,9 +830,9 @@ class PreParserTraits {
return PreParserExpression::Default(); return PreParserExpression::Default();
} }
static PreParserExpression ExpressionFromIdentifier(PreParserIdentifier name, static PreParserExpression ExpressionFromIdentifier(
int start_position, PreParserIdentifier name, int start_position, int end_position,
int end_position) { InferName = InferName::Yes) {
return PreParserExpression::FromIdentifier(name); return PreParserExpression::FromIdentifier(name);
} }
......
...@@ -7840,34 +7840,34 @@ TEST(AsyncAwaitErrors) { ...@@ -7840,34 +7840,34 @@ TEST(AsyncAwaitErrors) {
"async function foo() { function await() {} }", "async function foo() { function await() {} }",
// Henrique Ferreiro's bug (tm) // Henrique Ferreiro's bug (tm)
"(async function foo() { } foo => 1)", "(async function foo1() { } foo2 => 1)",
"(async function foo() { } () => 1)", "(async function foo3() { } () => 1)",
"(async function foo() { } => 1)", "(async function foo4() { } => 1)",
"(async function() { } foo => 1)", "(async function() { } foo5 => 1)",
"(async function() { } () => 1)", "(async function() { } () => 1)",
"(async function() { } => 1)", "(async function() { } => 1)",
"(async.foo => 1)", "(async.foo6 => 1)",
"(async.foo foo => 1)", "(async.foo7 foo8 => 1)",
"(async.foo () => 1)", "(async.foo9 () => 1)",
"(async().foo => 1)", "(async().foo10 => 1)",
"(async().foo foo => 1)", "(async().foo11 foo12 => 1)",
"(async().foo () => 1)", "(async().foo13 () => 1)",
"(async['foo'] => 1)", "(async['foo14'] => 1)",
"(async['foo'] foo => 1)", "(async['foo15'] foo16 => 1)",
"(async['foo'] () => 1)", "(async['foo17'] () => 1)",
"(async()['foo'] => 1)", "(async()['foo18'] => 1)",
"(async()['foo'] foo => 1)", "(async()['foo19'] foo20 => 1)",
"(async()['foo'] () => 1)", "(async()['foo21'] () => 1)",
"(async`foo` => 1)", "(async`foo22` => 1)",
"(async`foo` foo => 1)", "(async`foo23` foo24 => 1)",
"(async`foo` () => 1)", "(async`foo25` () => 1)",
"(async`foo`.bar => 1)", "(async`foo26`.bar27 => 1)",
"(async`foo`.bar foo => 1)", "(async`foo28`.bar29 foo30 => 1)",
"(async`foo`.bar () => 1)", "(async`foo31`.bar32 () => 1)",
// v8:5148 assert that errors are still thrown for calls that may have been // v8:5148 assert that errors are still thrown for calls that may have been
// async functions // async functions
"async({ foo = 1 })", "async({ foo33 = 1 })",
NULL NULL
}; };
......
...@@ -83,12 +83,30 @@ async function runTests() { ...@@ -83,12 +83,30 @@ async function runTests() {
try { await reject(); } catch (e) { throw new Error("FAIL"); } try { await reject(); } catch (e) { throw new Error("FAIL"); }
} }).c4, ["c4"]); } }).c4, ["c4"]);
// TODO(caitp): `async` probably shouldn't be the inferred name for async // TODO(caitp): We should infer anonymous async functions as the empty
// arrow functions... // string, not as the name of a function they're passed as a parameter to.
await test(async x => { throw new Error("FAIL") },
["test", "test", "runTests"]);
await test(async() => { throw new Error("FAIL") }, await test(async() => { throw new Error("FAIL") },
["async", "test", "runTests"]); ["test", "test", "runTests"]);
await test(async(a) => { throw new Error("FAIL") },
await test(async() => { await 1; throw new Error("FAIL") }, ["async"]); ["test", "test", "runTests"]);
await test(async(a, b) => { throw new Error("FAIL") },
["test", "test", "runTests"]);
await test(async x => { await 1; throw new Error("FAIL") }, ["test"]);
await test(async() => { await 1; throw new Error("FAIL") }, ["test"]);
await test(async(a) => { await 1; throw new Error("FAIL") }, ["test"]);
await test(async(a, b) => { await 1; throw new Error("FAIL") }, ["test"]);
await test(async x => {
await 1;
try {
await thrower();
} catch (e) {
throw new Error("FAIL");
}
}, ["test"]);
await test(async() => { await test(async() => {
await 1; await 1;
...@@ -97,7 +115,34 @@ async function runTests() { ...@@ -97,7 +115,34 @@ async function runTests() {
} catch (e) { } catch (e) {
throw new Error("FAIL"); throw new Error("FAIL");
} }
}, ["async"]); }, ["test"]);
await test(async(a) => {
await 1;
try {
await thrower();
} catch (e) {
throw new Error("FAIL");
}
}, ["test"]);
await test(async(a, b) => {
await 1;
try {
await thrower();
} catch (e) {
throw new Error("FAIL");
}
}, ["test"]);
await test(async x => {
await 1;
try {
await reject();
} catch (e) {
throw new Error("FAIL");
}
}, ["test"]);
await test(async() => { await test(async() => {
await 1; await 1;
...@@ -106,7 +151,25 @@ async function runTests() { ...@@ -106,7 +151,25 @@ async function runTests() {
} catch (e) { } catch (e) {
throw new Error("FAIL"); throw new Error("FAIL");
} }
}, ["async"]); }, ["test"]);
await test(async(a) => {
await 1;
try {
await reject();
} catch (e) {
throw new Error("FAIL");
}
}, ["test"]);
await test(async(a, b) => {
await 1;
try {
await reject();
} catch (e) {
throw new Error("FAIL");
}
}, ["test"]);
} }
runTests().catch(e => { runTests().catch(e => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment