[es6] More efficient way of marking AST call expressions in tail positions.
Instead of doing a full function body traversal we collect return expressions and mark them after function parsing. And since we rewrite do-expressions so that the result is explicitly assigned to a result variable the statements marking will never hit so I removed it from the AST. BUG=v8:4698 LOG=N Review URL: https://codereview.chromium.org/1693523002 Cr-Commit-Position: refs/heads/master@{#33911}
This commit is contained in:
parent
879d254d54
commit
d12dbab466
@ -244,7 +244,6 @@ class Statement : public AstNode {
|
|||||||
|
|
||||||
bool IsEmpty() { return AsEmptyStatement() != NULL; }
|
bool IsEmpty() { return AsEmptyStatement() != NULL; }
|
||||||
virtual bool IsJump() const { return false; }
|
virtual bool IsJump() const { return false; }
|
||||||
virtual void MarkTail() {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -472,13 +471,6 @@ class Block final : public BreakableStatement {
|
|||||||
&& labels() == NULL; // Good enough as an approximation...
|
&& labels() == NULL; // Good enough as an approximation...
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkTail() override {
|
|
||||||
for (int i = 0; i < statements_.length(); i++) {
|
|
||||||
Statement* stmt = statements_.at(i);
|
|
||||||
stmt->MarkTail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope* scope() const { return scope_; }
|
Scope* scope() const { return scope_; }
|
||||||
void set_scope(Scope* scope) { scope_ = scope; }
|
void set_scope(Scope* scope) { scope_ = scope; }
|
||||||
|
|
||||||
@ -509,8 +501,6 @@ class DoExpression final : public Expression {
|
|||||||
VariableProxy* result() { return result_; }
|
VariableProxy* result() { return result_; }
|
||||||
void set_result(VariableProxy* v) { result_ = v; }
|
void set_result(VariableProxy* v) { result_ = v; }
|
||||||
|
|
||||||
void MarkTail() override { block_->MarkTail(); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DoExpression(Zone* zone, Block* block, VariableProxy* result, int pos)
|
DoExpression(Zone* zone, Block* block, VariableProxy* result, int pos)
|
||||||
: Expression(zone, pos), block_(block), result_(result) {
|
: Expression(zone, pos), block_(block), result_(result) {
|
||||||
@ -1022,8 +1012,6 @@ class ReturnStatement final : public JumpStatement {
|
|||||||
|
|
||||||
void set_expression(Expression* e) { expression_ = e; }
|
void set_expression(Expression* e) { expression_ = e; }
|
||||||
|
|
||||||
void MarkTail() override { expression_->MarkTail(); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit ReturnStatement(Zone* zone, Expression* expression, int pos)
|
explicit ReturnStatement(Zone* zone, Expression* expression, int pos)
|
||||||
: JumpStatement(zone, pos), expression_(expression) { }
|
: JumpStatement(zone, pos), expression_(expression) { }
|
||||||
@ -1048,8 +1036,6 @@ class WithStatement final : public Statement {
|
|||||||
BailoutId ToObjectId() const { return BailoutId(local_id(0)); }
|
BailoutId ToObjectId() const { return BailoutId(local_id(0)); }
|
||||||
BailoutId EntryId() const { return BailoutId(local_id(1)); }
|
BailoutId EntryId() const { return BailoutId(local_id(1)); }
|
||||||
|
|
||||||
void MarkTail() override { statement_->MarkTail(); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
WithStatement(Zone* zone, Scope* scope, Expression* expression,
|
WithStatement(Zone* zone, Scope* scope, Expression* expression,
|
||||||
Statement* statement, int pos)
|
Statement* statement, int pos)
|
||||||
@ -1092,13 +1078,6 @@ class CaseClause final : public Expression {
|
|||||||
BailoutId EntryId() const { return BailoutId(local_id(0)); }
|
BailoutId EntryId() const { return BailoutId(local_id(0)); }
|
||||||
TypeFeedbackId CompareId() { return TypeFeedbackId(local_id(1)); }
|
TypeFeedbackId CompareId() { return TypeFeedbackId(local_id(1)); }
|
||||||
|
|
||||||
void MarkTail() override {
|
|
||||||
for (int i = 0; i < statements_->length(); i++) {
|
|
||||||
Statement* stmt = statements_->at(i);
|
|
||||||
stmt->MarkTail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Type* compare_type() { return compare_type_; }
|
Type* compare_type() { return compare_type_; }
|
||||||
void set_compare_type(Type* type) { compare_type_ = type; }
|
void set_compare_type(Type* type) { compare_type_ = type; }
|
||||||
|
|
||||||
@ -1131,13 +1110,6 @@ class SwitchStatement final : public BreakableStatement {
|
|||||||
|
|
||||||
void set_tag(Expression* t) { tag_ = t; }
|
void set_tag(Expression* t) { tag_ = t; }
|
||||||
|
|
||||||
void MarkTail() override {
|
|
||||||
for (int i = 0; i < cases_->length(); i++) {
|
|
||||||
CaseClause* clause = cases_->at(i);
|
|
||||||
clause->MarkTail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SwitchStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos)
|
SwitchStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos)
|
||||||
: BreakableStatement(zone, labels, TARGET_FOR_ANONYMOUS, pos),
|
: BreakableStatement(zone, labels, TARGET_FOR_ANONYMOUS, pos),
|
||||||
@ -1175,11 +1147,6 @@ class IfStatement final : public Statement {
|
|||||||
&& HasElseStatement() && else_statement()->IsJump();
|
&& HasElseStatement() && else_statement()->IsJump();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkTail() override {
|
|
||||||
then_statement_->MarkTail();
|
|
||||||
else_statement_->MarkTail();
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_base_id(int id) { base_id_ = id; }
|
void set_base_id(int id) { base_id_ = id; }
|
||||||
static int num_ids() { return parent_num_ids() + 3; }
|
static int num_ids() { return parent_num_ids() + 3; }
|
||||||
BailoutId IfId() const { return BailoutId(local_id(0)); }
|
BailoutId IfId() const { return BailoutId(local_id(0)); }
|
||||||
@ -1249,8 +1216,6 @@ class TryCatchStatement final : public TryStatement {
|
|||||||
Block* catch_block() const { return catch_block_; }
|
Block* catch_block() const { return catch_block_; }
|
||||||
void set_catch_block(Block* b) { catch_block_ = b; }
|
void set_catch_block(Block* b) { catch_block_ = b; }
|
||||||
|
|
||||||
void MarkTail() override { catch_block_->MarkTail(); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TryCatchStatement(Zone* zone, Block* try_block, Scope* scope,
|
TryCatchStatement(Zone* zone, Block* try_block, Scope* scope,
|
||||||
Variable* variable, Block* catch_block, int pos)
|
Variable* variable, Block* catch_block, int pos)
|
||||||
@ -1273,8 +1238,6 @@ class TryFinallyStatement final : public TryStatement {
|
|||||||
Block* finally_block() const { return finally_block_; }
|
Block* finally_block() const { return finally_block_; }
|
||||||
void set_finally_block(Block* b) { finally_block_ = b; }
|
void set_finally_block(Block* b) { finally_block_ = b; }
|
||||||
|
|
||||||
void MarkTail() override { finally_block_->MarkTail(); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TryFinallyStatement(Zone* zone, Block* try_block, Block* finally_block,
|
TryFinallyStatement(Zone* zone, Block* try_block, Block* finally_block,
|
||||||
int pos)
|
int pos)
|
||||||
|
@ -246,6 +246,22 @@ class ParserBase : public Traits {
|
|||||||
destructuring_assignments_to_rewrite_.Add(pair);
|
destructuring_assignments_to_rewrite_.Add(pair);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<ExpressionT>& expressions_in_tail_position() {
|
||||||
|
return expressions_in_tail_position_;
|
||||||
|
}
|
||||||
|
void AddExpressionInTailPosition(ExpressionT expression) {
|
||||||
|
if (collect_expressions_in_tail_position_) {
|
||||||
|
expressions_in_tail_position_.Add(expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool collect_expressions_in_tail_position() const {
|
||||||
|
return collect_expressions_in_tail_position_;
|
||||||
|
}
|
||||||
|
void set_collect_expressions_in_tail_position(bool collect) {
|
||||||
|
collect_expressions_in_tail_position_ = collect;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Used to assign an index to each literal that needs materialization in
|
// Used to assign an index to each literal that needs materialization in
|
||||||
// the function. Includes regexp literals, and boilerplate for object and
|
// the function. Includes regexp literals, and boilerplate for object and
|
||||||
@ -276,6 +292,8 @@ 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_;
|
||||||
|
bool collect_expressions_in_tail_position_;
|
||||||
|
|
||||||
void RewriteDestructuringAssignments();
|
void RewriteDestructuringAssignments();
|
||||||
|
|
||||||
@ -933,7 +951,6 @@ class ParserBase : public Traits {
|
|||||||
bool allow_harmony_function_name_;
|
bool allow_harmony_function_name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template <class Traits>
|
template <class Traits>
|
||||||
ParserBase<Traits>::FunctionState::FunctionState(
|
ParserBase<Traits>::FunctionState::FunctionState(
|
||||||
FunctionState** function_state_stack, Scope** scope_stack, Scope* scope,
|
FunctionState** function_state_stack, Scope** scope_stack, Scope* scope,
|
||||||
@ -949,6 +966,7 @@ ParserBase<Traits>::FunctionState::FunctionState(
|
|||||||
outer_function_state_(*function_state_stack),
|
outer_function_state_(*function_state_stack),
|
||||||
scope_stack_(scope_stack),
|
scope_stack_(scope_stack),
|
||||||
outer_scope_(*scope_stack),
|
outer_scope_(*scope_stack),
|
||||||
|
collect_expressions_in_tail_position_(true),
|
||||||
factory_(factory) {
|
factory_(factory) {
|
||||||
*scope_stack_ = scope;
|
*scope_stack_ = scope;
|
||||||
*function_state_stack = this;
|
*function_state_stack = this;
|
||||||
|
@ -2792,6 +2792,11 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
|
|||||||
is_undefined, ThisExpression(scope_, factory(), pos),
|
is_undefined, ThisExpression(scope_, factory(), pos),
|
||||||
is_object_conditional, pos);
|
is_object_conditional, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
||||||
|
if (FLAG_harmony_tailcalls && !is_sloppy(language_mode())) {
|
||||||
|
function_state_->AddExpressionInTailPosition(return_value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ExpectSemicolon(CHECK_OK);
|
ExpectSemicolon(CHECK_OK);
|
||||||
|
|
||||||
@ -2997,6 +3002,40 @@ 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 ::
|
||||||
@ -3013,7 +3052,11 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
|
|||||||
Expect(Token::TRY, CHECK_OK);
|
Expect(Token::TRY, CHECK_OK);
|
||||||
int pos = position();
|
int pos = position();
|
||||||
|
|
||||||
Block* try_block = ParseBlock(NULL, CHECK_OK);
|
Block* try_block;
|
||||||
|
{
|
||||||
|
DontCollectExpressionsInTailPositionScope no_tail_calls(function_state_);
|
||||||
|
try_block = ParseBlock(NULL, CHECK_OK);
|
||||||
|
}
|
||||||
|
|
||||||
Token::Value tok = peek();
|
Token::Value tok = peek();
|
||||||
if (tok != Token::CATCH && tok != Token::FINALLY) {
|
if (tok != Token::CATCH && tok != Token::FINALLY) {
|
||||||
@ -3025,6 +3068,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;
|
||||||
if (tok == Token::CATCH) {
|
if (tok == Token::CATCH) {
|
||||||
Consume(Token::CATCH);
|
Consume(Token::CATCH);
|
||||||
|
|
||||||
@ -3050,6 +3094,9 @@ TryStatement* Parser::ParseTryStatement(bool* 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);
|
||||||
|
|
||||||
// TODO(adamk): Make a version of ParseBlock that takes a scope and
|
// TODO(adamk): Make a version of ParseBlock that takes a scope and
|
||||||
@ -3124,6 +3171,11 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
|
|||||||
|
|
||||||
TryStatement* result = NULL;
|
TryStatement* result = NULL;
|
||||||
if (catch_block != NULL) {
|
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);
|
||||||
|
|
||||||
DCHECK(finally_block == NULL);
|
DCHECK(finally_block == NULL);
|
||||||
DCHECK(catch_scope != NULL && catch_variable != NULL);
|
DCHECK(catch_scope != NULL && catch_variable != NULL);
|
||||||
result = factory()->NewTryCatchStatement(try_block, catch_scope,
|
result = factory()->NewTryCatchStatement(try_block, catch_scope,
|
||||||
@ -4751,11 +4803,11 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
||||||
if (FLAG_harmony_tailcalls && !is_sloppy(language_mode())) {
|
// Mark collected return expressions that are in tail call position.
|
||||||
for (int i = 0; i < body->length(); i++) {
|
const List<Expression*>& expressions_in_tail_position =
|
||||||
Statement* stmt = body->at(i);
|
function_state_->expressions_in_tail_position();
|
||||||
stmt->MarkTail();
|
for (int i = 0; i < expressions_in_tail_position.length(); ++i) {
|
||||||
}
|
expressions_in_tail_position[i]->MarkTail();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -902,6 +902,8 @@ 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);
|
||||||
|
|
||||||
|
@ -15,6 +15,16 @@ function CheckStackTrace(expected) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// Tail call when caller does not have an arguments adaptor frame.
|
||||||
(function test() {
|
(function test() {
|
||||||
@ -175,3 +185,149 @@ function CheckStackTrace(expected) {
|
|||||||
function g4(a) { return b4(2); }
|
function g4(a) { return b4(2); }
|
||||||
assertEquals(12, g4());
|
assertEquals(12, g4());
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Tail calling via various expressions.
|
||||||
|
(function test() {
|
||||||
|
function g1(a) {
|
||||||
|
return f([f, g1, test], false) || f([f, test], true);
|
||||||
|
}
|
||||||
|
assertEquals(true, g1());
|
||||||
|
|
||||||
|
function g2(a) {
|
||||||
|
return f([f, g2, test], true) && f([f, test], true);
|
||||||
|
}
|
||||||
|
assertEquals(true, g2());
|
||||||
|
|
||||||
|
function g3(a) {
|
||||||
|
return f([f, g3, test], 13), f([f, test], 153);
|
||||||
|
}
|
||||||
|
assertEquals(153, g3());
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Test tail calls from try-catch-finally constructs.
|
||||||
|
(function test() {
|
||||||
|
//
|
||||||
|
// try-catch
|
||||||
|
//
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(153, tc1());
|
||||||
|
|
||||||
|
function tc2(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tc2, test]);
|
||||||
|
throw new Error("boom");
|
||||||
|
} catch(e) {
|
||||||
|
f_153([f_153, tc2, test]);
|
||||||
|
return f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(153, tc2());
|
||||||
|
|
||||||
|
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 f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
assertEquals(153, tc3());
|
||||||
|
|
||||||
|
//
|
||||||
|
// try-finally
|
||||||
|
//
|
||||||
|
function tf1(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tf1, test]);
|
||||||
|
return f_153([f_153, tf1, test]);
|
||||||
|
} finally {
|
||||||
|
f_153([f_153, tf1, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(153, tf1());
|
||||||
|
|
||||||
|
function tf2(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tf2, test]);
|
||||||
|
throw new Error("boom");
|
||||||
|
} finally {
|
||||||
|
f_153([f_153, tf2, test]);
|
||||||
|
return f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(153, tf2());
|
||||||
|
|
||||||
|
function tf3(a) {
|
||||||
|
try {
|
||||||
|
f_153([f_153, tf3, test]);
|
||||||
|
} finally {
|
||||||
|
f_153([f_153, tf3, test]);
|
||||||
|
}
|
||||||
|
return f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
assertEquals(153, tf3());
|
||||||
|
|
||||||
|
//
|
||||||
|
// try-catch-finally
|
||||||
|
//
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(153, tcf1());
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(153, tcf2());
|
||||||
|
|
||||||
|
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 f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(153, tcf3());
|
||||||
|
|
||||||
|
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 f_153([f_153, test]);
|
||||||
|
}
|
||||||
|
assertEquals(153, tcf4());
|
||||||
|
})();
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
# Issue 4698: not fully supported by Turbofan yet
|
# Issue 4698: not fully supported by Turbofan yet
|
||||||
'es6/tail-call-simple': [SKIP],
|
'es6/tail-call-simple': [SKIP],
|
||||||
|
'es6/tail-call': [PASS, NO_VARIANTS],
|
||||||
|
|
||||||
# Issue 3389: deopt_every_n_garbage_collections is unsafe
|
# Issue 3389: deopt_every_n_garbage_collections is unsafe
|
||||||
'regress/regress-2653': [SKIP],
|
'regress/regress-2653': [SKIP],
|
||||||
|
Loading…
Reference in New Issue
Block a user