[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 {
|
||||
let length: Object;
|
||||
try {
|
||||
try {
|
||||
return (Cast<JSArray>(o) otherwise CheckArgs).length;
|
||||
}
|
||||
label CheckArgs {
|
||||
const a: JSArgumentsObjectWithLength =
|
||||
Cast<JSArgumentsObjectWithLength>(context, o) otherwise Slow;
|
||||
return Cast<Smi>(length = a.length) otherwise ToLength;
|
||||
}
|
||||
return (Cast<JSArray>(o) otherwise CheckArgs).length;
|
||||
}
|
||||
label CheckArgs {
|
||||
const a: JSArgumentsObjectWithLength =
|
||||
Cast<JSArgumentsObjectWithLength>(context, o) otherwise Slow;
|
||||
const length: Object = a.length;
|
||||
return Cast<Smi>(length) otherwise goto ToLength(length);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,9 @@ namespace torque {
|
||||
V(ElementAccessExpression) \
|
||||
V(AssignmentExpression) \
|
||||
V(IncrementDecrementExpression) \
|
||||
V(AssumeTypeImpossibleExpression)
|
||||
V(AssumeTypeImpossibleExpression) \
|
||||
V(StatementExpression) \
|
||||
V(TryLabelExpression)
|
||||
|
||||
#define AST_TYPE_EXPRESSION_NODE_KIND_LIST(V) \
|
||||
V(BasicTypeExpression) \
|
||||
@ -52,7 +54,6 @@ namespace torque {
|
||||
V(TailCallStatement) \
|
||||
V(VarDeclarationStatement) \
|
||||
V(GotoStatement) \
|
||||
V(TryLabelStatement)
|
||||
|
||||
#define AST_DECLARATION_NODE_KIND_LIST(V) \
|
||||
V(TypeDeclaration) \
|
||||
@ -553,15 +554,22 @@ struct LabelBlock : AstNode {
|
||||
Statement* body;
|
||||
};
|
||||
|
||||
struct TryLabelStatement : Statement {
|
||||
DEFINE_AST_NODE_LEAF_BOILERPLATE(TryLabelStatement)
|
||||
TryLabelStatement(SourcePosition pos, Statement* try_block,
|
||||
std::vector<LabelBlock*> label_blocks)
|
||||
: Statement(kKind, pos),
|
||||
try_block(try_block),
|
||||
label_blocks(std::move(label_blocks)) {}
|
||||
Statement* try_block;
|
||||
std::vector<LabelBlock*> label_blocks;
|
||||
struct StatementExpression : Expression {
|
||||
DEFINE_AST_NODE_LEAF_BOILERPLATE(StatementExpression)
|
||||
StatementExpression(SourcePosition pos, Statement* statement)
|
||||
: Expression(kKind, pos), statement(statement) {}
|
||||
Statement* statement;
|
||||
};
|
||||
|
||||
struct TryLabelExpression : Expression {
|
||||
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 {
|
||||
|
@ -402,14 +402,15 @@ void DeclarationVisitor::Visit(ForLoopStatement* stmt) {
|
||||
if (stmt->action) Visit(*stmt->action);
|
||||
}
|
||||
|
||||
void DeclarationVisitor::Visit(TryLabelStatement* stmt) {
|
||||
// Activate a new scope to declare handler labels, they should not be
|
||||
// visible outside the label block.
|
||||
void DeclarationVisitor::Visit(TryLabelExpression* stmt) {
|
||||
// Activate a new scope to declare the handler's label parameters, they should
|
||||
// not be visible outside the label block.
|
||||
{
|
||||
Declarations::NodeScopeActivator scope(declarations(), stmt);
|
||||
|
||||
// Declare labels
|
||||
for (LabelBlock* block : stmt->label_blocks) {
|
||||
// Declare label
|
||||
{
|
||||
LabelBlock* block = stmt->label_block;
|
||||
CurrentSourcePosition::Scope scope(block->pos);
|
||||
Label* shared_label =
|
||||
declarations()->DeclareLabel(block->label, block->body);
|
||||
@ -432,18 +433,16 @@ void DeclarationVisitor::Visit(TryLabelStatement* stmt) {
|
||||
shared_label->AddVariable(DeclareVariable(p, type, false));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (global_context_.verbose()) {
|
||||
std::cout << " declaring label " << block->label << "\n";
|
||||
if (global_context_.verbose()) {
|
||||
std::cout << " declaring label " << block->label << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Visit(stmt->try_block);
|
||||
Visit(stmt->try_expression);
|
||||
}
|
||||
|
||||
for (LabelBlock* block : stmt->label_blocks) {
|
||||
Visit(block->body);
|
||||
}
|
||||
Visit(stmt->label_block->body);
|
||||
}
|
||||
|
||||
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) {
|
||||
Visit(&expr->callee);
|
||||
for (Expression* arg : expr->arguments) Visit(arg);
|
||||
|
@ -139,7 +139,8 @@ class DeclarationVisitor : public FileVisitor {
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
|
@ -80,6 +80,7 @@ void ImplementationVisitor::BeginModuleFile(Module* module) {
|
||||
DashifyString(module->name()) + "-gen.h\"";
|
||||
}
|
||||
source << "\n";
|
||||
source << "#include \"src/objects/arguments.h\"\n";
|
||||
source << "#include \"src/builtins/builtins-utils-gen.h\"\n";
|
||||
source << "#include \"src/builtins/builtins.h\"\n";
|
||||
source << "#include \"src/code-factory.h\"\n";
|
||||
@ -1081,19 +1082,19 @@ const Type* ImplementationVisitor::Visit(ForOfLoopStatement* stmt) {
|
||||
return TypeOracle::GetVoidType();
|
||||
}
|
||||
|
||||
const Type* ImplementationVisitor::Visit(TryLabelStatement* stmt) {
|
||||
VisitResult ImplementationVisitor::Visit(TryLabelExpression* expr) {
|
||||
Block* done_block = assembler().NewBlock();
|
||||
const Type* try_result = TypeOracle::GetNeverType();
|
||||
std::vector<Label*> labels;
|
||||
VisitResult try_result;
|
||||
Label* label = nullptr;
|
||||
|
||||
// Output labels for the goto handlers and for the merge after the try.
|
||||
{
|
||||
// Activate a new scope to see handler labels
|
||||
Declarations::NodeScopeActivator scope(declarations(), stmt);
|
||||
for (LabelBlock* block : stmt->label_blocks) {
|
||||
Declarations::NodeScopeActivator scope(declarations(), expr);
|
||||
{
|
||||
LabelBlock* block = expr->label_block;
|
||||
CurrentSourcePosition::Scope source_position(block->pos);
|
||||
Label* label = declarations()->LookupLabel(block->label);
|
||||
labels.push_back(label);
|
||||
label = declarations()->LookupLabel(block->label);
|
||||
|
||||
Declarations::NodeScopeActivator scope(declarations(), block->body);
|
||||
Stack<const Type*> label_input_stack = assembler().CurrentStack();
|
||||
@ -1108,42 +1109,48 @@ const Type* ImplementationVisitor::Visit(TryLabelStatement* stmt) {
|
||||
// Visit try
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that each label clause is actually used. It's not just a friendly
|
||||
// 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());
|
||||
if (label_result->IsVoid() && try_result.type()->IsNever()) {
|
||||
try_result =
|
||||
VisitResult(TypeOracle::GetVoidType(), try_result.stack_range());
|
||||
}
|
||||
label_iterator++;
|
||||
}
|
||||
|
||||
// Visit and output the code for each label block, one-by-one.
|
||||
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()) {
|
||||
if (!try_result.type()->IsNever()) {
|
||||
assembler().Bind(done_block);
|
||||
}
|
||||
return try_result;
|
||||
}
|
||||
|
||||
VisitResult ImplementationVisitor::Visit(StatementExpression* expr) {
|
||||
return VisitResult{Visit(expr->statement), assembler().TopRange(0)};
|
||||
}
|
||||
|
||||
const Type* ImplementationVisitor::Visit(BreakStatement* stmt) {
|
||||
Block* break_block = global_context_.GetCurrentBreak();
|
||||
if (break_block == nullptr) {
|
||||
|
@ -177,8 +177,9 @@ class ImplementationVisitor : public FileVisitor {
|
||||
VisitResult Visit(StringLiteralExpression* expr);
|
||||
VisitResult Visit(NumberLiteralExpression* 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(GotoStatement* 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) {
|
||||
auto callee = child_results->NextAs<std::string>();
|
||||
auto generic_args = child_results->NextAs<TypeList>();
|
||||
auto args = child_results->NextAs<std::vector<Expression*>>();
|
||||
auto labels = child_results->NextAs<std::vector<std::string>>();
|
||||
Expression* result =
|
||||
MakeNode<CallExpression>(callee, false, generic_args, args, labels);
|
||||
return ParseResult{result};
|
||||
auto otherwise = child_results->NextAs<std::vector<Statement*>>();
|
||||
return ParseResult{MakeCall(callee, false, generic_args, args, otherwise)};
|
||||
}
|
||||
|
||||
base::Optional<ParseResult> MakeBinaryOperator(
|
||||
@ -216,20 +252,17 @@ base::Optional<ParseResult> MakeBinaryOperator(
|
||||
auto left = child_results->NextAs<Expression*>();
|
||||
auto op = child_results->NextAs<std::string>();
|
||||
auto right = child_results->NextAs<Expression*>();
|
||||
Expression* result = MakeNode<CallExpression>(
|
||||
op, true, TypeList{}, std::vector<Expression*>{left, right},
|
||||
std::vector<std::string>{});
|
||||
return ParseResult{result};
|
||||
return ParseResult{MakeCall(op, true, TypeList{},
|
||||
std::vector<Expression*>{left, right},
|
||||
std::vector<Statement*>{})};
|
||||
}
|
||||
|
||||
base::Optional<ParseResult> MakeUnaryOperator(
|
||||
ParseResultIterator* child_results) {
|
||||
auto op = child_results->NextAs<std::string>();
|
||||
auto e = child_results->NextAs<Expression*>();
|
||||
Expression* result = MakeNode<CallExpression>(op, true, TypeList{},
|
||||
std::vector<Expression*>{e},
|
||||
std::vector<std::string>{});
|
||||
return ParseResult{result};
|
||||
return ParseResult{MakeCall(op, true, TypeList{}, std::vector<Expression*>{e},
|
||||
std::vector<Statement*>{})};
|
||||
}
|
||||
|
||||
template <bool has_varargs>
|
||||
@ -584,10 +617,11 @@ base::Optional<ParseResult> MakeTypeswitchStatement(
|
||||
}
|
||||
BlockStatement* case_block;
|
||||
if (i < cases.size() - 1) {
|
||||
value = MakeNode<CallExpression>(
|
||||
"Cast", false, std::vector<TypeExpression*>{cases[i].type},
|
||||
std::vector<Expression*>{value},
|
||||
std::vector<std::string>{"_NextCase"});
|
||||
value =
|
||||
MakeCall("Cast", false, std::vector<TypeExpression*>{cases[i].type},
|
||||
std::vector<Expression*>{value},
|
||||
std::vector<Statement*>{MakeNode<ExpressionStatement>(
|
||||
MakeNode<IdentifierExpression>("_NextCase"))});
|
||||
case_block = MakeNode<BlockStatement>();
|
||||
} else {
|
||||
case_block = current_block;
|
||||
@ -599,9 +633,11 @@ base::Optional<ParseResult> MakeTypeswitchStatement(
|
||||
case_block->statements.push_back(cases[i].block);
|
||||
if (i < cases.size() - 1) {
|
||||
BlockStatement* next_block = MakeNode<BlockStatement>();
|
||||
current_block->statements.push_back(MakeNode<TryLabelStatement>(
|
||||
case_block, std::vector<LabelBlock*>{MakeNode<LabelBlock>(
|
||||
"_NextCase", ParameterList::Empty(), next_block)}));
|
||||
current_block->statements.push_back(
|
||||
MakeNode<ExpressionStatement>(MakeNode<TryLabelExpression>(
|
||||
MakeNode<StatementExpression>(case_block),
|
||||
MakeNode<LabelBlock>("_NextCase", ParameterList::Empty(),
|
||||
next_block))));
|
||||
current_block = next_block;
|
||||
}
|
||||
accumulated_types =
|
||||
@ -692,13 +728,16 @@ base::Optional<ParseResult> MakeBlockStatement(
|
||||
return ParseResult{result};
|
||||
}
|
||||
|
||||
base::Optional<ParseResult> MakeTryLabelStatement(
|
||||
base::Optional<ParseResult> MakeTryLabelExpression(
|
||||
ParseResultIterator* child_results) {
|
||||
auto try_block = child_results->NextAs<Statement*>();
|
||||
CheckNotDeferredStatement(try_block);
|
||||
Statement* result = try_block;
|
||||
auto label_blocks = child_results->NextAs<std::vector<LabelBlock*>>();
|
||||
Statement* result =
|
||||
MakeNode<TryLabelStatement>(try_block, std::move(label_blocks));
|
||||
for (auto block : label_blocks) {
|
||||
result = MakeNode<ExpressionStatement>(MakeNode<TryLabelExpression>(
|
||||
MakeNode<StatementExpression>(result), block));
|
||||
}
|
||||
return ParseResult{result};
|
||||
}
|
||||
|
||||
@ -1028,10 +1067,10 @@ struct TorqueGrammar : Grammar {
|
||||
Sequence({Token("labels"),
|
||||
NonemptyList<LabelAndTypes>(&labelParameter, Token(","))}))};
|
||||
|
||||
// Result: std::vector<std::string>
|
||||
Symbol* optionalOtherwise{TryOrDefault<std::vector<std::string>>(
|
||||
// Result: std::vector<Statement*>
|
||||
Symbol* optionalOtherwise{TryOrDefault<std::vector<Statement*>>(
|
||||
Sequence({Token("otherwise"),
|
||||
NonemptyList<std::string>(&identifier, Token(","))}))};
|
||||
NonemptyList<Statement*>(&atomarStatement, Token(","))}))};
|
||||
|
||||
// Result: NameAndTypeExpression
|
||||
Symbol nameAndType = {
|
||||
@ -1212,28 +1251,27 @@ struct TorqueGrammar : Grammar {
|
||||
expression},
|
||||
MakeVarDeclarationStatement)};
|
||||
|
||||
// Disallow ambiguous dangling else by only allowing an {atomarStatement} as
|
||||
// a then-clause. Result: Statement*
|
||||
// Result: Statement*
|
||||
Symbol atomarStatement = {
|
||||
Rule({&block}),
|
||||
Rule({expression, Token(";")}, MakeExpressionStatement),
|
||||
Rule({Token("return"), Optional<Expression*>(expression), Token(";")},
|
||||
Rule({expression}, MakeExpressionStatement),
|
||||
Rule({Token("return"), Optional<Expression*>(expression)},
|
||||
MakeReturnStatement),
|
||||
Rule({Token("tail"), &callExpression, Token(";")}, MakeTailCallStatement),
|
||||
Rule({Token("break"), Token(";")}, MakeBreakStatement),
|
||||
Rule({Token("continue"), Token(";")}, MakeContinueStatement),
|
||||
Rule({Token("tail"), &callExpression}, MakeTailCallStatement),
|
||||
Rule({Token("break")}, MakeBreakStatement),
|
||||
Rule({Token("continue")}, MakeContinueStatement),
|
||||
Rule({Token("goto"), &identifier,
|
||||
TryOrDefault<std::vector<Expression*>>(&argumentList), Token(";")},
|
||||
TryOrDefault<std::vector<Expression*>>(&argumentList)},
|
||||
MakeGotoStatement),
|
||||
Rule({OneOf({"debug", "unreachable"}), Token(";")}, MakeDebugStatement)};
|
||||
Rule({OneOf({"debug", "unreachable"})}, MakeDebugStatement)};
|
||||
|
||||
// Result: Statement*
|
||||
Symbol statement = {
|
||||
Rule({&atomarStatement}),
|
||||
Rule({&block}),
|
||||
Rule({&atomarStatement, Token(";")}),
|
||||
Rule({&varDeclaration, Token(";")}),
|
||||
Rule({&varDeclarationWithInitialization, Token(";")}),
|
||||
Rule({Token("if"), CheckIf(Token("constexpr")), Token("("), expression,
|
||||
Token(")"), &atomarStatement,
|
||||
Token(")"), &statement,
|
||||
Optional<Statement*>(Sequence({Token("else"), &statement}))},
|
||||
MakeIfStatement),
|
||||
Rule(
|
||||
@ -1244,21 +1282,19 @@ struct TorqueGrammar : Grammar {
|
||||
},
|
||||
MakeTypeswitchStatement),
|
||||
Rule({Token("try"), &block, NonemptyList<LabelBlock*>(&labelBlock)},
|
||||
MakeTryLabelStatement),
|
||||
MakeTryLabelExpression),
|
||||
Rule({OneOf({"assert", "check"}), Token("("), &expressionWithSource,
|
||||
Token(")"), Token(";")},
|
||||
MakeAssertStatement),
|
||||
Rule({Token("while"), Token("("), expression, Token(")"),
|
||||
&atomarStatement},
|
||||
Rule({Token("while"), Token("("), expression, Token(")"), &statement},
|
||||
MakeWhileStatement),
|
||||
Rule({Token("for"), Token("("), &varDeclaration, Token("of"), expression,
|
||||
Optional<RangeExpression>(&rangeSpecifier), Token(")"),
|
||||
&atomarStatement},
|
||||
Optional<RangeExpression>(&rangeSpecifier), Token(")"), &statement},
|
||||
MakeForOfLoopStatement),
|
||||
Rule({Token("for"), Token("("),
|
||||
Optional<Statement*>(&varDeclarationWithInitialization), Token(";"),
|
||||
Optional<Expression*>(expression), Token(";"),
|
||||
Optional<Expression*>(expression), Token(")"), &atomarStatement},
|
||||
Optional<Expression*>(expression), Token(")"), &statement},
|
||||
MakeForLoopStatement)};
|
||||
|
||||
// Result: TypeswitchCase
|
||||
|
@ -271,6 +271,21 @@ TEST(TestLogicalOperators) {
|
||||
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 internal
|
||||
} // namespace v8
|
||||
|
@ -154,9 +154,9 @@ module test {
|
||||
check(GenericMacroTest<Object>(Null) == Null);
|
||||
check(GenericMacroTest<Object>(False) == False);
|
||||
check(GenericMacroTest<Object>(True) == True);
|
||||
check(GenericMacroTestWithLabels<Smi>(0) otherwise Fail == Undefined);
|
||||
check(GenericMacroTestWithLabels<Smi>(0) otherwise Fail == Undefined);
|
||||
check(GenericMacroTestWithLabels<Object>(smi0) otherwise Fail == smi0);
|
||||
check((GenericMacroTestWithLabels<Smi>(0) otherwise Fail) == Undefined);
|
||||
check((GenericMacroTestWithLabels<Smi>(0) otherwise Fail) == Undefined);
|
||||
check((GenericMacroTestWithLabels<Object>(smi0) otherwise Fail) == smi0);
|
||||
try {
|
||||
GenericMacroTestWithLabels<Object>(False) otherwise Expected;
|
||||
}
|
||||
@ -541,4 +541,51 @@ module test {
|
||||
check(!TestOrAnd2(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