[torque] Allow atomarStatements in otherwise statements
In the process: - Convert TryLabelStatements into TryLabelExpressions - Change TryLabelExpressions to support only single label blocks and de-sugar try/labels into nested try/label statements. This allows the code in a label block to goto subsequent labels in the same try/label statement. - Make otherwise expressions either take IdentifierExpressions which get converted into simple label names OR atomarStatements, which make useful non-label operations, like 'break' and 'continue', useful together with otherwise. Non-label otherwise statements get de-sugared into try/label blocks. Bug: v8:7793 Change-Id: Ie56ede6306e2a3182f6aa1bb8750ed418bda01db Reviewed-on: https://chromium-review.googlesource.com/c/1266997 Commit-Queue: Daniel Clifford <danno@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#56447}
This commit is contained in:
parent
3efbaf8eb1
commit
6f5600e256
@ -968,21 +968,19 @@ macro ToIndex(input: Object, context: Context): Number
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro GetLengthProperty(context: Context, o: Object): Number {
|
macro GetLengthProperty(context: Context, o: Object): Number {
|
||||||
let length: Object;
|
|
||||||
try {
|
try {
|
||||||
try {
|
return (Cast<JSArray>(o) otherwise CheckArgs).length;
|
||||||
return (Cast<JSArray>(o) otherwise CheckArgs).length;
|
}
|
||||||
}
|
label CheckArgs {
|
||||||
label CheckArgs {
|
const a: JSArgumentsObjectWithLength =
|
||||||
const a: JSArgumentsObjectWithLength =
|
Cast<JSArgumentsObjectWithLength>(context, o) otherwise Slow;
|
||||||
Cast<JSArgumentsObjectWithLength>(context, o) otherwise Slow;
|
const length: Object = a.length;
|
||||||
return Cast<Smi>(length = a.length) otherwise ToLength;
|
return Cast<Smi>(length) otherwise goto ToLength(length);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
label Slow deferred {
|
label Slow deferred {
|
||||||
return ToLength_Inline(context, GetProperty(context, o, kLengthString));
|
goto ToLength(GetProperty(context, o, kLengthString));
|
||||||
}
|
}
|
||||||
label ToLength deferred {
|
label ToLength(length: Object) deferred {
|
||||||
return ToLength_Inline(context, length);
|
return ToLength_Inline(context, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,9 @@ namespace torque {
|
|||||||
V(ElementAccessExpression) \
|
V(ElementAccessExpression) \
|
||||||
V(AssignmentExpression) \
|
V(AssignmentExpression) \
|
||||||
V(IncrementDecrementExpression) \
|
V(IncrementDecrementExpression) \
|
||||||
V(AssumeTypeImpossibleExpression)
|
V(AssumeTypeImpossibleExpression) \
|
||||||
|
V(StatementExpression) \
|
||||||
|
V(TryLabelExpression)
|
||||||
|
|
||||||
#define AST_TYPE_EXPRESSION_NODE_KIND_LIST(V) \
|
#define AST_TYPE_EXPRESSION_NODE_KIND_LIST(V) \
|
||||||
V(BasicTypeExpression) \
|
V(BasicTypeExpression) \
|
||||||
@ -52,7 +54,6 @@ namespace torque {
|
|||||||
V(TailCallStatement) \
|
V(TailCallStatement) \
|
||||||
V(VarDeclarationStatement) \
|
V(VarDeclarationStatement) \
|
||||||
V(GotoStatement) \
|
V(GotoStatement) \
|
||||||
V(TryLabelStatement)
|
|
||||||
|
|
||||||
#define AST_DECLARATION_NODE_KIND_LIST(V) \
|
#define AST_DECLARATION_NODE_KIND_LIST(V) \
|
||||||
V(TypeDeclaration) \
|
V(TypeDeclaration) \
|
||||||
@ -553,15 +554,22 @@ struct LabelBlock : AstNode {
|
|||||||
Statement* body;
|
Statement* body;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TryLabelStatement : Statement {
|
struct StatementExpression : Expression {
|
||||||
DEFINE_AST_NODE_LEAF_BOILERPLATE(TryLabelStatement)
|
DEFINE_AST_NODE_LEAF_BOILERPLATE(StatementExpression)
|
||||||
TryLabelStatement(SourcePosition pos, Statement* try_block,
|
StatementExpression(SourcePosition pos, Statement* statement)
|
||||||
std::vector<LabelBlock*> label_blocks)
|
: Expression(kKind, pos), statement(statement) {}
|
||||||
: Statement(kKind, pos),
|
Statement* statement;
|
||||||
try_block(try_block),
|
};
|
||||||
label_blocks(std::move(label_blocks)) {}
|
|
||||||
Statement* try_block;
|
struct TryLabelExpression : Expression {
|
||||||
std::vector<LabelBlock*> label_blocks;
|
DEFINE_AST_NODE_LEAF_BOILERPLATE(TryLabelExpression)
|
||||||
|
TryLabelExpression(SourcePosition pos, Expression* try_expression,
|
||||||
|
LabelBlock* label_block)
|
||||||
|
: Expression(kKind, pos),
|
||||||
|
try_expression(try_expression),
|
||||||
|
label_block(label_block) {}
|
||||||
|
Expression* try_expression;
|
||||||
|
LabelBlock* label_block;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlockStatement : Statement {
|
struct BlockStatement : Statement {
|
||||||
|
@ -402,14 +402,15 @@ void DeclarationVisitor::Visit(ForLoopStatement* stmt) {
|
|||||||
if (stmt->action) Visit(*stmt->action);
|
if (stmt->action) Visit(*stmt->action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclarationVisitor::Visit(TryLabelStatement* stmt) {
|
void DeclarationVisitor::Visit(TryLabelExpression* stmt) {
|
||||||
// Activate a new scope to declare handler labels, they should not be
|
// Activate a new scope to declare the handler's label parameters, they should
|
||||||
// visible outside the label block.
|
// not be visible outside the label block.
|
||||||
{
|
{
|
||||||
Declarations::NodeScopeActivator scope(declarations(), stmt);
|
Declarations::NodeScopeActivator scope(declarations(), stmt);
|
||||||
|
|
||||||
// Declare labels
|
// Declare label
|
||||||
for (LabelBlock* block : stmt->label_blocks) {
|
{
|
||||||
|
LabelBlock* block = stmt->label_block;
|
||||||
CurrentSourcePosition::Scope scope(block->pos);
|
CurrentSourcePosition::Scope scope(block->pos);
|
||||||
Label* shared_label =
|
Label* shared_label =
|
||||||
declarations()->DeclareLabel(block->label, block->body);
|
declarations()->DeclareLabel(block->label, block->body);
|
||||||
@ -432,18 +433,16 @@ void DeclarationVisitor::Visit(TryLabelStatement* stmt) {
|
|||||||
shared_label->AddVariable(DeclareVariable(p, type, false));
|
shared_label->AddVariable(DeclareVariable(p, type, false));
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
if (global_context_.verbose()) {
|
||||||
if (global_context_.verbose()) {
|
std::cout << " declaring label " << block->label << "\n";
|
||||||
std::cout << " declaring label " << block->label << "\n";
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Visit(stmt->try_block);
|
Visit(stmt->try_expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (LabelBlock* block : stmt->label_blocks) {
|
Visit(stmt->label_block->body);
|
||||||
Visit(block->body);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclarationVisitor::GenerateHeader(std::string& file_name) {
|
void DeclarationVisitor::GenerateHeader(std::string& file_name) {
|
||||||
@ -516,6 +515,10 @@ void DeclarationVisitor::Visit(IdentifierExpression* expr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeclarationVisitor::Visit(StatementExpression* expr) {
|
||||||
|
Visit(expr->statement);
|
||||||
|
}
|
||||||
|
|
||||||
void DeclarationVisitor::Visit(CallExpression* expr) {
|
void DeclarationVisitor::Visit(CallExpression* expr) {
|
||||||
Visit(&expr->callee);
|
Visit(&expr->callee);
|
||||||
for (Expression* arg : expr->arguments) Visit(arg);
|
for (Expression* arg : expr->arguments) Visit(arg);
|
||||||
|
@ -139,7 +139,8 @@ class DeclarationVisitor : public FileVisitor {
|
|||||||
|
|
||||||
void Visit(AssumeTypeImpossibleExpression* expr) { Visit(expr->expression); }
|
void Visit(AssumeTypeImpossibleExpression* expr) { Visit(expr->expression); }
|
||||||
|
|
||||||
void Visit(TryLabelStatement* stmt);
|
void Visit(TryLabelExpression* stmt);
|
||||||
|
void Visit(StatementExpression* stmt);
|
||||||
void GenerateHeader(std::string& file_name);
|
void GenerateHeader(std::string& file_name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -80,6 +80,7 @@ void ImplementationVisitor::BeginModuleFile(Module* module) {
|
|||||||
DashifyString(module->name()) + "-gen.h\"";
|
DashifyString(module->name()) + "-gen.h\"";
|
||||||
}
|
}
|
||||||
source << "\n";
|
source << "\n";
|
||||||
|
source << "#include \"src/objects/arguments.h\"\n";
|
||||||
source << "#include \"src/builtins/builtins-utils-gen.h\"\n";
|
source << "#include \"src/builtins/builtins-utils-gen.h\"\n";
|
||||||
source << "#include \"src/builtins/builtins.h\"\n";
|
source << "#include \"src/builtins/builtins.h\"\n";
|
||||||
source << "#include \"src/code-factory.h\"\n";
|
source << "#include \"src/code-factory.h\"\n";
|
||||||
@ -1081,19 +1082,19 @@ const Type* ImplementationVisitor::Visit(ForOfLoopStatement* stmt) {
|
|||||||
return TypeOracle::GetVoidType();
|
return TypeOracle::GetVoidType();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Type* ImplementationVisitor::Visit(TryLabelStatement* stmt) {
|
VisitResult ImplementationVisitor::Visit(TryLabelExpression* expr) {
|
||||||
Block* done_block = assembler().NewBlock();
|
Block* done_block = assembler().NewBlock();
|
||||||
const Type* try_result = TypeOracle::GetNeverType();
|
VisitResult try_result;
|
||||||
std::vector<Label*> labels;
|
Label* label = nullptr;
|
||||||
|
|
||||||
// Output labels for the goto handlers and for the merge after the try.
|
// Output labels for the goto handlers and for the merge after the try.
|
||||||
{
|
{
|
||||||
// Activate a new scope to see handler labels
|
// Activate a new scope to see handler labels
|
||||||
Declarations::NodeScopeActivator scope(declarations(), stmt);
|
Declarations::NodeScopeActivator scope(declarations(), expr);
|
||||||
for (LabelBlock* block : stmt->label_blocks) {
|
{
|
||||||
|
LabelBlock* block = expr->label_block;
|
||||||
CurrentSourcePosition::Scope source_position(block->pos);
|
CurrentSourcePosition::Scope source_position(block->pos);
|
||||||
Label* label = declarations()->LookupLabel(block->label);
|
label = declarations()->LookupLabel(block->label);
|
||||||
labels.push_back(label);
|
|
||||||
|
|
||||||
Declarations::NodeScopeActivator scope(declarations(), block->body);
|
Declarations::NodeScopeActivator scope(declarations(), block->body);
|
||||||
Stack<const Type*> label_input_stack = assembler().CurrentStack();
|
Stack<const Type*> label_input_stack = assembler().CurrentStack();
|
||||||
@ -1108,42 +1109,48 @@ const Type* ImplementationVisitor::Visit(TryLabelStatement* stmt) {
|
|||||||
// Visit try
|
// Visit try
|
||||||
{
|
{
|
||||||
StackScope stack_scope(this);
|
StackScope stack_scope(this);
|
||||||
try_result = Visit(stmt->try_block);
|
try_result = Visit(expr->try_expression);
|
||||||
|
if (try_result.type() != TypeOracle::GetNeverType()) {
|
||||||
|
try_result = stack_scope.Yield(try_result);
|
||||||
|
assembler().Goto(done_block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (try_result != TypeOracle::GetNeverType()) {
|
}
|
||||||
|
|
||||||
|
if (label->IsUsed()) {
|
||||||
|
// Visit and output the code for the label block. If the label block falls
|
||||||
|
// through, then the try must not return a value. Also, if the try doesn't
|
||||||
|
// fall through, but the label does, then overall the try-label block
|
||||||
|
// returns type void.
|
||||||
|
GenerateLabelBind(label);
|
||||||
|
const Type* label_result;
|
||||||
|
{
|
||||||
|
StackScope stack_scope(this);
|
||||||
|
label_result = Visit(expr->label_block->body);
|
||||||
|
}
|
||||||
|
if (!try_result.type()->IsVoidOrNever() && label_result->IsVoid()) {
|
||||||
|
ReportError(
|
||||||
|
"otherwise clauses cannot fall through in a non-void expression");
|
||||||
|
}
|
||||||
|
if (label_result != TypeOracle::GetNeverType()) {
|
||||||
assembler().Goto(done_block);
|
assembler().Goto(done_block);
|
||||||
}
|
}
|
||||||
}
|
if (label_result->IsVoid() && try_result.type()->IsNever()) {
|
||||||
|
try_result =
|
||||||
// Make sure that each label clause is actually used. It's not just a friendly
|
VisitResult(TypeOracle::GetVoidType(), try_result.stack_range());
|
||||||
// thing to do, it will cause problems downstream in the compiler if there are
|
|
||||||
// bound labels that are never jumped to.
|
|
||||||
auto label_iterator = stmt->label_blocks.begin();
|
|
||||||
for (auto label : labels) {
|
|
||||||
CurrentSourcePosition::Scope scope((*label_iterator)->pos);
|
|
||||||
if (!label->IsUsed()) {
|
|
||||||
std::stringstream s;
|
|
||||||
s << "label ";
|
|
||||||
s << (*label_iterator)->label;
|
|
||||||
s << " has a handler block but is never referred to in try block";
|
|
||||||
ReportError(s.str());
|
|
||||||
}
|
}
|
||||||
label_iterator++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visit and output the code for each label block, one-by-one.
|
if (!try_result.type()->IsNever()) {
|
||||||
std::vector<Statement*> bodies;
|
|
||||||
for (LabelBlock* block : stmt->label_blocks) bodies.push_back(block->body);
|
|
||||||
if (GenerateLabeledStatementBlocks(bodies, labels, done_block)) {
|
|
||||||
try_result = TypeOracle::GetVoidType();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!try_result->IsNever()) {
|
|
||||||
assembler().Bind(done_block);
|
assembler().Bind(done_block);
|
||||||
}
|
}
|
||||||
return try_result;
|
return try_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VisitResult ImplementationVisitor::Visit(StatementExpression* expr) {
|
||||||
|
return VisitResult{Visit(expr->statement), assembler().TopRange(0)};
|
||||||
|
}
|
||||||
|
|
||||||
const Type* ImplementationVisitor::Visit(BreakStatement* stmt) {
|
const Type* ImplementationVisitor::Visit(BreakStatement* stmt) {
|
||||||
Block* break_block = global_context_.GetCurrentBreak();
|
Block* break_block = global_context_.GetCurrentBreak();
|
||||||
if (break_block == nullptr) {
|
if (break_block == nullptr) {
|
||||||
|
@ -177,8 +177,9 @@ class ImplementationVisitor : public FileVisitor {
|
|||||||
VisitResult Visit(StringLiteralExpression* expr);
|
VisitResult Visit(StringLiteralExpression* expr);
|
||||||
VisitResult Visit(NumberLiteralExpression* expr);
|
VisitResult Visit(NumberLiteralExpression* expr);
|
||||||
VisitResult Visit(AssumeTypeImpossibleExpression* expr);
|
VisitResult Visit(AssumeTypeImpossibleExpression* expr);
|
||||||
|
VisitResult Visit(TryLabelExpression* expr);
|
||||||
|
VisitResult Visit(StatementExpression* expr);
|
||||||
|
|
||||||
const Type* Visit(TryLabelStatement* stmt);
|
|
||||||
const Type* Visit(ReturnStatement* stmt);
|
const Type* Visit(ReturnStatement* stmt);
|
||||||
const Type* Visit(GotoStatement* stmt);
|
const Type* Visit(GotoStatement* stmt);
|
||||||
const Type* Visit(IfStatement* stmt);
|
const Type* Visit(IfStatement* stmt);
|
||||||
|
@ -201,14 +201,50 @@ void CheckNotDeferredStatement(Statement* statement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expression* MakeCall(const std::string& callee, bool is_operator,
|
||||||
|
const std::vector<TypeExpression*>& generic_arguments,
|
||||||
|
const std::vector<Expression*>& arguments,
|
||||||
|
const std::vector<Statement*>& otherwise) {
|
||||||
|
std::vector<std::string> labels;
|
||||||
|
|
||||||
|
// All IdentifierExpressions are treated as label names and can be directly
|
||||||
|
// used as labels identifiers. All other statements in a call's otherwise
|
||||||
|
// must create intermediate Labels for the otherwise's statement code.
|
||||||
|
size_t label_id = 0;
|
||||||
|
std::vector<LabelBlock*> temp_labels;
|
||||||
|
for (auto* statement : otherwise) {
|
||||||
|
if (auto* e = ExpressionStatement::DynamicCast(statement)) {
|
||||||
|
if (auto* id = IdentifierExpression::DynamicCast(e->expression)) {
|
||||||
|
if (id->generic_arguments.size() != 0) {
|
||||||
|
ReportError("An otherwise label cannot have generic parameters");
|
||||||
|
}
|
||||||
|
labels.push_back(id->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto label_name = std::string("_label") + std::to_string(label_id++);
|
||||||
|
labels.push_back(label_name);
|
||||||
|
auto* label_block =
|
||||||
|
MakeNode<LabelBlock>(label_name, ParameterList::Empty(), statement);
|
||||||
|
temp_labels.push_back(label_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create nested try-label expression for all of the temporary Labels that
|
||||||
|
// were created.
|
||||||
|
Expression* result = MakeNode<CallExpression>(
|
||||||
|
callee, false, generic_arguments, arguments, labels);
|
||||||
|
for (auto* label : temp_labels) {
|
||||||
|
result = MakeNode<TryLabelExpression>(result, label);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
base::Optional<ParseResult> MakeCall(ParseResultIterator* child_results) {
|
base::Optional<ParseResult> MakeCall(ParseResultIterator* child_results) {
|
||||||
auto callee = child_results->NextAs<std::string>();
|
auto callee = child_results->NextAs<std::string>();
|
||||||
auto generic_args = child_results->NextAs<TypeList>();
|
auto generic_args = child_results->NextAs<TypeList>();
|
||||||
auto args = child_results->NextAs<std::vector<Expression*>>();
|
auto args = child_results->NextAs<std::vector<Expression*>>();
|
||||||
auto labels = child_results->NextAs<std::vector<std::string>>();
|
auto otherwise = child_results->NextAs<std::vector<Statement*>>();
|
||||||
Expression* result =
|
return ParseResult{MakeCall(callee, false, generic_args, args, otherwise)};
|
||||||
MakeNode<CallExpression>(callee, false, generic_args, args, labels);
|
|
||||||
return ParseResult{result};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base::Optional<ParseResult> MakeBinaryOperator(
|
base::Optional<ParseResult> MakeBinaryOperator(
|
||||||
@ -216,20 +252,17 @@ base::Optional<ParseResult> MakeBinaryOperator(
|
|||||||
auto left = child_results->NextAs<Expression*>();
|
auto left = child_results->NextAs<Expression*>();
|
||||||
auto op = child_results->NextAs<std::string>();
|
auto op = child_results->NextAs<std::string>();
|
||||||
auto right = child_results->NextAs<Expression*>();
|
auto right = child_results->NextAs<Expression*>();
|
||||||
Expression* result = MakeNode<CallExpression>(
|
return ParseResult{MakeCall(op, true, TypeList{},
|
||||||
op, true, TypeList{}, std::vector<Expression*>{left, right},
|
std::vector<Expression*>{left, right},
|
||||||
std::vector<std::string>{});
|
std::vector<Statement*>{})};
|
||||||
return ParseResult{result};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base::Optional<ParseResult> MakeUnaryOperator(
|
base::Optional<ParseResult> MakeUnaryOperator(
|
||||||
ParseResultIterator* child_results) {
|
ParseResultIterator* child_results) {
|
||||||
auto op = child_results->NextAs<std::string>();
|
auto op = child_results->NextAs<std::string>();
|
||||||
auto e = child_results->NextAs<Expression*>();
|
auto e = child_results->NextAs<Expression*>();
|
||||||
Expression* result = MakeNode<CallExpression>(op, true, TypeList{},
|
return ParseResult{MakeCall(op, true, TypeList{}, std::vector<Expression*>{e},
|
||||||
std::vector<Expression*>{e},
|
std::vector<Statement*>{})};
|
||||||
std::vector<std::string>{});
|
|
||||||
return ParseResult{result};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool has_varargs>
|
template <bool has_varargs>
|
||||||
@ -584,10 +617,11 @@ base::Optional<ParseResult> MakeTypeswitchStatement(
|
|||||||
}
|
}
|
||||||
BlockStatement* case_block;
|
BlockStatement* case_block;
|
||||||
if (i < cases.size() - 1) {
|
if (i < cases.size() - 1) {
|
||||||
value = MakeNode<CallExpression>(
|
value =
|
||||||
"Cast", false, std::vector<TypeExpression*>{cases[i].type},
|
MakeCall("Cast", false, std::vector<TypeExpression*>{cases[i].type},
|
||||||
std::vector<Expression*>{value},
|
std::vector<Expression*>{value},
|
||||||
std::vector<std::string>{"_NextCase"});
|
std::vector<Statement*>{MakeNode<ExpressionStatement>(
|
||||||
|
MakeNode<IdentifierExpression>("_NextCase"))});
|
||||||
case_block = MakeNode<BlockStatement>();
|
case_block = MakeNode<BlockStatement>();
|
||||||
} else {
|
} else {
|
||||||
case_block = current_block;
|
case_block = current_block;
|
||||||
@ -599,9 +633,11 @@ base::Optional<ParseResult> MakeTypeswitchStatement(
|
|||||||
case_block->statements.push_back(cases[i].block);
|
case_block->statements.push_back(cases[i].block);
|
||||||
if (i < cases.size() - 1) {
|
if (i < cases.size() - 1) {
|
||||||
BlockStatement* next_block = MakeNode<BlockStatement>();
|
BlockStatement* next_block = MakeNode<BlockStatement>();
|
||||||
current_block->statements.push_back(MakeNode<TryLabelStatement>(
|
current_block->statements.push_back(
|
||||||
case_block, std::vector<LabelBlock*>{MakeNode<LabelBlock>(
|
MakeNode<ExpressionStatement>(MakeNode<TryLabelExpression>(
|
||||||
"_NextCase", ParameterList::Empty(), next_block)}));
|
MakeNode<StatementExpression>(case_block),
|
||||||
|
MakeNode<LabelBlock>("_NextCase", ParameterList::Empty(),
|
||||||
|
next_block))));
|
||||||
current_block = next_block;
|
current_block = next_block;
|
||||||
}
|
}
|
||||||
accumulated_types =
|
accumulated_types =
|
||||||
@ -692,13 +728,16 @@ base::Optional<ParseResult> MakeBlockStatement(
|
|||||||
return ParseResult{result};
|
return ParseResult{result};
|
||||||
}
|
}
|
||||||
|
|
||||||
base::Optional<ParseResult> MakeTryLabelStatement(
|
base::Optional<ParseResult> MakeTryLabelExpression(
|
||||||
ParseResultIterator* child_results) {
|
ParseResultIterator* child_results) {
|
||||||
auto try_block = child_results->NextAs<Statement*>();
|
auto try_block = child_results->NextAs<Statement*>();
|
||||||
CheckNotDeferredStatement(try_block);
|
CheckNotDeferredStatement(try_block);
|
||||||
|
Statement* result = try_block;
|
||||||
auto label_blocks = child_results->NextAs<std::vector<LabelBlock*>>();
|
auto label_blocks = child_results->NextAs<std::vector<LabelBlock*>>();
|
||||||
Statement* result =
|
for (auto block : label_blocks) {
|
||||||
MakeNode<TryLabelStatement>(try_block, std::move(label_blocks));
|
result = MakeNode<ExpressionStatement>(MakeNode<TryLabelExpression>(
|
||||||
|
MakeNode<StatementExpression>(result), block));
|
||||||
|
}
|
||||||
return ParseResult{result};
|
return ParseResult{result};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1028,10 +1067,10 @@ struct TorqueGrammar : Grammar {
|
|||||||
Sequence({Token("labels"),
|
Sequence({Token("labels"),
|
||||||
NonemptyList<LabelAndTypes>(&labelParameter, Token(","))}))};
|
NonemptyList<LabelAndTypes>(&labelParameter, Token(","))}))};
|
||||||
|
|
||||||
// Result: std::vector<std::string>
|
// Result: std::vector<Statement*>
|
||||||
Symbol* optionalOtherwise{TryOrDefault<std::vector<std::string>>(
|
Symbol* optionalOtherwise{TryOrDefault<std::vector<Statement*>>(
|
||||||
Sequence({Token("otherwise"),
|
Sequence({Token("otherwise"),
|
||||||
NonemptyList<std::string>(&identifier, Token(","))}))};
|
NonemptyList<Statement*>(&atomarStatement, Token(","))}))};
|
||||||
|
|
||||||
// Result: NameAndTypeExpression
|
// Result: NameAndTypeExpression
|
||||||
Symbol nameAndType = {
|
Symbol nameAndType = {
|
||||||
@ -1212,28 +1251,27 @@ struct TorqueGrammar : Grammar {
|
|||||||
expression},
|
expression},
|
||||||
MakeVarDeclarationStatement)};
|
MakeVarDeclarationStatement)};
|
||||||
|
|
||||||
// Disallow ambiguous dangling else by only allowing an {atomarStatement} as
|
// Result: Statement*
|
||||||
// a then-clause. Result: Statement*
|
|
||||||
Symbol atomarStatement = {
|
Symbol atomarStatement = {
|
||||||
Rule({&block}),
|
Rule({expression}, MakeExpressionStatement),
|
||||||
Rule({expression, Token(";")}, MakeExpressionStatement),
|
Rule({Token("return"), Optional<Expression*>(expression)},
|
||||||
Rule({Token("return"), Optional<Expression*>(expression), Token(";")},
|
|
||||||
MakeReturnStatement),
|
MakeReturnStatement),
|
||||||
Rule({Token("tail"), &callExpression, Token(";")}, MakeTailCallStatement),
|
Rule({Token("tail"), &callExpression}, MakeTailCallStatement),
|
||||||
Rule({Token("break"), Token(";")}, MakeBreakStatement),
|
Rule({Token("break")}, MakeBreakStatement),
|
||||||
Rule({Token("continue"), Token(";")}, MakeContinueStatement),
|
Rule({Token("continue")}, MakeContinueStatement),
|
||||||
Rule({Token("goto"), &identifier,
|
Rule({Token("goto"), &identifier,
|
||||||
TryOrDefault<std::vector<Expression*>>(&argumentList), Token(";")},
|
TryOrDefault<std::vector<Expression*>>(&argumentList)},
|
||||||
MakeGotoStatement),
|
MakeGotoStatement),
|
||||||
Rule({OneOf({"debug", "unreachable"}), Token(";")}, MakeDebugStatement)};
|
Rule({OneOf({"debug", "unreachable"})}, MakeDebugStatement)};
|
||||||
|
|
||||||
// Result: Statement*
|
// Result: Statement*
|
||||||
Symbol statement = {
|
Symbol statement = {
|
||||||
Rule({&atomarStatement}),
|
Rule({&block}),
|
||||||
|
Rule({&atomarStatement, Token(";")}),
|
||||||
Rule({&varDeclaration, Token(";")}),
|
Rule({&varDeclaration, Token(";")}),
|
||||||
Rule({&varDeclarationWithInitialization, Token(";")}),
|
Rule({&varDeclarationWithInitialization, Token(";")}),
|
||||||
Rule({Token("if"), CheckIf(Token("constexpr")), Token("("), expression,
|
Rule({Token("if"), CheckIf(Token("constexpr")), Token("("), expression,
|
||||||
Token(")"), &atomarStatement,
|
Token(")"), &statement,
|
||||||
Optional<Statement*>(Sequence({Token("else"), &statement}))},
|
Optional<Statement*>(Sequence({Token("else"), &statement}))},
|
||||||
MakeIfStatement),
|
MakeIfStatement),
|
||||||
Rule(
|
Rule(
|
||||||
@ -1244,21 +1282,19 @@ struct TorqueGrammar : Grammar {
|
|||||||
},
|
},
|
||||||
MakeTypeswitchStatement),
|
MakeTypeswitchStatement),
|
||||||
Rule({Token("try"), &block, NonemptyList<LabelBlock*>(&labelBlock)},
|
Rule({Token("try"), &block, NonemptyList<LabelBlock*>(&labelBlock)},
|
||||||
MakeTryLabelStatement),
|
MakeTryLabelExpression),
|
||||||
Rule({OneOf({"assert", "check"}), Token("("), &expressionWithSource,
|
Rule({OneOf({"assert", "check"}), Token("("), &expressionWithSource,
|
||||||
Token(")"), Token(";")},
|
Token(")"), Token(";")},
|
||||||
MakeAssertStatement),
|
MakeAssertStatement),
|
||||||
Rule({Token("while"), Token("("), expression, Token(")"),
|
Rule({Token("while"), Token("("), expression, Token(")"), &statement},
|
||||||
&atomarStatement},
|
|
||||||
MakeWhileStatement),
|
MakeWhileStatement),
|
||||||
Rule({Token("for"), Token("("), &varDeclaration, Token("of"), expression,
|
Rule({Token("for"), Token("("), &varDeclaration, Token("of"), expression,
|
||||||
Optional<RangeExpression>(&rangeSpecifier), Token(")"),
|
Optional<RangeExpression>(&rangeSpecifier), Token(")"), &statement},
|
||||||
&atomarStatement},
|
|
||||||
MakeForOfLoopStatement),
|
MakeForOfLoopStatement),
|
||||||
Rule({Token("for"), Token("("),
|
Rule({Token("for"), Token("("),
|
||||||
Optional<Statement*>(&varDeclarationWithInitialization), Token(";"),
|
Optional<Statement*>(&varDeclarationWithInitialization), Token(";"),
|
||||||
Optional<Expression*>(expression), Token(";"),
|
Optional<Expression*>(expression), Token(";"),
|
||||||
Optional<Expression*>(expression), Token(")"), &atomarStatement},
|
Optional<Expression*>(expression), Token(")"), &statement},
|
||||||
MakeForLoopStatement)};
|
MakeForLoopStatement)};
|
||||||
|
|
||||||
// Result: TypeswitchCase
|
// Result: TypeswitchCase
|
||||||
|
@ -271,6 +271,21 @@ TEST(TestLogicalOperators) {
|
|||||||
ft.Call();
|
ft.Call();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(TestOtherwiseAndLabels) {
|
||||||
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
||||||
|
CodeAssemblerTester asm_tester(isolate, 0);
|
||||||
|
TestBuiltinsFromDSLAssembler m(asm_tester.state());
|
||||||
|
{
|
||||||
|
m.TestOtherwiseWithCode1();
|
||||||
|
m.TestOtherwiseWithCode2();
|
||||||
|
m.TestOtherwiseWithCode3();
|
||||||
|
m.TestForwardLabel();
|
||||||
|
m.Return(m.UndefinedConstant());
|
||||||
|
}
|
||||||
|
FunctionTester ft(asm_tester.GenerateCode(), 0);
|
||||||
|
ft.Call();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -154,9 +154,9 @@ module test {
|
|||||||
check(GenericMacroTest<Object>(Null) == Null);
|
check(GenericMacroTest<Object>(Null) == Null);
|
||||||
check(GenericMacroTest<Object>(False) == False);
|
check(GenericMacroTest<Object>(False) == False);
|
||||||
check(GenericMacroTest<Object>(True) == True);
|
check(GenericMacroTest<Object>(True) == True);
|
||||||
check(GenericMacroTestWithLabels<Smi>(0) otherwise Fail == Undefined);
|
check((GenericMacroTestWithLabels<Smi>(0) otherwise Fail) == Undefined);
|
||||||
check(GenericMacroTestWithLabels<Smi>(0) otherwise Fail == Undefined);
|
check((GenericMacroTestWithLabels<Smi>(0) otherwise Fail) == Undefined);
|
||||||
check(GenericMacroTestWithLabels<Object>(smi0) otherwise Fail == smi0);
|
check((GenericMacroTestWithLabels<Object>(smi0) otherwise Fail) == smi0);
|
||||||
try {
|
try {
|
||||||
GenericMacroTestWithLabels<Object>(False) otherwise Expected;
|
GenericMacroTestWithLabels<Object>(False) otherwise Expected;
|
||||||
}
|
}
|
||||||
@ -541,4 +541,51 @@ module test {
|
|||||||
check(!TestOrAnd2(false, false, false));
|
check(!TestOrAnd2(false, false, false));
|
||||||
check(!TestOrAnd3(false, false, false));
|
check(!TestOrAnd3(false, false, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro TestCall(i: Smi): Smi
|
||||||
|
labels A {
|
||||||
|
if (i < 5) return i;
|
||||||
|
goto A;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro TestOtherwiseWithCode1() {
|
||||||
|
let v: Smi = 0;
|
||||||
|
let s: Smi = 1;
|
||||||
|
try {
|
||||||
|
TestCall(10) otherwise goto B(++s);
|
||||||
|
}
|
||||||
|
label B(v1: Smi) {
|
||||||
|
v = v1;
|
||||||
|
}
|
||||||
|
assert(v == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro TestOtherwiseWithCode2() {
|
||||||
|
let s: Smi = 0;
|
||||||
|
for (let i: Smi = 0; i < 10; ++i) {
|
||||||
|
TestCall(i) otherwise break;
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
assert(s == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro TestOtherwiseWithCode3() {
|
||||||
|
let s: Smi = 0;
|
||||||
|
for (let i: Smi = 0; i < 10; ++i) {
|
||||||
|
s += TestCall(i) otherwise break;
|
||||||
|
}
|
||||||
|
assert(s == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro TestForwardLabel() {
|
||||||
|
try {
|
||||||
|
goto A;
|
||||||
|
}
|
||||||
|
label A {
|
||||||
|
goto B(5);
|
||||||
|
}
|
||||||
|
label B(b: Smi) {
|
||||||
|
assert(b == 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user