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