Commit 1350eb3d authored by ishell's avatar ishell Committed by Commit bot

[es8] More spec compliant syntactic tail calls implementation.

Unlike previous implementation where the 'continue' keyword was a feature of a return statement the keyword is now recognized as a part of expression. Error reporting was significantly improved.

--harmony-explicit-tailcalls option is now orthogonal to --harmony-tailcalls so we can test both modes at the same time.

This CL also adds %GetExceptionDetails(exception) that fetches hidden |start_pos| and |end_pos| values from the exception object.

BUG=v8:4915
LOG=N

Review-Url: https://codereview.chromium.org/1928203002
Cr-Commit-Position: refs/heads/master@{#36024}
parent 46907cbb
......@@ -426,10 +426,6 @@ class CallSite {
"inside a block.") \
T(StrictOctalLiteral, "Octal literals are not allowed in strict mode.") \
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(TailCallInForInOf, "Tail call expression in for-in/of body.") \
T(TailCallInTryBlock, "Tail call expression in try block.") \
T(TemplateOctalLiteral, \
"Octal literals are not allowed in template strings.") \
T(ThisFormalParameter, "'this' is not a valid formal parameter name") \
......@@ -443,10 +439,16 @@ class CallSite {
T(UnexpectedEOS, "Unexpected end of input") \
T(UnexpectedFunctionSent, \
"function.sent expression is not allowed outside a generator") \
T(UnexpectedInsideTailCall, "Unexpected expression inside tail call") \
T(UnexpectedReserved, "Unexpected reserved word") \
T(UnexpectedStrictReserved, "Unexpected strict mode reserved word") \
T(UnexpectedSuper, "'super' keyword unexpected here") \
T(UnexpectedNewTarget, "new.target expression is not allowed here") \
T(UnexpectedTailCall, "Tail call expression is not allowed here") \
T(UnexpectedTailCallInCatchBlock, \
"Tail call expression in catch block when finally block is also present") \
T(UnexpectedTailCallInForInOf, "Tail call expression in for-in/of body") \
T(UnexpectedTailCallInTryBlock, "Tail call expression in try block") \
T(UnexpectedTemplateString, "Unexpected template string") \
T(UnexpectedToken, "Unexpected token %") \
T(UnexpectedTokenIdentifier, "Unexpected identifier") \
......
......@@ -39,9 +39,11 @@ class ExpressionClassifier {
ArrowFormalParametersProduction = 1 << 6,
LetPatternProduction = 1 << 7,
CoverInitializedNameProduction = 1 << 8,
TailCallExpressionProduction = 1 << 9,
ExpressionProductions =
(ExpressionProduction | FormalParameterInitializerProduction),
(ExpressionProduction | FormalParameterInitializerProduction |
TailCallExpressionProduction),
PatternProductions = (BindingPatternProduction |
AssignmentPatternProduction | LetPatternProduction),
FormalParametersProductions = (DistinctFormalParametersProduction |
......@@ -143,6 +145,13 @@ class ExpressionClassifier {
return cover_initialized_name_error_;
}
bool has_tail_call_expression() const {
return !is_valid(TailCallExpressionProduction);
}
const Error& tail_call_expression_error() const {
return tail_call_expression_error_;
}
bool is_simple_parameter_list() const {
return !(function_properties_ & NonSimpleParameter);
}
......@@ -260,6 +269,16 @@ class ExpressionClassifier {
cover_initialized_name_error_.arg = arg;
}
void RecordTailCallExpressionError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
if (has_tail_call_expression()) return;
invalid_productions_ |= TailCallExpressionProduction;
tail_call_expression_error_.location = loc;
tail_call_expression_error_.message = message;
tail_call_expression_error_.arg = arg;
}
void ForgiveCoverInitializedNameError() {
invalid_productions_ &= ~CoverInitializedNameProduction;
cover_initialized_name_error_ = Error();
......@@ -305,6 +324,8 @@ class ExpressionClassifier {
let_pattern_error_ = inner->let_pattern_error_;
if (errors & CoverInitializedNameProduction)
cover_initialized_name_error_ = inner->cover_initialized_name_error_;
if (errors & TailCallExpressionProduction)
tail_call_expression_error_ = inner->tail_call_expression_error_;
}
// As an exception to the above, the result continues to be a valid arrow
......@@ -340,6 +361,8 @@ class ExpressionClassifier {
int non_pattern_begin_;
unsigned invalid_productions_;
unsigned function_properties_;
// TODO(ishell): consider using Zone[Hash]Map<TargetProduction, Error>
// here to consume less stack space during parsing.
Error expression_error_;
Error formal_parameter_initializer_error_;
Error binding_pattern_error_;
......@@ -349,6 +372,7 @@ class ExpressionClassifier {
Error strict_mode_formal_parameter_error_;
Error let_pattern_error_;
Error cover_initialized_name_error_;
Error tail_call_expression_error_;
DuplicateFinder* duplicate_finder_;
};
......
......@@ -191,19 +191,48 @@ class ParserBase : public Traits {
Scope* scope;
};
struct TailCallExpression {
TailCallExpression(ExpressionT expression, int pos)
: expression(expression), pos(pos) {}
class TailCallExpressionList {
public:
explicit TailCallExpressionList(Zone* zone)
: zone_(zone), expressions_(0, zone) {}
const ZoneList<ExpressionT>& expressions() const { return expressions_; }
const Scanner::Location& location() const { return loc_; }
bool is_empty() const { return expressions_.is_empty(); }
void Swap(TailCallExpressionList& other) {
expressions_.Swap(&other.expressions_);
std::swap(loc_, other.loc_);
}
void Add(ExpressionT expr, const Scanner::Location& loc) {
if (expressions_.is_empty()) loc_ = loc;
expressions_.Add(expr, zone_);
}
void Append(const TailCallExpressionList& other) {
if (expressions_.is_empty()) loc_ = other.loc_;
expressions_.AddAll(other.expressions_, zone_);
}
ExpressionT expression;
int pos;
private:
Zone* zone_;
ZoneList<ExpressionT> expressions_;
Scanner::Location loc_;
};
// Defines whether tail call expressions are allowed or not.
enum class ReturnExprContext {
// Tail call expressions are allowed.
kNormal,
// Tail call expressions are not allowed.
// We are inside return statement which is allowed to contain tail call
// expressions. Tail call expressions are allowed.
kInsideValidReturnStatement,
// We are inside a block in which tail call expressions are allowed but
// not yet inside a return statement.
kInsideValidBlock,
// Tail call expressions are not allowed in the following blocks.
kInsideTryBlock,
kInsideForInOfBody,
};
......@@ -265,12 +294,18 @@ class ParserBase : public Traits {
return destructuring_assignments_to_rewrite_;
}
List<TailCallExpression>& expressions_in_tail_position() {
return expressions_in_tail_position_;
TailCallExpressionList& tail_call_expressions() {
return tail_call_expressions_;
}
void AddExpressionInTailPosition(ExpressionT expression, int pos) {
if (return_expr_context() == ReturnExprContext::kNormal) {
expressions_in_tail_position_.Add(TailCallExpression(expression, pos));
void AddExpressionInTailPosition(ExpressionT expression,
const Scanner::Location& loc) {
// If only FLAG_harmony_explicit_tailcalls is enabled then expression
// must be a Call expression.
DCHECK(FLAG_harmony_tailcalls || !FLAG_harmony_explicit_tailcalls ||
expression->IsCall());
if (return_expr_context() ==
ReturnExprContext::kInsideValidReturnStatement) {
tail_call_expressions_.Add(expression, loc);
}
}
......@@ -333,7 +368,7 @@ class ParserBase : public Traits {
Scope* outer_scope_;
List<DestructuringAssignment> destructuring_assignments_to_rewrite_;
List<TailCallExpression> expressions_in_tail_position_;
TailCallExpressionList tail_call_expressions_;
ReturnExprContext return_expr_context_;
ZoneList<ExpressionT> non_patterns_to_rewrite_;
......@@ -359,7 +394,13 @@ class ParserBase : public Traits {
ReturnExprContext return_expr_context)
: function_state_(function_state),
sav_return_expr_context_(function_state->return_expr_context()) {
function_state->set_return_expr_context(return_expr_context);
// Don't update context if we are requested to enable tail call
// expressions but current block does not allow them.
if (return_expr_context !=
ReturnExprContext::kInsideValidReturnStatement ||
sav_return_expr_context_ == ReturnExprContext::kInsideValidBlock) {
function_state->set_return_expr_context(return_expr_context);
}
}
~ReturnExprScope() {
function_state_->set_return_expr_context(sav_return_expr_context_);
......@@ -375,17 +416,17 @@ class ParserBase : public Traits {
class CollectExpressionsInTailPositionToListScope {
public:
CollectExpressionsInTailPositionToListScope(FunctionState* function_state,
List<TailCallExpression>* list)
TailCallExpressionList* list)
: function_state_(function_state), list_(list) {
function_state->expressions_in_tail_position().Swap(list_);
function_state->tail_call_expressions().Swap(*list_);
}
~CollectExpressionsInTailPositionToListScope() {
function_state_->expressions_in_tail_position().Swap(list_);
function_state_->tail_call_expressions().Swap(*list_);
}
private:
FunctionState* function_state_;
List<TailCallExpression>* list_;
TailCallExpressionList* list_;
};
// Annoyingly, arrow functions first parse as comma expressions, then when we
......@@ -643,23 +684,6 @@ class ParserBase : public Traits {
error_type);
}
void ReportIllegalTailCallAt(int pos, ReturnExprContext return_expr_context) {
Scanner::Location loc(pos, pos + 1);
MessageTemplate::Template msg = MessageTemplate::kNone;
switch (return_expr_context) {
case ReturnExprContext::kNormal:
UNREACHABLE();
return;
case ReturnExprContext::kInsideTryBlock:
msg = MessageTemplate::kTailCallInTryBlock;
break;
case ReturnExprContext::kInsideForInOfBody:
msg = MessageTemplate::kTailCallInForInOf;
break;
}
ReportMessageAt(loc, msg);
}
void GetUnexpectedTokenMessage(
Token::Value token, MessageTemplate::Template* message,
Scanner::Location* location, const char** arg,
......@@ -760,6 +784,15 @@ class ParserBase : public Traits {
}
}
void CheckNoTailCallExpressions(const ExpressionClassifier* classifier,
bool* ok) {
if (FLAG_harmony_explicit_tailcalls &&
classifier->has_tail_call_expression()) {
ReportClassifierError(classifier->tail_call_expression_error());
*ok = false;
}
}
void ExpressionUnexpectedToken(ExpressionClassifier* classifier) {
MessageTemplate::Template message = MessageTemplate::kUnexpectedToken;
const char* arg;
......@@ -837,6 +870,8 @@ class ParserBase : public Traits {
bool* ok);
ExpressionT ParseYieldExpression(bool accept_IN,
ExpressionClassifier* classifier, bool* ok);
ExpressionT ParseTailCallExpression(ExpressionClassifier* classifier,
bool* ok);
ExpressionT ParseConditionalExpression(bool accept_IN,
ExpressionClassifier* classifier,
bool* ok);
......@@ -1013,7 +1048,8 @@ ParserBase<Traits>::FunctionState::FunctionState(
outer_function_state_(*function_state_stack),
scope_stack_(scope_stack),
outer_scope_(*scope_stack),
return_expr_context_(ReturnExprContext::kNormal),
tail_call_expressions_(scope->zone()),
return_expr_context_(ReturnExprContext::kInsideValidBlock),
non_patterns_to_rewrite_(0, scope->zone()),
factory_(factory),
next_function_is_parenthesized_(false),
......@@ -1454,7 +1490,6 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
return result;
}
template <class Traits>
typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
bool accept_IN, ExpressionClassifier* classifier, bool* ok) {
......@@ -1470,6 +1505,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
bool is_simple_parameter_list = this->IsIdentifier(result);
bool seen_rest = false;
while (peek() == Token::COMMA) {
CheckNoTailCallExpressions(classifier, CHECK_OK);
if (seen_rest) {
// At this point the production can't possibly be valid, but we don't know
// which error to signal.
......@@ -1533,6 +1569,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
int expr_pos = peek_position();
ExpressionT argument =
this->ParseAssignmentExpression(true, classifier, CHECK_OK);
CheckNoTailCallExpressions(classifier, CHECK_OK);
elem = factory()->NewSpread(argument, start_pos, expr_pos);
if (first_spread_index < 0) {
......@@ -1556,6 +1593,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
} else {
int beg_pos = peek_position();
elem = this->ParseAssignmentExpression(true, classifier, CHECK_OK);
CheckNoTailCallExpressions(classifier, CHECK_OK);
CheckDestructuringElement(elem, classifier, beg_pos,
scanner()->location().end_pos);
}
......@@ -1903,6 +1941,7 @@ typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments(
ExpressionT argument = this->ParseAssignmentExpression(
true, classifier, CHECK_OK_CUSTOM(NullExpressionList));
CheckNoTailCallExpressions(classifier, CHECK_OK_CUSTOM(NullExpressionList));
Traits::RewriteNonPattern(classifier, CHECK_OK_CUSTOM(NullExpressionList));
if (is_spread) {
if (!spread_arg.IsValid()) {
......@@ -1961,7 +2000,6 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
// ArrowFunction
// YieldExpression
// LeftHandSideExpression AssignmentOperator AssignmentExpression
// TailCallExpression
bool is_destructuring_assignment = false;
int lhs_beg_pos = peek_position();
......@@ -2047,6 +2085,8 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
// Now pending non-pattern expressions must be discarded.
arrow_formals_classifier.Discard();
CheckNoTailCallExpressions(classifier, CHECK_OK);
if (IsValidPattern(expression) && peek() == Token::ASSIGN) {
classifier->ForgiveCoverInitializedNameError();
ValidateAssignmentPattern(classifier, CHECK_OK);
......@@ -2071,6 +2111,7 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
ExpressionT right =
this->ParseAssignmentExpression(accept_IN, &rhs_classifier, CHECK_OK);
CheckNoTailCallExpressions(&rhs_classifier, CHECK_OK);
Traits::RewriteNonPattern(&rhs_classifier, CHECK_OK);
classifier->Accumulate(
&rhs_classifier, ExpressionClassifier::ExpressionProductions |
......@@ -2170,6 +2211,56 @@ ParserBase<Traits>::ParseYieldExpression(bool accept_IN,
return yield;
}
template <class Traits>
typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::ParseTailCallExpression(ExpressionClassifier* classifier,
bool* ok) {
// TailCallExpression::
// 'continue' MemberExpression Arguments
// 'continue' CallExpression Arguments
// 'continue' MemberExpression TemplateLiteral
// 'continue' CallExpression TemplateLiteral
Expect(Token::CONTINUE, CHECK_OK);
int pos = position();
int sub_expression_pos = peek_position();
ExpressionT expression =
this->ParseLeftHandSideExpression(classifier, CHECK_OK);
CheckNoTailCallExpressions(classifier, CHECK_OK);
Scanner::Location loc(pos, scanner()->location().end_pos);
ReturnExprContext return_expr_context =
function_state_->return_expr_context();
if (return_expr_context != ReturnExprContext::kInsideValidReturnStatement) {
MessageTemplate::Template msg = MessageTemplate::kNone;
switch (return_expr_context) {
case ReturnExprContext::kInsideValidReturnStatement:
UNREACHABLE();
return Traits::EmptyExpression();
case ReturnExprContext::kInsideValidBlock:
msg = MessageTemplate::kUnexpectedTailCall;
break;
case ReturnExprContext::kInsideTryBlock:
msg = MessageTemplate::kUnexpectedTailCallInTryBlock;
break;
case ReturnExprContext::kInsideForInOfBody:
msg = MessageTemplate::kUnexpectedTailCallInForInOf;
break;
}
ReportMessageAt(loc, msg);
*ok = false;
return Traits::EmptyExpression();
}
if (!expression->IsCall()) {
Scanner::Location sub_loc(sub_expression_pos, loc.end_pos);
ReportMessageAt(sub_loc, MessageTemplate::kUnexpectedInsideTailCall);
*ok = false;
return Traits::EmptyExpression();
}
classifier->RecordTailCallExpressionError(
loc, MessageTemplate::kUnexpectedTailCall);
function_state_->AddExpressionInTailPosition(expression, loc);
return expression;
}
// Precedence = 3
template <class Traits>
......@@ -2186,6 +2277,7 @@ ParserBase<Traits>::ParseConditionalExpression(bool accept_IN,
ExpressionT expression =
this->ParseBinaryExpression(4, accept_IN, classifier, CHECK_OK);
if (peek() != Token::CONDITIONAL) return expression;
CheckNoTailCallExpressions(classifier, CHECK_OK);
Traits::RewriteNonPattern(classifier, CHECK_OK);
ArrowFormalParametersUnexpectedToken(classifier);
BindingPatternUnexpectedToken(classifier);
......@@ -2214,6 +2306,7 @@ ParserBase<Traits>::ParseBinaryExpression(int prec, bool accept_IN,
for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) {
// prec1 >= 4
while (Precedence(peek(), accept_IN) == prec1) {
CheckNoTailCallExpressions(classifier, CHECK_OK);
Traits::RewriteNonPattern(classifier, CHECK_OK);
BindingPatternUnexpectedToken(classifier);
ArrowFormalParametersUnexpectedToken(classifier);
......@@ -2224,6 +2317,9 @@ ParserBase<Traits>::ParseBinaryExpression(int prec, bool accept_IN,
const int next_prec = is_right_associative ? prec1 : prec1 + 1;
ExpressionT y =
ParseBinaryExpression(next_prec, accept_IN, classifier, CHECK_OK);
if (op != Token::OR && op != Token::AND) {
CheckNoTailCallExpressions(classifier, CHECK_OK);
}
Traits::RewriteNonPattern(classifier, CHECK_OK);
if (this->ShortcutNumericLiteralBinaryExpression(&x, y, op, pos,
......@@ -2288,6 +2384,7 @@ ParserBase<Traits>::ParseUnaryExpression(ExpressionClassifier* classifier,
op = Next();
int pos = position();
ExpressionT expression = ParseUnaryExpression(classifier, CHECK_OK);
CheckNoTailCallExpressions(classifier, CHECK_OK);
Traits::RewriteNonPattern(classifier, CHECK_OK);
if (op == Token::DELETE && is_strict(language_mode())) {
......@@ -2313,6 +2410,7 @@ ParserBase<Traits>::ParseUnaryExpression(ExpressionClassifier* classifier,
op = Next();
int beg_pos = peek_position();
ExpressionT expression = this->ParseUnaryExpression(classifier, CHECK_OK);
CheckNoTailCallExpressions(classifier, CHECK_OK);
expression = this->CheckAndRewriteReferenceExpression(
expression, beg_pos, scanner()->location().end_pos,
MessageTemplate::kInvalidLhsInPrefixOp, CHECK_OK);
......@@ -2342,6 +2440,7 @@ ParserBase<Traits>::ParsePostfixExpression(ExpressionClassifier* classifier,
this->ParseLeftHandSideExpression(classifier, CHECK_OK);
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
Token::IsCountOp(peek())) {
CheckNoTailCallExpressions(classifier, CHECK_OK);
BindingPatternUnexpectedToken(classifier);
ArrowFormalParametersUnexpectedToken(classifier);
......@@ -2361,7 +2460,6 @@ ParserBase<Traits>::ParsePostfixExpression(ExpressionClassifier* classifier,
return expression;
}
template <class Traits>
typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::ParseLeftHandSideExpression(
......@@ -2369,12 +2467,17 @@ ParserBase<Traits>::ParseLeftHandSideExpression(
// LeftHandSideExpression ::
// (NewExpression | MemberExpression) ...
if (FLAG_harmony_explicit_tailcalls && peek() == Token::CONTINUE) {
return this->ParseTailCallExpression(classifier, ok);
}
ExpressionT result =
this->ParseMemberWithNewPrefixesExpression(classifier, CHECK_OK);
while (true) {
switch (peek()) {
case Token::LBRACK: {
CheckNoTailCallExpressions(classifier, CHECK_OK);
Traits::RewriteNonPattern(classifier, CHECK_OK);
BindingPatternUnexpectedToken(classifier);
ArrowFormalParametersUnexpectedToken(classifier);
......@@ -2388,6 +2491,7 @@ ParserBase<Traits>::ParseLeftHandSideExpression(
}
case Token::LPAREN: {
CheckNoTailCallExpressions(classifier, CHECK_OK);
Traits::RewriteNonPattern(classifier, CHECK_OK);
BindingPatternUnexpectedToken(classifier);
ArrowFormalParametersUnexpectedToken(classifier);
......@@ -2446,6 +2550,7 @@ ParserBase<Traits>::ParseLeftHandSideExpression(
}
case Token::PERIOD: {
CheckNoTailCallExpressions(classifier, CHECK_OK);
Traits::RewriteNonPattern(classifier, CHECK_OK);
BindingPatternUnexpectedToken(classifier);
ArrowFormalParametersUnexpectedToken(classifier);
......@@ -2460,6 +2565,7 @@ ParserBase<Traits>::ParseLeftHandSideExpression(
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL: {
CheckNoTailCallExpressions(classifier, CHECK_OK);
Traits::RewriteNonPattern(classifier, CHECK_OK);
BindingPatternUnexpectedToken(classifier);
ArrowFormalParametersUnexpectedToken(classifier);
......@@ -2938,22 +3044,10 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
// Single-expression body
int pos = position();
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());
}
DCHECK(ReturnExprContext::kInsideValidBlock ==
function_state_->return_expr_context());
ReturnExprScope allow_tail_calls(
function_state_, ReturnExprContext::kInsideValidReturnStatement);
ExpressionT expression =
ParseAssignmentExpression(accept_IN, &classifier, CHECK_OK);
Traits::RewriteNonPattern(&classifier, CHECK_OK);
......@@ -2962,9 +3056,11 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
body->Add(factory()->NewReturnStatement(expression, pos), zone());
materialized_literal_count = function_state.materialized_literal_count();
expected_property_count = function_state.expected_property_count();
if (is_tail_call_expression) {
if (allow_tailcalls() && !is_sloppy(language_mode())) {
// ES6 14.6.1 Static Semantics: IsInTailPosition
this->MarkTailPosition(expression);
}
this->MarkCollectedTailCallExpressions();
}
super_loc = function_state.super_location();
......@@ -3063,6 +3159,7 @@ ParserBase<Traits>::ParseTemplateLiteral(ExpressionT tag, int start,
int expr_pos = peek_position();
ExpressionT expression = this->ParseExpression(true, classifier, CHECK_OK);
CheckNoTailCallExpressions(classifier, CHECK_OK);
Traits::RewriteNonPattern(classifier, CHECK_OK);
Traits::AddTemplateExpression(&ts, expression);
......
......@@ -765,6 +765,10 @@ void ParserTraits::MarkTailPosition(Expression* expression) {
expression->MarkTail();
}
void ParserTraits::MarkCollectedTailCallExpressions() {
parser_->MarkCollectedTailCallExpressions();
}
Parser::Parser(ParseInfo* info)
: ParserBase<ParserTraits>(info->zone(), &scanner_, info->stack_limit(),
info->extension(), info->ast_value_factory(),
......@@ -2588,13 +2592,6 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
function_state_->set_return_location(loc);
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;
Expression* return_value;
if (scanner()->HasAnyLineTerminatorBeforeNext() ||
......@@ -2608,9 +2605,13 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
}
} else {
int pos = peek_position();
return_value = ParseExpression(true, CHECK_OK);
if (IsSubclassConstructor(function_state_->kind())) {
// Because of the return code rewriting that happens in case of a subclass
// constructor we don't want to accept tail calls, therefore we don't set
// ReturnExprScope to kInsideValidReturnStatement here.
return_value = ParseExpression(true, CHECK_OK);
// For subclass constructors we need to return this in case of undefined
// return a Smi (transformed into an exception in the ConstructStub)
// for a non object.
......@@ -2649,24 +2650,16 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
return_value = factory()->NewConditional(
is_undefined, ThisExpression(scope_, factory(), pos),
is_object_conditional, pos);
}
// TODO(ishell): update chapter number.
// ES8 XX.YY.ZZ
if (tail_call_position >= 0) {
ReturnExprContext return_expr_context =
function_state_->return_expr_context();
if (return_expr_context != ReturnExprContext::kNormal) {
ReportIllegalTailCallAt(tail_call_position, return_expr_context);
*ok = false;
return NULL;
} else {
ReturnExprScope maybe_allow_tail_calls(
function_state_, ReturnExprContext::kInsideValidReturnStatement);
return_value = ParseExpression(true, CHECK_OK);
if (allow_tailcalls() && !is_sloppy(language_mode())) {
// ES6 14.6.1 Static Semantics: IsInTailPosition
Scanner::Location loc(pos, pos + 1);
function_state_->AddExpressionInTailPosition(return_value, loc);
}
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);
......@@ -2879,7 +2872,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
Scope* catch_scope = NULL;
Variable* catch_variable = NULL;
Block* catch_block = NULL;
List<TailCallExpression> expressions_in_tail_position_in_catch_block;
TailCallExpressionList tail_call_expressions_in_catch_block(zone());
if (tok == Token::CATCH) {
Consume(Token::CATCH);
......@@ -2906,8 +2899,8 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
{
CollectExpressionsInTailPositionToListScope
collect_expressions_in_tail_position_scope(
function_state_, &expressions_in_tail_position_in_catch_block);
collect_tail_call_expressions_scope(
function_state_, &tail_call_expressions_in_catch_block);
BlockState block_state(&scope_, catch_scope);
// TODO(adamk): Make a version of ParseBlock that takes a scope and
......@@ -2987,8 +2980,8 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
if (catch_block != NULL) {
// For a try-catch construct append return expressions from the catch block
// to the list of return expressions.
function_state_->expressions_in_tail_position().AddAll(
expressions_in_tail_position_in_catch_block);
function_state_->tail_call_expressions().Append(
tail_call_expressions_in_catch_block);
DCHECK(finally_block == NULL);
DCHECK(catch_scope != NULL && catch_variable != NULL);
......@@ -2996,12 +2989,11 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
catch_variable, catch_block, pos);
} else {
if (FLAG_harmony_explicit_tailcalls &&
expressions_in_tail_position_in_catch_block.length() > 0) {
!tail_call_expressions_in_catch_block.is_empty()) {
// 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);
ReportMessageAt(tail_call_expressions_in_catch_block.location(),
MessageTemplate::kUnexpectedTailCallInCatchBlock);
*ok = false;
return NULL;
}
......@@ -4585,13 +4577,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
RelocInfo::kNoPosition));
}
// ES6 14.6.1 Static Semantics: IsInTailPosition
// Mark collected return expressions that are in tail call position.
const List<TailCallExpression>& expressions_in_tail_position =
function_state_->expressions_in_tail_position();
for (int i = 0; i < expressions_in_tail_position.length(); ++i) {
MarkTailPosition(expressions_in_tail_position[i].expression);
}
MarkCollectedTailCallExpressions();
return result;
}
......@@ -4666,6 +4652,7 @@ ClassLiteral* Parser::ParseClassLiteral(ExpressionClassifier* classifier,
block_scope->set_start_position(scanner()->location().end_pos);
ExpressionClassifier extends_classifier(this);
extends = ParseLeftHandSideExpression(&extends_classifier, CHECK_OK);
CheckNoTailCallExpressions(&extends_classifier, CHECK_OK);
RewriteNonPattern(&extends_classifier, CHECK_OK);
if (classifier != nullptr) {
classifier->Accumulate(&extends_classifier,
......@@ -5308,6 +5295,18 @@ void Parser::RaiseLanguageMode(LanguageMode mode) {
SetLanguageMode(scope_, old > mode ? old : mode);
}
void Parser::MarkCollectedTailCallExpressions() {
const ZoneList<Expression*>& tail_call_expressions =
function_state_->tail_call_expressions().expressions();
for (int i = 0; i < tail_call_expressions.length(); ++i) {
Expression* expression = tail_call_expressions[i];
// If only FLAG_harmony_explicit_tailcalls is enabled then expression
// must be a Call expression.
DCHECK(FLAG_harmony_tailcalls || !FLAG_harmony_explicit_tailcalls ||
expression->IsCall());
MarkTailPosition(expression);
}
}
void ParserTraits::RewriteDestructuringAssignments() {
parser_->RewriteDestructuringAssignments();
......
......@@ -573,6 +573,7 @@ class ParserTraits {
bool name_is_strict_reserved, int pos,
bool* ok);
V8_INLINE void MarkCollectedTailCallExpressions();
V8_INLINE void MarkTailPosition(Expression* expression);
V8_INLINE void CheckConflictingVarDeclarations(v8::internal::Scope* scope,
......@@ -1048,6 +1049,8 @@ class Parser : public ParserBase<ParserTraits> {
void SetLanguageMode(Scope* scope, LanguageMode mode);
void RaiseLanguageMode(LanguageMode mode);
V8_INLINE void MarkCollectedTailCallExpressions();
V8_INLINE void RewriteDestructuringAssignments();
V8_INLINE Expression* RewriteExponentiation(Expression* left,
......
......@@ -706,28 +706,21 @@ PreParser::Statement PreParser::ParseReturnStatement(bool* ok) {
// This is not handled during preparsing.
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() &&
tok != Token::SEMICOLON &&
tok != Token::RBRACE &&
tok != Token::EOS) {
// Because of the return code rewriting that happens in case of a subclass
// constructor we don't want to accept tail calls, therefore we don't set
// ReturnExprScope to kInsideValidReturnStatement here.
ReturnExprContext return_expr_context =
IsSubclassConstructor(function_state_->kind())
? function_state_->return_expr_context()
: ReturnExprContext::kInsideValidReturnStatement;
ReturnExprScope maybe_allow_tail_calls(function_state_,
return_expr_context);
ParseExpression(true, CHECK_OK);
if (tail_call_position >= 0) {
ReturnExprContext return_expr_context =
function_state_->return_expr_context();
if (return_expr_context != ReturnExprContext::kNormal) {
ReportIllegalTailCallAt(tail_call_position, return_expr_context);
*ok = false;
return Statement::Default();
}
function_state_->AddExpressionInTailPosition(
PreParserExpression::Default(), tail_call_position);
}
}
ExpectSemicolon(CHECK_OK);
return Statement::Jump();
......@@ -994,7 +987,7 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
*ok = false;
return Statement::Default();
}
List<TailCallExpression> expressions_in_tail_position_in_catch_block;
TailCallExpressionList tail_call_expressions_in_catch_block(zone());
bool catch_block_exists = false;
if (tok == Token::CATCH) {
Consume(Token::CATCH);
......@@ -1006,8 +999,8 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
Expect(Token::RPAREN, CHECK_OK);
{
CollectExpressionsInTailPositionToListScope
collect_expressions_in_tail_position_scope(
function_state_, &expressions_in_tail_position_in_catch_block);
collect_tail_call_expressions_scope(
function_state_, &tail_call_expressions_in_catch_block);
BlockState block_state(&scope_, catch_scope);
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
{
......@@ -1022,12 +1015,11 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
Consume(Token::FINALLY);
ParseBlock(CHECK_OK);
if (FLAG_harmony_explicit_tailcalls && catch_block_exists &&
expressions_in_tail_position_in_catch_block.length() > 0) {
!tail_call_expressions_in_catch_block.is_empty()) {
// 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);
ReportMessageAt(tail_call_expressions_in_catch_block.location(),
MessageTemplate::kUnexpectedTailCallInCatchBlock);
*ok = false;
return Statement::Default();
}
......@@ -1165,6 +1157,7 @@ PreParserExpression PreParser::ParseClassLiteral(
if (has_extends) {
ExpressionClassifier extends_classifier(this);
ParseLeftHandSideExpression(&extends_classifier, CHECK_OK);
CheckNoTailCallExpressions(&extends_classifier, CHECK_OK);
ValidateExpression(&extends_classifier, CHECK_OK);
if (classifier != nullptr) {
classifier->Accumulate(&extends_classifier,
......
......@@ -897,6 +897,7 @@ class PreParserTraits {
bool name_is_strict_reserved, int pos,
bool* ok);
V8_INLINE void MarkCollectedTailCallExpressions() {}
V8_INLINE void MarkTailPosition(PreParserExpression) {}
PreParserExpressionList PrepareSpreadArguments(PreParserExpressionList list) {
......
......@@ -8,6 +8,7 @@
#include "src/deoptimizer.h"
#include "src/frames-inl.h"
#include "src/full-codegen/full-codegen.h"
#include "src/isolate-inl.h"
#include "src/snapshot/natives.h"
namespace v8 {
......@@ -481,6 +482,31 @@ RUNTIME_FUNCTION(Runtime_TraceTailCall) {
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_GetExceptionDetails) {
HandleScope shs(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSObject, exception_obj, 0);
Factory* factory = isolate->factory();
Handle<JSMessageObject> message_obj =
isolate->CreateMessage(exception_obj, nullptr);
Handle<JSObject> message = factory->NewJSObject(isolate->object_function());
Handle<String> key;
Handle<Object> value;
key = factory->NewStringFromAsciiChecked("start_pos");
value = handle(Smi::FromInt(message_obj->start_position()), isolate);
JSObject::SetProperty(message, key, value, STRICT).Assert();
key = factory->NewStringFromAsciiChecked("end_pos");
value = handle(Smi::FromInt(message_obj->end_position()), isolate);
JSObject::SetProperty(message, key, value, STRICT).Assert();
return *message;
}
RUNTIME_FUNCTION(Runtime_HaveSameMap) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 2);
......
......@@ -870,6 +870,7 @@ namespace internal {
F(SetAllocationTimeout, -1 /* 2 || 3 */, 1) \
F(DebugPrint, 1, 1) \
F(DebugTrace, 0, 1) \
F(GetExceptionDetails, 1, 1) \
F(GlobalPrint, 1, 1) \
F(SystemBreak, 0, 1) \
F(SetFlags, 1, 1) \
......
// 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() {
return continue f() - a ;
}
*%(basename)s:12: SyntaxError: Tail call expression is not allowed here
return continue f() - a ;
^^^^^^^^^^^^
SyntaxError: Tail call expression is not allowed here
// 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() {
return b + continue f() ;
}
*%(basename)s:12: SyntaxError: Tail call expression is not allowed here
return b + continue f() ;
^^^^^^^^^^^^
SyntaxError: Tail call expression is not allowed here
// 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() {
return 1, 2, 3, continue f() , 4 ;
}
*%(basename)s:12: SyntaxError: Tail call expression is not allowed here
return 1, 2, 3, continue f() , 4 ;
^^^^^^^^^^^^
SyntaxError: Tail call expression is not allowed here
// 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 g() {
return class A extends continue f() {};
}
*%(basename)s:8: SyntaxError: Tail call expression is not allowed here
return class A extends continue f() {};
^^^^^^^^^^^^
SyntaxError: Tail call expression is not allowed here
......@@ -10,6 +10,6 @@ function f() {
function g() {
for (var v in {a:0}) {
return continue f();
return continue f() ;
}
}
*%(basename)s:13: SyntaxError: Tail call expression in for-in/of body.
return continue f();
^
SyntaxError: Tail call expression in for-in/of body.
*%(basename)s:13: SyntaxError: Tail call expression in for-in/of body
return continue f() ;
^^^^^^^^^^^^
SyntaxError: Tail call expression in for-in/of body
......@@ -10,6 +10,6 @@ function f() {
function g() {
for (var v of [1, 2, 3]) {
return continue f();
return continue f() ;
}
}
*%(basename)s:13: SyntaxError: Tail call expression in for-in/of body.
return continue f();
^
SyntaxError: Tail call expression in for-in/of body.
*%(basename)s:13: SyntaxError: Tail call expression in for-in/of body
return continue f() ;
^^^^^^^^^^^^
SyntaxError: Tail call expression in for-in/of body
// 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() {
return continue f() && a ;
}
*%(basename)s:12: SyntaxError: Tail call expression is not allowed here
return continue f() && a ;
^^^^^^^^^^^^
SyntaxError: Tail call expression is not allowed here
// 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() {
return continue f() || a ;
}
*%(basename)s:12: SyntaxError: Tail call expression is not allowed here
return continue f() || a ;
^^^^^^^^^^^^
SyntaxError: Tail call expression is not allowed here
// 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 g() {
class A {};
class B extends A {
constructor() {
return continue f() ;
}
}
}
*%(basename)s:11: SyntaxError: Tail call expression is not allowed here
return continue f() ;
^^^^^^^^^^^^
SyntaxError: Tail call expression is not allowed here
......@@ -12,7 +12,7 @@ function g() {
try {
f();
} catch(e) {
return continue f();
return continue f() ;
} finally {
f();
}
......
*%(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.
*%(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
......@@ -13,7 +13,7 @@ function g() {
try {
f();
} catch(e) {
return continue f();
return continue f() ;
}
} finally {
f();
......
*%(basename)s:16: SyntaxError: Tail call expression in try block.
return continue f();
^
SyntaxError: Tail call expression in try block.
*%(basename)s:16: SyntaxError: Tail call expression in try block
return continue f() ;
^^^^^^^^^^^^
SyntaxError: Tail call expression in try block
......@@ -10,7 +10,7 @@ function f() {
function g() {
try {
return continue f();
return continue f() ;
} catch(e) {
}
}
*%(basename)s:13: SyntaxError: Tail call expression in try block.
return continue f();
^
SyntaxError: Tail call expression in try block.
*%(basename)s:13: SyntaxError: Tail call expression in try block
return continue f() ;
^^^^^^^^^^^^
SyntaxError: Tail call expression in try block
// 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() {
return (continue f(1)) (2) ;
}
*%(basename)s:12: SyntaxError: Tail call expression is not allowed here
return (continue f(1)) (2) ;
^^^^^^^^^^^^^^
SyntaxError: Tail call expression is not allowed here
// 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 g(x) {
return continue x ;
}
*%(basename)s:8: SyntaxError: Unexpected expression inside tail call
return continue x ;
^
SyntaxError: Unexpected expression inside tail call
// 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() {
return continue new f() ;
}
*%(basename)s:12: SyntaxError: Unexpected expression inside tail call
return continue new f() ;
^^^^^^^
SyntaxError: Unexpected expression inside tail call
// 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() {
var x = continue f() ;
}
*%(basename)s:12: SyntaxError: Tail call expression is not allowed here
var x = continue f() ;
^^^^^^^^^^^^^
SyntaxError: Tail call expression is not allowed here
......@@ -3,6 +3,8 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// Flags: --harmony-do-expressions
"use strict";
Error.prepareStackTrace = (error,stack) => {
......@@ -235,10 +237,57 @@ function f_153(expected_call_stack, a) {
return f([f, g3, test], 13), f([f, test], 153);
}
function g4(a) {
return f([f, g4, test], false) ||
(f([f, g4, test], true) && f([f, test], true));
}
function g5(a) {
return f([f, g5, test], true) &&
(f([f, g5, test], false) || f([f, test], true));
}
function g6(a) {
return f([f, g6, test], 13), f([f, g6, test], 42),
f([f, test], 153);
}
function g7(a) {
return f([f, g7, test], false) ||
(f([f, g7, test], false) ? f([f, test], true)
: f([f, test], true));
}
function g8(a) {
return f([f, g8, test], false) || f([f, g8, test], true) &&
f([f, test], true);
}
function g9(a) {
return f([f, g9, test], true) && f([f, g9, test], false) ||
f([f, test], true);
}
function g10(a) {
return f([f, g10, test], true) && f([f, g10, test], false) ||
f([f, g10, test], true) ?
f([f, g10, test], true) && f([f, g10, test], false) ||
f([f, test], true) :
f([f, g10, test], true) && f([f, g10, test], false) ||
f([f, test], true);
}
function test() {
assertEquals(true, g1());
assertEquals(true, g2());
assertEquals(153, g3());
assertEquals(true, g4());
assertEquals(true, g5());
assertEquals(153, g6());
assertEquals(true, g7());
assertEquals(true, g8());
assertEquals(true, g9());
assertEquals(true, g10());
}
test();
test();
......@@ -450,9 +499,34 @@ function f_153(expected_call_stack, a) {
return (() => f_153([f_153, test]))();
}
function g3(a) {
var closure = () => f([f, closure, test], true)
? f_153([f_153, test])
: f_153([f_153, test]);
return closure();
}
function test() {
assertEquals(153, g1());
assertEquals(153, g2());
assertEquals(153, g3());
}
test();
test();
%OptimizeFunctionOnNextCall(test);
test();
})();
// Test tail calls from do expressions.
(function () {
function g1(a) {
var a = do { return f_153([f_153, test]); 42; };
return a;
}
function test() {
assertEquals(153, g1());
}
test();
test();
......
// 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
// Flags: --harmony-do-expressions
var SyntaxErrorTests = [
{ msg: "Unexpected expression inside tail call",
tests: [
{ src: `()=>{ return continue foo ; }`,
err: ` ^^^`,
},
{ src: `()=>{ return continue 42 ; }`,
err: ` ^^`,
},
{ src: `()=>{ return continue new foo () ; }`,
err: ` ^^^^^^^^^^`,
},
{ src: `()=>{ loop: return continue loop ; }`,
err: ` ^^^^`,
},
{ src: `class A { foo() { return continue super.x ; } }`,
err: ` ^^^^^^^`,
},
{ src: `()=>{ return continue this ; }`,
err: ` ^^^^`,
},
{ src: `()=>{ return continue class A {} ; }`,
err: ` ^^^^^^^^^^`,
},
{ src: `()=>{ return continue class A extends B {} ; }`,
err: ` ^^^^^^^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue function A() { } ; }`,
err: ` ^^^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue { a: b, c: d} ; }`,
err: ` ^^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue function* Gen() { yield 1; } ; }`,
err: ` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^`,
},
{ src: `function A() { return continue new.target ; }`,
err: ` ^^^^^^^^^^`,
},
{ src: `()=>{ return continue () ; }`,
err: ` ^^`,
},
{ src: `()=>{ return continue ( 42 ) ; }`,
err: ` ^^^^^^`,
},
{ src: "()=>{ return continue `123 ${foo} 34lk` ; }",
err: ` ^^^^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue do { x ? foo() : bar() ; } }`,
err: ` ^^^^^^^^^^^^^^^^^^^^^^^^^^`,
},
],
},
{ msg: "Tail call expression is not allowed here",
tests: [
{ src: `()=>{ return continue continue continue b() ; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue ( continue b() ) ; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue f() - a ; }`,
err: ` ^^^^^^^^^^^^^`,
},
{ src: `()=>{ return b + continue f() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return 1, 2, 3, continue f() , 4 ; }`,
err: ` ^^^^^^^^^^^^^`,
},
{ src: `()=>{ var x = continue f ( ) ; }`,
err: ` ^^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue f () ? 1 : 2 ; }`,
err: ` ^^^^^^^^^^^^^`,
},
{ src: `()=>{ return (1, 2, 3, continue f()), 4; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return [1, 2, continue f() ] ; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return [1, 2, ... continue f() ] ; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return [1, 2, continue f(), 3 ] ; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: "()=>{ return `123 ${a} ${ continue foo ( ) } 34lk` ; }",
err: ` ^^^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return g( 1, 2, continue f() ); }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue f() || a; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return a || b || c || continue f() || d; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return a && b && c && continue f() && d; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return a && b || c && continue f() ? d : e; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return a ? b : c && continue f() && d || e; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue foo() instanceof bar ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return bar instanceof continue foo() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue foo() in bar ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return bar in continue foo() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ function* G() { yield continue foo(); } }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ (1, 2, 3, continue f() ) => {} }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ (... continue f()) => {} }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ (a, b, c, ... continue f() ) => {} }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return a <= continue f(); }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return b > continue f(); }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return a << continue f(); }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return b >> continue f(); }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return c >>> continue f(); }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue f() = a ; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return a = continue f() ; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return a += continue f(); }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return a ** continue f() ; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return delete continue foo() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ typeof continue foo() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return ~ continue foo() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return void continue foo() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return !continue foo() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return -continue foo() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return +continue foo() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ return ++ continue f( ) ; }`,
err: ` ^^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue f() ++; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return continue f() --; }`,
err: ` ^^^^^^^^^^^^`,
},
{ src: `()=>{ return (continue foo()) () ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ for (var i = continue foo(); i < 10; i++) bar(); }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ for (var i = 0; i < continue foo(); i++) bar(); }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ for (var i = 0; i < 10; continue foo()) bar(); }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ if (continue foo()) bar(); }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ while (continue foo()) bar(); }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ do { smth; } while (continue foo()) ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ throw continue foo() ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ switch (continue foo()) { case 1: break; } ; }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ with (continue foo()) { smth; } }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ let x = continue foo() }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ const c = continue foo() }`,
err: ` ^^^^^^^^^^^^^^^`,
},
{ src: `class A {}; class B extends A { constructor() { return continue super () ; } }`,
err: ` ^^^^^^^^^^^^^^^^^`,
},
{ src: `class A extends continue f () {}; }`,
err: ` ^^^^^^^^^^^^^`,
},
],
},
{ msg: "Tail call expression in try block",
tests: [
{ src: `()=>{ try { return continue f ( ) ; } catch(e) {} }`,
err: ` ^^^^^^^^^^^^^^^^`,
},
{ src: `()=>{ try { try { smth; } catch(e) { return continue f( ) ; } }`,
err: ` ^^^^^^^^^^^^^^`,
},
{ src: `()=>{ try { try { smth; } catch(e) { return continue f( ) ; } } finally { bla; } }`,
err: ` ^^^^^^^^^^^^^^`,
},
],
},
{ msg: "Tail call expression in catch block when finally block is also present",
tests: [
{ src: `()=>{ try { smth; } catch(e) { return continue f ( ) ; } finally { blah; } }`,
err: ` ^^^^^^^^^^^^^^^^`,
},
{ src: `()=>{ try { smth; } catch(e) { try { smth; } catch (e) { return continue f ( ) ; } } finally { blah; } }`,
err: ` ^^^^^^^^^^^^^^^^`,
},
],
},
{ msg: "Tail call expression in for-in/of body",
tests: [
{ src: `()=>{ for (var v in {a:0}) { return continue foo () ; } }`,
err: ` ^^^^^^^^^^^^^^^^`,
},
{ src: `()=>{ for (var v of [1, 2, 3]) { return continue foo () ; } }`,
err: ` ^^^^^^^^^^^^^^^^`,
},
],
},
{ msg: "Undefined label 'foo'",
tests: [
{ src: `()=>{ continue foo () ; }`,
err: ` ^^^`,
},
],
},
];
// Should parse successfully.
var NoErrorTests = [
`()=>{ return continue a.b.c.foo () ; }`,
`()=>{ return continue a().b.c().d.foo () ; }`,
`()=>{ return continue foo (1)(2)(3, 4) ; }`,
`()=>{ return ( continue b() ) ; }`,
"()=>{ return continue bar`ab cd ef` ; }",
"()=>{ return continue bar`ab ${cd} ef` ; }",
`()=>{ return a || continue f() ; }`,
`()=>{ return a && continue f() ; }`,
`()=>{ return a , continue f() ; }`,
`()=>{ function* G() { return continue foo(); } }`,
`()=>{ class A { foo() { return continue super.f() ; } } }`,
`()=>{ function B() { return continue new.target() ; } }`,
`()=>{ return continue do { x ? foo() : bar() ; }() }`,
`()=>{ return continue (do { x ? foo() : bar() ; })() }`,
`()=>{ return do { 1, continue foo() } }`,
`()=>{ return do { x ? continue foo() : y } }`,
`()=>{ return a || (b && continue c()); }`,
`()=>{ return a && (b || continue c()); }`,
`()=>{ return a || (b ? c : continue d()); }`,
`()=>{ return 1, 2, 3, a || (b ? c : continue d()); }`,
`()=> continue (foo ()) ;`,
`()=> a || continue foo () ;`,
`()=> a && continue foo () ;`,
`()=> a ? continue foo () : b;`,
];
(function() {
for (test_set of SyntaxErrorTests) {
var expected_message = "SyntaxError: " + test_set.msg;
for (test of test_set.tests) {
var passed = true;
var e = null;
try {
Realm.eval(0, test.src);
} catch (ee) {
e = ee;
}
print("=======================================");
print("Expected | " + expected_message);
print("Source | " + test.src);
print(" | " + test.err);
if (e === null) {
print("FAILED");
throw new Error("SyntaxError was not thrown");
}
var details = %GetExceptionDetails(e);
if (details.start_pos == undefined ||
details.end_pos == undefined) {
throw new Error("Bad message object returned");
}
var underline = " ".repeat(details.start_pos) +
"^".repeat(details.end_pos - details.start_pos);
var passed = expected_message === e.toString() &&
test.err === underline;
if (passed) {
print("PASSED");
print();
} else {
print("---------------------------------------");
print("Actual | " + e);
print("Source | " + test.src);
print(" | " + underline);
print("FAILED");
throw new Error("Test failed");
}
}
}
})();
(function() {
for (src of NoErrorTests) {
print("=======================================");
print("Source | " + src);
Realm.eval(0, src);
print("PASSED");
print();
}
})();
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-explicit-tailcalls
// Flags: --harmony-do-expressions
Error.prepareStackTrace = (error,stack) => {
error.strace = stack;
......@@ -223,21 +224,68 @@ function f_153(expected_call_stack, a) {
// Tail calling via various expressions.
(function() {
function g1(a) {
return continue f([f, g1, test], false) || f([f, test], true);
return f([f, g1, test], false) || continue f([f, test], true);
}
function g2(a) {
return continue f([f, g2, test], true) && f([f, test], true);
return f([f, g2, test], true) && continue f([f, test], true);
}
function g3(a) {
return continue f([f, g3, test], 13), f([f, test], 153);
return f([f, g3, test], 13), continue f([f, test], 153);
}
function g4(a) {
return f([f, g4, test], false) ||
(f([f, g4, test], true) && continue f([f, test], true));
}
function g5(a) {
return f([f, g5, test], true) &&
(f([f, g5, test], false) || continue f([f, test], true));
}
function g6(a) {
return f([f, g6, test], 13), f([f, g6, test], 42),
continue f([f, test], 153);
}
function g7(a) {
return f([f, g7, test], false) ||
(f([f, g7, test], false) ? continue f([f, test], true)
: continue f([f, test], true));
}
function g8(a) {
return f([f, g8, test], false) || f([f, g8, test], true) &&
continue f([f, test], true);
}
function g9(a) {
return f([f, g9, test], true) && f([f, g9, test], false) ||
continue f([f, test], true);
}
function g10(a) {
return f([f, g10, test], true) && f([f, g10, test], false) ||
f([f, g10, test], true) ?
f([f, g10, test], true) && f([f, g10, test], false) ||
continue f([f, test], true) :
f([f, g10, test], true) && f([f, g10, test], false) ||
continue f([f, test], true);
}
function test() {
assertEquals(true, g1());
assertEquals(true, g2());
assertEquals(153, g3());
assertEquals(true, g4());
assertEquals(true, g5());
assertEquals(153, g6());
assertEquals(true, g7());
assertEquals(true, g8());
assertEquals(true, g9());
assertEquals(true, g10());
}
test();
test();
......@@ -404,10 +452,9 @@ function f_153(expected_call_stack, a) {
}
function g3(a) {
var closure = () => continue f([f, closure, test], true)
? f_153([f_153, test])
: f_153([f_153, test]);
var closure = () => f([f, closure, test], true)
? continue f_153([f_153, test])
: continue f_153([f_153, test]);
return continue closure();
}
......@@ -421,3 +468,20 @@ function f_153(expected_call_stack, a) {
%OptimizeFunctionOnNextCall(test);
test();
})();
// Test tail calls from do expressions.
(function () {
function g1(a) {
var a = do { return continue f_153([f_153, test]); 42; };
return a;
}
function test() {
assertEquals(153, g1());
}
test();
test();
%OptimizeFunctionOnNextCall(test);
test();
})();
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