[es8] Initial set of changes to support syntactic tail calls.
The syntax is "return continue expr;". BUG=v8:4915 LOG=Y Review URL: https://codereview.chromium.org/1917993004 Cr-Commit-Position: refs/heads/master@{#35799}
This commit is contained in:
parent
967a04634d
commit
ea2fbb7620
@ -423,6 +423,9 @@ class CallSite {
|
|||||||
"inside a block.") \
|
"inside a block.") \
|
||||||
T(StrictOctalLiteral, "Octal literals are not allowed in strict mode.") \
|
T(StrictOctalLiteral, "Octal literals are not allowed in strict mode.") \
|
||||||
T(StrictWith, "Strict mode code may not include a with statement") \
|
T(StrictWith, "Strict mode code may not include a with statement") \
|
||||||
|
T(TailCallInCatchBlock, \
|
||||||
|
"Tail call expression in catch block when finally block is also present.") \
|
||||||
|
T(TailCallInTryBlock, "Tail call expression in try block.") \
|
||||||
T(TemplateOctalLiteral, \
|
T(TemplateOctalLiteral, \
|
||||||
"Octal literals are not allowed in template strings.") \
|
"Octal literals are not allowed in template strings.") \
|
||||||
T(ThisFormalParameter, "'this' is not a valid formal parameter name") \
|
T(ThisFormalParameter, "'this' is not a valid formal parameter name") \
|
||||||
|
@ -190,6 +190,14 @@ class ParserBase : public Traits {
|
|||||||
Scope* scope;
|
Scope* scope;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TailCallExpression {
|
||||||
|
TailCallExpression(ExpressionT expression, int pos)
|
||||||
|
: expression(expression), pos(pos) {}
|
||||||
|
|
||||||
|
ExpressionT expression;
|
||||||
|
int pos;
|
||||||
|
};
|
||||||
|
|
||||||
class FunctionState BASE_EMBEDDED {
|
class FunctionState BASE_EMBEDDED {
|
||||||
public:
|
public:
|
||||||
FunctionState(FunctionState** function_state_stack, Scope** scope_stack,
|
FunctionState(FunctionState** function_state_stack, Scope** scope_stack,
|
||||||
@ -247,12 +255,12 @@ class ParserBase : public Traits {
|
|||||||
return destructuring_assignments_to_rewrite_;
|
return destructuring_assignments_to_rewrite_;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ExpressionT>& expressions_in_tail_position() {
|
List<TailCallExpression>& expressions_in_tail_position() {
|
||||||
return expressions_in_tail_position_;
|
return expressions_in_tail_position_;
|
||||||
}
|
}
|
||||||
void AddExpressionInTailPosition(ExpressionT expression) {
|
void AddExpressionInTailPosition(ExpressionT expression, int pos) {
|
||||||
if (collect_expressions_in_tail_position_) {
|
if (collect_expressions_in_tail_position_) {
|
||||||
expressions_in_tail_position_.Add(expression);
|
expressions_in_tail_position_.Add(TailCallExpression(expression, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +323,7 @@ class ParserBase : public Traits {
|
|||||||
Scope* outer_scope_;
|
Scope* outer_scope_;
|
||||||
|
|
||||||
List<DestructuringAssignment> destructuring_assignments_to_rewrite_;
|
List<DestructuringAssignment> destructuring_assignments_to_rewrite_;
|
||||||
List<ExpressionT> expressions_in_tail_position_;
|
List<TailCallExpression> expressions_in_tail_position_;
|
||||||
bool collect_expressions_in_tail_position_;
|
bool collect_expressions_in_tail_position_;
|
||||||
ZoneList<ExpressionT> non_patterns_to_rewrite_;
|
ZoneList<ExpressionT> non_patterns_to_rewrite_;
|
||||||
|
|
||||||
@ -334,6 +342,42 @@ class ParserBase : public Traits {
|
|||||||
friend class Checkpoint;
|
friend class Checkpoint;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This scope disables collecting of expressions at tail call position.
|
||||||
|
class DontCollectExpressionsInTailPositionScope {
|
||||||
|
public:
|
||||||
|
explicit DontCollectExpressionsInTailPositionScope(
|
||||||
|
FunctionState* function_state)
|
||||||
|
: function_state_(function_state),
|
||||||
|
old_value_(function_state->collect_expressions_in_tail_position()) {
|
||||||
|
function_state->set_collect_expressions_in_tail_position(false);
|
||||||
|
}
|
||||||
|
~DontCollectExpressionsInTailPositionScope() {
|
||||||
|
function_state_->set_collect_expressions_in_tail_position(old_value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FunctionState* function_state_;
|
||||||
|
bool old_value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Collects all return expressions at tail call position in this scope
|
||||||
|
// to a separate list.
|
||||||
|
class CollectExpressionsInTailPositionToListScope {
|
||||||
|
public:
|
||||||
|
CollectExpressionsInTailPositionToListScope(FunctionState* function_state,
|
||||||
|
List<TailCallExpression>* list)
|
||||||
|
: function_state_(function_state), list_(list) {
|
||||||
|
function_state->expressions_in_tail_position().Swap(list_);
|
||||||
|
}
|
||||||
|
~CollectExpressionsInTailPositionToListScope() {
|
||||||
|
function_state_->expressions_in_tail_position().Swap(list_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FunctionState* function_state_;
|
||||||
|
List<TailCallExpression>* list_;
|
||||||
|
};
|
||||||
|
|
||||||
// Annoyingly, arrow functions first parse as comma expressions, then when we
|
// Annoyingly, arrow functions first parse as comma expressions, then when we
|
||||||
// see the => we have to go back and reinterpret the arguments as being formal
|
// see the => we have to go back and reinterpret the arguments as being formal
|
||||||
// parameters. To do so we need to reset some of the parser state back to
|
// parameters. To do so we need to reset some of the parser state back to
|
||||||
@ -1893,6 +1937,7 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
|
|||||||
// ArrowFunction
|
// ArrowFunction
|
||||||
// YieldExpression
|
// YieldExpression
|
||||||
// LeftHandSideExpression AssignmentOperator AssignmentExpression
|
// LeftHandSideExpression AssignmentOperator AssignmentExpression
|
||||||
|
// TailCallExpression
|
||||||
bool is_destructuring_assignment = false;
|
bool is_destructuring_assignment = false;
|
||||||
int lhs_beg_pos = peek_position();
|
int lhs_beg_pos = peek_position();
|
||||||
|
|
||||||
@ -2860,6 +2905,22 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
|
|||||||
// Single-expression body
|
// Single-expression body
|
||||||
int pos = position();
|
int pos = position();
|
||||||
ExpressionClassifier classifier(this);
|
ExpressionClassifier classifier(this);
|
||||||
|
bool is_tail_call_expression;
|
||||||
|
if (FLAG_harmony_explicit_tailcalls) {
|
||||||
|
// TODO(ishell): update chapter number.
|
||||||
|
// ES8 XX.YY.ZZ
|
||||||
|
if (peek() == Token::CONTINUE) {
|
||||||
|
Consume(Token::CONTINUE);
|
||||||
|
pos = position();
|
||||||
|
is_tail_call_expression = true;
|
||||||
|
} else {
|
||||||
|
is_tail_call_expression = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
||||||
|
is_tail_call_expression =
|
||||||
|
allow_tailcalls() && !is_sloppy(language_mode());
|
||||||
|
}
|
||||||
ExpressionT expression =
|
ExpressionT expression =
|
||||||
ParseAssignmentExpression(accept_IN, &classifier, CHECK_OK);
|
ParseAssignmentExpression(accept_IN, &classifier, CHECK_OK);
|
||||||
Traits::RewriteNonPattern(&classifier, CHECK_OK);
|
Traits::RewriteNonPattern(&classifier, CHECK_OK);
|
||||||
@ -2868,8 +2929,7 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
|
|||||||
body->Add(factory()->NewReturnStatement(expression, pos), zone());
|
body->Add(factory()->NewReturnStatement(expression, pos), zone());
|
||||||
materialized_literal_count = function_state.materialized_literal_count();
|
materialized_literal_count = function_state.materialized_literal_count();
|
||||||
expected_property_count = function_state.expected_property_count();
|
expected_property_count = function_state.expected_property_count();
|
||||||
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
if (is_tail_call_expression) {
|
||||||
if (allow_tailcalls() && !is_sloppy(language_mode())) {
|
|
||||||
this->MarkTailPosition(expression);
|
this->MarkTailPosition(expression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2573,6 +2573,13 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
|
|||||||
function_state_->set_return_location(loc);
|
function_state_->set_return_location(loc);
|
||||||
|
|
||||||
Token::Value tok = peek();
|
Token::Value tok = peek();
|
||||||
|
int tail_call_position = -1;
|
||||||
|
if (FLAG_harmony_explicit_tailcalls && tok == Token::CONTINUE) {
|
||||||
|
Consume(Token::CONTINUE);
|
||||||
|
tail_call_position = position();
|
||||||
|
tok = peek();
|
||||||
|
}
|
||||||
|
|
||||||
Statement* result;
|
Statement* result;
|
||||||
Expression* return_value;
|
Expression* return_value;
|
||||||
if (scanner()->HasAnyLineTerminatorBeforeNext() ||
|
if (scanner()->HasAnyLineTerminatorBeforeNext() ||
|
||||||
@ -2629,9 +2636,21 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
|
|||||||
is_object_conditional, pos);
|
is_object_conditional, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
// TODO(ishell): update chapter number.
|
||||||
if (allow_tailcalls() && !is_sloppy(language_mode())) {
|
// ES8 XX.YY.ZZ
|
||||||
function_state_->AddExpressionInTailPosition(return_value);
|
if (tail_call_position >= 0) {
|
||||||
|
if (!function_state_->collect_expressions_in_tail_position()) {
|
||||||
|
Scanner::Location loc(tail_call_position, tail_call_position + 1);
|
||||||
|
ReportMessageAt(loc, MessageTemplate::kTailCallInTryBlock);
|
||||||
|
*ok = false;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
function_state_->AddExpressionInTailPosition(return_value,
|
||||||
|
tail_call_position);
|
||||||
|
|
||||||
|
} else if (allow_tailcalls() && !is_sloppy(language_mode())) {
|
||||||
|
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
||||||
|
function_state_->AddExpressionInTailPosition(return_value, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExpectSemicolon(CHECK_OK);
|
ExpectSemicolon(CHECK_OK);
|
||||||
@ -2811,40 +2830,6 @@ Statement* Parser::ParseThrowStatement(bool* ok) {
|
|||||||
factory()->NewThrow(exception, pos), pos);
|
factory()->NewThrow(exception, pos), pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Parser::DontCollectExpressionsInTailPositionScope {
|
|
||||||
public:
|
|
||||||
DontCollectExpressionsInTailPositionScope(
|
|
||||||
Parser::FunctionState* function_state)
|
|
||||||
: function_state_(function_state),
|
|
||||||
old_value_(function_state->collect_expressions_in_tail_position()) {
|
|
||||||
function_state->set_collect_expressions_in_tail_position(false);
|
|
||||||
}
|
|
||||||
~DontCollectExpressionsInTailPositionScope() {
|
|
||||||
function_state_->set_collect_expressions_in_tail_position(old_value_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Parser::FunctionState* function_state_;
|
|
||||||
bool old_value_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Collects all return expressions at tail call position in this scope
|
|
||||||
// to a separate list.
|
|
||||||
class Parser::CollectExpressionsInTailPositionToListScope {
|
|
||||||
public:
|
|
||||||
CollectExpressionsInTailPositionToListScope(
|
|
||||||
Parser::FunctionState* function_state, List<Expression*>* list)
|
|
||||||
: function_state_(function_state), list_(list) {
|
|
||||||
function_state->expressions_in_tail_position().Swap(list_);
|
|
||||||
}
|
|
||||||
~CollectExpressionsInTailPositionToListScope() {
|
|
||||||
function_state_->expressions_in_tail_position().Swap(list_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Parser::FunctionState* function_state_;
|
|
||||||
List<Expression*>* list_;
|
|
||||||
};
|
|
||||||
|
|
||||||
TryStatement* Parser::ParseTryStatement(bool* ok) {
|
TryStatement* Parser::ParseTryStatement(bool* ok) {
|
||||||
// TryStatement ::
|
// TryStatement ::
|
||||||
@ -2877,7 +2862,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
|
|||||||
Scope* catch_scope = NULL;
|
Scope* catch_scope = NULL;
|
||||||
Variable* catch_variable = NULL;
|
Variable* catch_variable = NULL;
|
||||||
Block* catch_block = NULL;
|
Block* catch_block = NULL;
|
||||||
List<Expression*> expressions_in_tail_position_in_catch_block;
|
List<TailCallExpression> expressions_in_tail_position_in_catch_block;
|
||||||
if (tok == Token::CATCH) {
|
if (tok == Token::CATCH) {
|
||||||
Consume(Token::CATCH);
|
Consume(Token::CATCH);
|
||||||
|
|
||||||
@ -2993,6 +2978,16 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
|
|||||||
result = factory()->NewTryCatchStatement(try_block, catch_scope,
|
result = factory()->NewTryCatchStatement(try_block, catch_scope,
|
||||||
catch_variable, catch_block, pos);
|
catch_variable, catch_block, pos);
|
||||||
} else {
|
} else {
|
||||||
|
if (FLAG_harmony_explicit_tailcalls &&
|
||||||
|
expressions_in_tail_position_in_catch_block.length() > 0) {
|
||||||
|
// TODO(ishell): update chapter number.
|
||||||
|
// ES8 XX.YY.ZZ
|
||||||
|
int pos = expressions_in_tail_position_in_catch_block[0].pos;
|
||||||
|
ReportMessageAt(Scanner::Location(pos, pos + 1),
|
||||||
|
MessageTemplate::kTailCallInCatchBlock);
|
||||||
|
*ok = false;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
DCHECK(finally_block != NULL);
|
DCHECK(finally_block != NULL);
|
||||||
result = factory()->NewTryFinallyStatement(try_block, finally_block, pos);
|
result = factory()->NewTryFinallyStatement(try_block, finally_block, pos);
|
||||||
}
|
}
|
||||||
@ -4573,10 +4568,10 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
|
|||||||
|
|
||||||
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
||||||
// Mark collected return expressions that are in tail call position.
|
// Mark collected return expressions that are in tail call position.
|
||||||
const List<Expression*>& expressions_in_tail_position =
|
const List<TailCallExpression>& expressions_in_tail_position =
|
||||||
function_state_->expressions_in_tail_position();
|
function_state_->expressions_in_tail_position();
|
||||||
for (int i = 0; i < expressions_in_tail_position.length(); ++i) {
|
for (int i = 0; i < expressions_in_tail_position.length(); ++i) {
|
||||||
MarkTailPosition(expressions_in_tail_position[i]);
|
MarkTailPosition(expressions_in_tail_position[i].expression);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -920,8 +920,6 @@ class Parser : public ParserBase<ParserTraits> {
|
|||||||
Statement* ParseForStatement(ZoneList<const AstRawString*>* labels, bool* ok);
|
Statement* ParseForStatement(ZoneList<const AstRawString*>* labels, bool* ok);
|
||||||
Statement* ParseThrowStatement(bool* ok);
|
Statement* ParseThrowStatement(bool* ok);
|
||||||
Expression* MakeCatchContext(Handle<String> id, VariableProxy* value);
|
Expression* MakeCatchContext(Handle<String> id, VariableProxy* value);
|
||||||
class DontCollectExpressionsInTailPositionScope;
|
|
||||||
class CollectExpressionsInTailPositionToListScope;
|
|
||||||
TryStatement* ParseTryStatement(bool* ok);
|
TryStatement* ParseTryStatement(bool* ok);
|
||||||
DebuggerStatement* ParseDebuggerStatement(bool* ok);
|
DebuggerStatement* ParseDebuggerStatement(bool* ok);
|
||||||
// Parse a SubStatement in strict mode, or with an extra block scope in
|
// Parse a SubStatement in strict mode, or with an extra block scope in
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
#include "src/hashmap.h"
|
#include "src/hashmap.h"
|
||||||
#include "src/list.h"
|
#include "src/list.h"
|
||||||
#include "src/parsing/parser-base.h"
|
#include "src/parsing/parser-base.h"
|
||||||
#include "src/parsing/preparse-data.h"
|
|
||||||
#include "src/parsing/preparse-data-format.h"
|
#include "src/parsing/preparse-data-format.h"
|
||||||
|
#include "src/parsing/preparse-data.h"
|
||||||
#include "src/parsing/preparser.h"
|
#include "src/parsing/preparser.h"
|
||||||
#include "src/unicode.h"
|
#include "src/unicode.h"
|
||||||
#include "src/utils.h"
|
#include "src/utils.h"
|
||||||
@ -681,11 +681,27 @@ PreParser::Statement PreParser::ParseReturnStatement(bool* ok) {
|
|||||||
// This is not handled during preparsing.
|
// This is not handled during preparsing.
|
||||||
|
|
||||||
Token::Value tok = peek();
|
Token::Value tok = peek();
|
||||||
|
int tail_call_position = -1;
|
||||||
|
if (FLAG_harmony_explicit_tailcalls && tok == Token::CONTINUE) {
|
||||||
|
Consume(Token::CONTINUE);
|
||||||
|
tail_call_position = position();
|
||||||
|
tok = peek();
|
||||||
|
}
|
||||||
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
|
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
|
||||||
tok != Token::SEMICOLON &&
|
tok != Token::SEMICOLON &&
|
||||||
tok != Token::RBRACE &&
|
tok != Token::RBRACE &&
|
||||||
tok != Token::EOS) {
|
tok != Token::EOS) {
|
||||||
ParseExpression(true, CHECK_OK);
|
ParseExpression(true, CHECK_OK);
|
||||||
|
if (tail_call_position >= 0) {
|
||||||
|
if (!function_state_->collect_expressions_in_tail_position()) {
|
||||||
|
Scanner::Location loc(tail_call_position, tail_call_position + 1);
|
||||||
|
ReportMessageAt(loc, MessageTemplate::kTailCallInTryBlock);
|
||||||
|
*ok = false;
|
||||||
|
return Statement::Default();
|
||||||
|
}
|
||||||
|
function_state_->AddExpressionInTailPosition(
|
||||||
|
PreParserExpression::Default(), tail_call_position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ExpectSemicolon(CHECK_OK);
|
ExpectSemicolon(CHECK_OK);
|
||||||
return Statement::Jump();
|
return Statement::Jump();
|
||||||
@ -936,7 +952,10 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
|
|||||||
|
|
||||||
Expect(Token::TRY, CHECK_OK);
|
Expect(Token::TRY, CHECK_OK);
|
||||||
|
|
||||||
ParseBlock(CHECK_OK);
|
{
|
||||||
|
DontCollectExpressionsInTailPositionScope no_tail_calls(function_state_);
|
||||||
|
ParseBlock(CHECK_OK);
|
||||||
|
}
|
||||||
|
|
||||||
Token::Value tok = peek();
|
Token::Value tok = peek();
|
||||||
if (tok != Token::CATCH && tok != Token::FINALLY) {
|
if (tok != Token::CATCH && tok != Token::FINALLY) {
|
||||||
@ -944,6 +963,8 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
|
|||||||
*ok = false;
|
*ok = false;
|
||||||
return Statement::Default();
|
return Statement::Default();
|
||||||
}
|
}
|
||||||
|
List<TailCallExpression> expressions_in_tail_position_in_catch_block;
|
||||||
|
bool catch_block_exists = false;
|
||||||
if (tok == Token::CATCH) {
|
if (tok == Token::CATCH) {
|
||||||
Consume(Token::CATCH);
|
Consume(Token::CATCH);
|
||||||
Expect(Token::LPAREN, CHECK_OK);
|
Expect(Token::LPAREN, CHECK_OK);
|
||||||
@ -953,6 +974,9 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
|
|||||||
ValidateBindingPattern(&pattern_classifier, CHECK_OK);
|
ValidateBindingPattern(&pattern_classifier, CHECK_OK);
|
||||||
Expect(Token::RPAREN, CHECK_OK);
|
Expect(Token::RPAREN, CHECK_OK);
|
||||||
{
|
{
|
||||||
|
CollectExpressionsInTailPositionToListScope
|
||||||
|
collect_expressions_in_tail_position_scope(
|
||||||
|
function_state_, &expressions_in_tail_position_in_catch_block);
|
||||||
BlockState block_state(&scope_, catch_scope);
|
BlockState block_state(&scope_, catch_scope);
|
||||||
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
|
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
|
||||||
{
|
{
|
||||||
@ -960,11 +984,22 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
|
|||||||
ParseBlock(CHECK_OK);
|
ParseBlock(CHECK_OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch_block_exists = true;
|
||||||
tok = peek();
|
tok = peek();
|
||||||
}
|
}
|
||||||
if (tok == Token::FINALLY) {
|
if (tok == Token::FINALLY) {
|
||||||
Consume(Token::FINALLY);
|
Consume(Token::FINALLY);
|
||||||
ParseBlock(CHECK_OK);
|
ParseBlock(CHECK_OK);
|
||||||
|
if (FLAG_harmony_explicit_tailcalls && catch_block_exists &&
|
||||||
|
expressions_in_tail_position_in_catch_block.length() > 0) {
|
||||||
|
// TODO(ishell): update chapter number.
|
||||||
|
// ES8 XX.YY.ZZ
|
||||||
|
int pos = expressions_in_tail_position_in_catch_block[0].pos;
|
||||||
|
ReportMessageAt(Scanner::Location(pos, pos + 1),
|
||||||
|
MessageTemplate::kTailCallInCatchBlock);
|
||||||
|
*ok = false;
|
||||||
|
return Statement::Default();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Statement::Default();
|
return Statement::Default();
|
||||||
}
|
}
|
||||||
|
19
test/message/syntactic-tail-call-in-try-catch-finally.js
Normal file
19
test/message/syntactic-tail-call-in-try-catch-finally.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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() {
|
||||||
|
try {
|
||||||
|
f();
|
||||||
|
} catch(e) {
|
||||||
|
return continue f();
|
||||||
|
} finally {
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
*%(basename)s:15: SyntaxError: Tail call expression in catch block when finally block is also present.
|
||||||
|
return continue f();
|
||||||
|
^
|
||||||
|
SyntaxError: Tail call expression in catch block when finally block is also present.
|
21
test/message/syntactic-tail-call-in-try-try-catch-finally.js
Normal file
21
test/message/syntactic-tail-call-in-try-try-catch-finally.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// 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() {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
f();
|
||||||
|
} catch(e) {
|
||||||
|
return continue f();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
*%(basename)s:16: SyntaxError: Tail call expression in try block.
|
||||||
|
return continue f();
|
||||||
|
^
|
||||||
|
SyntaxError: Tail call expression in try block.
|
16
test/message/syntactic-tail-call-in-try.js
Normal file
16
test/message/syntactic-tail-call-in-try.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// 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() {
|
||||||
|
try {
|
||||||
|
return continue f();
|
||||||
|
} catch(e) {
|
||||||
|
}
|
||||||
|
}
|
4
test/message/syntactic-tail-call-in-try.out
Normal file
4
test/message/syntactic-tail-call-in-try.out
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*%(basename)s:13: SyntaxError: Tail call expression in try block.
|
||||||
|
return continue f();
|
||||||
|
^
|
||||||
|
SyntaxError: Tail call expression in try block.
|
121
test/mjsunit/es7/syntactic-tail-call-simple.js
Normal file
121
test/mjsunit/es7/syntactic-tail-call-simple.js
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// 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 --harmony-explicit-tailcalls --stack-size=100
|
||||||
|
|
||||||
|
//
|
||||||
|
// Tail call normal functions.
|
||||||
|
//
|
||||||
|
(function() {
|
||||||
|
function f(n) {
|
||||||
|
if (n <= 0) {
|
||||||
|
return "foo";
|
||||||
|
}
|
||||||
|
return continue f(n - 1);
|
||||||
|
}
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function f(n) {
|
||||||
|
if (n <= 0) {
|
||||||
|
return "foo";
|
||||||
|
}
|
||||||
|
return continue f(n - 1, 42); // Call with arguments adaptor.
|
||||||
|
}
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function f(n){
|
||||||
|
if (n <= 0) {
|
||||||
|
return "foo";
|
||||||
|
}
|
||||||
|
return continue g(n - 1);
|
||||||
|
}
|
||||||
|
function g(n){
|
||||||
|
if (n <= 0) {
|
||||||
|
return "bar";
|
||||||
|
}
|
||||||
|
return continue f(n - 1);
|
||||||
|
}
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
assertEquals("bar", f(1e5 + 1));
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
assertEquals("bar", f(1e5 + 1));
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function f(n){
|
||||||
|
if (n <= 0) {
|
||||||
|
return "foo";
|
||||||
|
}
|
||||||
|
return continue g(n - 1, 42); // Call with arguments adaptor.
|
||||||
|
}
|
||||||
|
function g(n){
|
||||||
|
if (n <= 0) {
|
||||||
|
return "bar";
|
||||||
|
}
|
||||||
|
return continue f(n - 1, 42); // Call with arguments adaptor.
|
||||||
|
}
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
assertEquals("bar", f(1e5 + 1));
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
assertEquals("bar", f(1e5 + 1));
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Tail call bound functions.
|
||||||
|
//
|
||||||
|
(function() {
|
||||||
|
function f0(n) {
|
||||||
|
if (n <= 0) {
|
||||||
|
return "foo";
|
||||||
|
}
|
||||||
|
return continue f_bound(n - 1);
|
||||||
|
}
|
||||||
|
var f_bound = f0.bind({});
|
||||||
|
function f(n) {
|
||||||
|
return continue f_bound(n);
|
||||||
|
}
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function f0(n){
|
||||||
|
if (n <= 0) {
|
||||||
|
return "foo";
|
||||||
|
}
|
||||||
|
return continue g_bound(n - 1);
|
||||||
|
}
|
||||||
|
function g0(n){
|
||||||
|
if (n <= 0) {
|
||||||
|
return "bar";
|
||||||
|
}
|
||||||
|
return continue f_bound(n - 1);
|
||||||
|
}
|
||||||
|
var f_bound = f0.bind({});
|
||||||
|
var g_bound = g0.bind({});
|
||||||
|
function f(n) {
|
||||||
|
return continue f_bound(n);
|
||||||
|
}
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
assertEquals("bar", f(1e5 + 1));
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertEquals("foo", f(1e5));
|
||||||
|
assertEquals("bar", f(1e5 + 1));
|
||||||
|
})();
|
423
test/mjsunit/es7/syntactic-tail-call.js
Normal file
423
test/mjsunit/es7/syntactic-tail-call.js
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
// 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 --harmony-explicit-tailcalls
|
||||||
|
|
||||||
|
Error.prepareStackTrace = (error,stack) => {
|
||||||
|
error.strace = stack;
|
||||||
|
return error.message + "\n at " + stack.join("\n at ");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function CheckStackTrace(expected) {
|
||||||
|
var e = new Error();
|
||||||
|
e.stack; // prepare stack trace
|
||||||
|
var stack = e.strace;
|
||||||
|
assertEquals("CheckStackTrace", stack[0].getFunctionName());
|
||||||
|
for (var i = 0; i < expected.length; i++) {
|
||||||
|
assertEquals(expected[i].name, stack[i + 1].getFunctionName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function f(expected_call_stack, a, b) {
|
||||||
|
CheckStackTrace(expected_call_stack);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f_153(expected_call_stack, a) {
|
||||||
|
CheckStackTrace(expected_call_stack);
|
||||||
|
return 153;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Tail call when caller does not have an arguments adaptor frame.
|
||||||
|
(function() {
|
||||||
|
// Caller and callee have same number of arguments.
|
||||||
|
function f1(a) {
|
||||||
|
CheckStackTrace([f1, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
function g1(a) { return continue f1(2); }
|
||||||
|
|
||||||
|
// Caller has more arguments than callee.
|
||||||
|
function f2(a) {
|
||||||
|
CheckStackTrace([f2, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
function g2(a, b, c) { return continue f2(2); }
|
||||||
|
|
||||||
|
// Caller has less arguments than callee.
|
||||||
|
function f3(a, b, c) {
|
||||||
|
CheckStackTrace([f3, test]);
|
||||||
|
return 10 + a + b + c;
|
||||||
|
}
|
||||||
|
function g3(a) { return continue f3(2, 3, 4); }
|
||||||
|
|
||||||
|
// Callee has arguments adaptor frame.
|
||||||
|
function f4(a, b, c) {
|
||||||
|
CheckStackTrace([f4, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
function g4(a) { return continue f4(2); }
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
assertEquals(12, g1(1));
|
||||||
|
assertEquals(12, g2(1, 2, 3));
|
||||||
|
assertEquals(19, g3(1));
|
||||||
|
assertEquals(12, g4(1));
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test();
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Tail call when caller has an arguments adaptor frame.
|
||||||
|
(function() {
|
||||||
|
// Caller and callee have same number of arguments.
|
||||||
|
function f1(a) {
|
||||||
|
CheckStackTrace([f1, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
function g1(a) { return continue f1(2); }
|
||||||
|
|
||||||
|
// Caller has more arguments than callee.
|
||||||
|
function f2(a) {
|
||||||
|
CheckStackTrace([f2, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
function g2(a, b, c) { return continue f2(2); }
|
||||||
|
|
||||||
|
// Caller has less arguments than callee.
|
||||||
|
function f3(a, b, c) {
|
||||||
|
CheckStackTrace([f3, test]);
|
||||||
|
return 10 + a + b + c;
|
||||||
|
}
|
||||||
|
function g3(a) { return continue f3(2, 3, 4); }
|
||||||
|
|
||||||
|
// Callee has arguments adaptor frame.
|
||||||
|
function f4(a, b, c) {
|
||||||
|
CheckStackTrace([f4, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
function g4(a) { return continue f4(2); }
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
assertEquals(12, g1());
|
||||||
|
assertEquals(12, g2());
|
||||||
|
assertEquals(19, g3());
|
||||||
|
assertEquals(12, g4());
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test();
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Tail call bound function when caller does not have an arguments
|
||||||
|
// adaptor frame.
|
||||||
|
(function() {
|
||||||
|
// Caller and callee have same number of arguments.
|
||||||
|
function f1(a) {
|
||||||
|
assertEquals(153, this.a);
|
||||||
|
CheckStackTrace([f1, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
var b1 = f1.bind({a: 153});
|
||||||
|
function g1(a) { return continue b1(2); }
|
||||||
|
|
||||||
|
// Caller has more arguments than callee.
|
||||||
|
function f2(a) {
|
||||||
|
assertEquals(153, this.a);
|
||||||
|
CheckStackTrace([f2, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
var b2 = f2.bind({a: 153});
|
||||||
|
function g2(a, b, c) { return continue b2(2); }
|
||||||
|
|
||||||
|
// Caller has less arguments than callee.
|
||||||
|
function f3(a, b, c) {
|
||||||
|
assertEquals(153, this.a);
|
||||||
|
CheckStackTrace([f3, test]);
|
||||||
|
return 10 + a + b + c;
|
||||||
|
}
|
||||||
|
var b3 = f3.bind({a: 153});
|
||||||
|
function g3(a) { return continue b3(2, 3, 4); }
|
||||||
|
|
||||||
|
// Callee has arguments adaptor frame.
|
||||||
|
function f4(a, b, c) {
|
||||||
|
assertEquals(153, this.a);
|
||||||
|
CheckStackTrace([f4, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
var b4 = f4.bind({a: 153});
|
||||||
|
function g4(a) { return continue b4(2); }
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
assertEquals(12, g1(1));
|
||||||
|
assertEquals(12, g2(1, 2, 3));
|
||||||
|
assertEquals(19, g3(1));
|
||||||
|
assertEquals(12, g4(1));
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test();
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Tail call bound function when caller has an arguments adaptor frame.
|
||||||
|
(function() {
|
||||||
|
// Caller and callee have same number of arguments.
|
||||||
|
function f1(a) {
|
||||||
|
assertEquals(153, this.a);
|
||||||
|
CheckStackTrace([f1, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
var b1 = f1.bind({a: 153});
|
||||||
|
function g1(a) { return continue b1(2); }
|
||||||
|
|
||||||
|
// Caller has more arguments than callee.
|
||||||
|
function f2(a) {
|
||||||
|
assertEquals(153, this.a);
|
||||||
|
CheckStackTrace([f2, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
var b2 = f2.bind({a: 153});
|
||||||
|
function g2(a, b, c) { return continue b2(2); }
|
||||||
|
|
||||||
|
// Caller has less arguments than callee.
|
||||||
|
function f3(a, b, c) {
|
||||||
|
assertEquals(153, this.a);
|
||||||
|
CheckStackTrace([f3, test]);
|
||||||
|
return 10 + a + b + c;
|
||||||
|
}
|
||||||
|
var b3 = f3.bind({a: 153});
|
||||||
|
function g3(a) { return continue b3(2, 3, 4); }
|
||||||
|
|
||||||
|
// Callee has arguments adaptor frame.
|
||||||
|
function f4(a, b, c) {
|
||||||
|
assertEquals(153, this.a);
|
||||||
|
CheckStackTrace([f4, test]);
|
||||||
|
return 10 + a;
|
||||||
|
}
|
||||||
|
var b4 = f4.bind({a: 153});
|
||||||
|
function g4(a) { return continue b4(2); }
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
assertEquals(12, g1());
|
||||||
|
assertEquals(12, g2());
|
||||||
|
assertEquals(19, g3());
|
||||||
|
assertEquals(12, g4());
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test();
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Tail calling via various expressions.
|
||||||
|
(function() {
|
||||||
|
function g1(a) {
|
||||||
|
return continue f([f, g1, test], false) || f([f, test], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function g2(a) {
|
||||||
|
return continue f([f, g2, test], true) && f([f, test], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function g3(a) {
|
||||||
|
return continue f([f, g3, test], 13), f([f, test], 153);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
assertEquals(true, g1());
|
||||||
|
assertEquals(true, g2());
|
||||||
|
assertEquals(153, g3());
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test();
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Test tail calls from try-catch constructs.
|
||||||
|
(function() {
|
||||||
|
function tc1(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tc1, test]);
|
||||||
|
return f_153([f_153, tc1, test]);
|
||||||
|
} catch(e) {
|
||||||
|
f_153([f_153, tc1, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tc2(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tc2, test]);
|
||||||
|
throw new Error("boom");
|
||||||
|
} catch(e) {
|
||||||
|
f_153([f_153, tc2, test]);
|
||||||
|
return continue f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tc3(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tc3, test]);
|
||||||
|
throw new Error("boom");
|
||||||
|
} catch(e) {
|
||||||
|
f_153([f_153, tc3, test]);
|
||||||
|
}
|
||||||
|
f_153([f_153, tc3, test]);
|
||||||
|
return continue f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
assertEquals(153, tc1());
|
||||||
|
assertEquals(153, tc2());
|
||||||
|
assertEquals(153, tc3());
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test();
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Test tail calls from try-finally constructs.
|
||||||
|
(function() {
|
||||||
|
function tf1(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tf1, test]);
|
||||||
|
return f_153([f_153, tf1, test]);
|
||||||
|
} finally {
|
||||||
|
f_153([f_153, tf1, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tf2(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tf2, test]);
|
||||||
|
throw new Error("boom");
|
||||||
|
} finally {
|
||||||
|
f_153([f_153, tf2, test]);
|
||||||
|
return continue f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tf3(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tf3, test]);
|
||||||
|
} finally {
|
||||||
|
f_153([f_153, tf3, test]);
|
||||||
|
}
|
||||||
|
return continue f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
assertEquals(153, tf1());
|
||||||
|
assertEquals(153, tf2());
|
||||||
|
assertEquals(153, tf3());
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test();
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Test tail calls from try-catch-finally constructs.
|
||||||
|
(function() {
|
||||||
|
function tcf1(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tcf1, test]);
|
||||||
|
return f_153([f_153, tcf1, test]);
|
||||||
|
} catch(e) {
|
||||||
|
} finally {
|
||||||
|
f_153([f_153, tcf1, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tcf2(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tcf2, test]);
|
||||||
|
throw new Error("boom");
|
||||||
|
} catch(e) {
|
||||||
|
f_153([f_153, tcf2, test]);
|
||||||
|
return f_153([f_153, tcf2, test]);
|
||||||
|
} finally {
|
||||||
|
f_153([f_153, tcf2, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tcf3(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tcf3, test]);
|
||||||
|
throw new Error("boom");
|
||||||
|
} catch(e) {
|
||||||
|
f_153([f_153, tcf3, test]);
|
||||||
|
} finally {
|
||||||
|
f_153([f_153, tcf3, test]);
|
||||||
|
return continue f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tcf4(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tcf4, test]);
|
||||||
|
throw new Error("boom");
|
||||||
|
} catch(e) {
|
||||||
|
f_153([f_153, tcf4, test]);
|
||||||
|
} finally {
|
||||||
|
f_153([f_153, tcf4, test]);
|
||||||
|
}
|
||||||
|
return continue f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
assertEquals(153, tcf1());
|
||||||
|
assertEquals(153, tcf2());
|
||||||
|
assertEquals(153, tcf3());
|
||||||
|
assertEquals(153, tcf4());
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test();
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Test tail calls from arrow functions.
|
||||||
|
(function () {
|
||||||
|
function g1(a) {
|
||||||
|
return continue (() => { return continue f_153([f_153, test]); })();
|
||||||
|
}
|
||||||
|
|
||||||
|
function g2(a) {
|
||||||
|
return continue (() => continue f_153([f_153, test]))();
|
||||||
|
}
|
||||||
|
|
||||||
|
function g3(a) {
|
||||||
|
var closure = () => continue f([f, closure, test], true)
|
||||||
|
? f_153([f_153, test])
|
||||||
|
: f_153([f_153, test]);
|
||||||
|
|
||||||
|
return continue closure();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
assertEquals(153, g1());
|
||||||
|
assertEquals(153, g2());
|
||||||
|
assertEquals(153, g3());
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test();
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user