Commit 5af4cd98 authored by littledan's avatar littledan Committed by Commit bot

Disallow tail calls from async functions and generators

Tail calls don't make sense from async functions and generators, as
each activation of these functions needs to make a new, distnict,
non-reused generator object. These tail calls are not required per
spec. This patch disables both syntactic and implicit tail calls
in async functions and generators.

R=neis
BUG=v8:5301,chromium:639270

Review-Url: https://codereview.chromium.org/2278413003
Cr-Commit-Position: refs/heads/master@{#38986}
parent 0f01a588
...@@ -2548,6 +2548,7 @@ ParserBase<Impl>::ParseTailCallExpression(ExpressionClassifier* classifier, ...@@ -2548,6 +2548,7 @@ ParserBase<Impl>::ParseTailCallExpression(ExpressionClassifier* classifier,
CheckNoTailCallExpressions(classifier, CHECK_OK); CheckNoTailCallExpressions(classifier, CHECK_OK);
Scanner::Location loc(pos, scanner()->location().end_pos); Scanner::Location loc(pos, scanner()->location().end_pos);
if (!expression->IsCall()) { if (!expression->IsCall()) {
Scanner::Location sub_loc(sub_expression_pos, loc.end_pos); Scanner::Location sub_loc(sub_expression_pos, loc.end_pos);
impl()->ReportMessageAt(sub_loc, impl()->ReportMessageAt(sub_loc,
...@@ -2567,6 +2568,12 @@ ParserBase<Impl>::ParseTailCallExpression(ExpressionClassifier* classifier, ...@@ -2567,6 +2568,12 @@ ParserBase<Impl>::ParseTailCallExpression(ExpressionClassifier* classifier,
*ok = false; *ok = false;
return impl()->EmptyExpression(); return impl()->EmptyExpression();
} }
if (is_resumable()) {
Scanner::Location sub_loc(sub_expression_pos, loc.end_pos);
impl()->ReportMessageAt(sub_loc, MessageTemplate::kUnexpectedTailCall);
*ok = false;
return impl()->EmptyExpression();
}
ReturnExprContext return_expr_context = ReturnExprContext return_expr_context =
function_state_->return_expr_context(); function_state_->return_expr_context();
if (return_expr_context != ReturnExprContext::kInsideValidReturnStatement) { if (return_expr_context != ReturnExprContext::kInsideValidReturnStatement) {
......
...@@ -2369,7 +2369,7 @@ Statement* Parser::ParseReturnStatement(bool* ok) { ...@@ -2369,7 +2369,7 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
function_state_, ReturnExprContext::kInsideValidReturnStatement); function_state_, ReturnExprContext::kInsideValidReturnStatement);
return_value = ParseExpression(true, CHECK_OK); return_value = ParseExpression(true, CHECK_OK);
if (allow_tailcalls() && !is_sloppy(language_mode())) { if (allow_tailcalls() && !is_sloppy(language_mode()) && !is_resumable()) {
// ES6 14.6.1 Static Semantics: IsInTailPosition // ES6 14.6.1 Static Semantics: IsInTailPosition
function_state_->AddImplicitTailCallExpression(return_value); function_state_->AddImplicitTailCallExpression(return_value);
} }
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-explicit-tailcalls
function f() {
return 1;
}
function* g() {
'use strict';
return continue f();
}
*%(basename)s:13: SyntaxError: Tail call expression is not allowed here
return continue f();
^^^
SyntaxError: Tail call expression is not allowed here
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-explicit-tailcalls // Flags: --allow-natives-syntax --harmony-explicit-tailcalls
// Flags: --harmony-do-expressions // Flags: --harmony-do-expressions --harmony-async-await
"use strict"; "use strict";
var SyntaxErrorTests = [ var SyntaxErrorTests = [
...@@ -128,7 +128,10 @@ var SyntaxErrorTests = [ ...@@ -128,7 +128,10 @@ var SyntaxErrorTests = [
err: ` ^^^^^^^^^^^^^^`, err: ` ^^^^^^^^^^^^^^`,
}, },
{ src: `()=>{ function* G() { yield continue foo(); } }`, { src: `()=>{ function* G() { yield continue foo(); } }`,
err: ` ^^^^^^^^^^^^^^`, err: ` ^^^^^`,
},
{ src: `()=>{ function* G() { return continue foo(); } }`,
err: ` ^^^^^`,
}, },
{ src: `()=>{ (1, 2, 3, continue f() ) => {} }`, { src: `()=>{ (1, 2, 3, continue f() ) => {} }`,
err: ` ^^^^^^^^^^^^`, err: ` ^^^^^^^^^^^^`,
...@@ -235,6 +238,9 @@ var SyntaxErrorTests = [ ...@@ -235,6 +238,9 @@ var SyntaxErrorTests = [
{ src: `class A extends continue f () {}; }`, { src: `class A extends continue f () {}; }`,
err: ` ^^^^^^^^^^^^^`, err: ` ^^^^^^^^^^^^^`,
}, },
{ src: `async() => continue foo()`,
err: ` ^^^^^`,
},
], ],
}, },
{ msg: "Tail call expression in try block", { msg: "Tail call expression in try block",
...@@ -311,7 +317,6 @@ var NoErrorTests = [ ...@@ -311,7 +317,6 @@ var NoErrorTests = [
`()=>{ return a || continue f() ; }`, `()=>{ return a || continue f() ; }`,
`()=>{ return a && continue f() ; }`, `()=>{ return a && continue f() ; }`,
`()=>{ return a , continue f() ; }`, `()=>{ return a , continue f() ; }`,
`()=>{ function* G() { return continue foo(); } }`,
`()=>{ class A { foo() { return continue super.f() ; } } }`, `()=>{ class A { foo() { return continue super.f() ; } } }`,
`()=>{ function B() { return continue new.target() ; } }`, `()=>{ function B() { return continue new.target() ; } }`,
`()=>{ return continue do { x ? foo() : bar() ; }() }`, `()=>{ return continue do { x ? foo() : bar() ; }() }`,
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --es-staging --ignition-staging --turbo
"use strict";
var g = (async () => { return JSON.stringify() });
g();
g();
%OptimizeFunctionOnNextCall(g);
g();
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