Chromium Code Reviews| Index: runtime/vm/ast_transformer.cc |
| diff --git a/runtime/vm/ast_transformer.cc b/runtime/vm/ast_transformer.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..3645d1196d3acd7d2f22e63d24694519933f8310 |
| --- /dev/null |
| +++ b/runtime/vm/ast_transformer.cc |
| @@ -0,0 +1,527 @@ |
| +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +#include "vm/ast_transformer.h" |
| + |
| +#include "vm/parser.h" |
| + |
| +namespace dart { |
| + |
| +#define I Isolate::Current() |
| + |
| +// Nodes that are unreachable from already parsed expressions. |
| +#define FOR_EACH_UNREACHABLE_NODE(V) \ |
| + V(Case) \ |
| + V(CatchClause) \ |
| + V(CloneContext) \ |
| + V(ClosureCall) \ |
| + V(DoWhile) \ |
| + V(If) \ |
| + V(InlinedFinally) \ |
| + V(For) \ |
| + V(Jump) \ |
| + V(LoadInstanceField) \ |
| + V(NativeBody) \ |
| + V(Primary) \ |
| + V(Return) \ |
| + V(Sequence) \ |
| + V(StoreInstanceField) \ |
| + V(Switch) \ |
| + V(TryCatch) \ |
| + V(While) |
| + |
| +#define DEFINE_UNREACHABLE(BaseName) \ |
| +void AwaitTransformer::Visit##BaseName##Node(BaseName##Node* node) { \ |
| + UNREACHABLE(); \ |
| +} |
| + |
| +FOR_EACH_UNREACHABLE_NODE(DEFINE_UNREACHABLE) |
| +#undef DEFINE_UNREACHABLE |
| + |
| + |
| +void AwaitTransformer::Transform(AstNode* expr) { |
| + expr->Visit(this); |
| +} |
| + |
| + |
| +LocalVariable* AwaitTransformer::EnsureCurrentTempVar() { |
| + const String& cnt_str = String::Handle( |
| + String::NewFormatted( |
| + "%s%" Pd "", Symbols::AwaitTempVarPrefix().ToCString(), temp_cnt_)); |
| + const String& symbol = String::ZoneHandle(I, Symbols::New(cnt_str)); |
| + ASSERT(!symbol.IsNull()); |
| + LocalVariable* await_tmp = |
| + parsed_function_->await_temps_scope()->LookupVariable(symbol, false); |
| + if (await_tmp == NULL) { |
| + await_tmp = new (I) LocalVariable( |
| + Scanner::kNoSourcePos, |
| + symbol, |
| + Type::ZoneHandle(I, Type::DynamicType())); |
| + parsed_function_->await_temps_scope()->AddVariable(await_tmp); |
| + } |
| + return await_tmp; |
| +} |
| + |
| + |
| +LocalVariable* AwaitTransformer::AddToPreambleNewTempVar(AstNode* node) { |
| + LocalVariable* tmp_var = EnsureCurrentTempVar(); |
| + preamble_->Add(new (I) StoreLocalNode(Scanner::kNoSourcePos, tmp_var, node)); |
| + NextTempVar(); |
| + return tmp_var; |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitLiteralNode(LiteralNode* node) { |
| + result_ = new (I) LiteralNode(node->token_pos(), node->literal()); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitTypeNode(TypeNode* node) { |
| + result_ = new (I) TypeNode(node->token_pos(), node->type()); |
| +} |
| + |
| + |
| + |
| +void AwaitTransformer::VisitAwaitNode(AwaitNode* node) { |
| + // Await transformation: |
| + // |
| + // :await_temp_var_X = <expr>; |
| + // :result_param = :await_temp_var_X; |
| + // if (:result_param is Future) { |
| + // // :result_param.then(:async_op); |
| + // } |
| + // :await_temp_var_(X+1) = :result_param; |
| + |
| + LocalVariable* async_op = preamble_->scope()->LookupVariable( |
| + Symbols::AsyncOperation(), false); |
| + ASSERT(async_op != NULL); |
| + LocalVariable* result_param = preamble_->scope()->LookupVariable( |
| + Symbols::AsyncOperationParam(), false); |
| + ASSERT(result_param != NULL); |
| + |
| + node->expr()->Visit(this); |
| + preamble_->Add(new (I) StoreLocalNode(Scanner::kNoSourcePos, |
| + result_param, |
| + result_)); |
| + LoadLocalNode* load_result_param = new (I) LoadLocalNode( |
| + Scanner::kNoSourcePos, result_param); |
| + SequenceNode* is_future_branch = new (I) SequenceNode( |
| + Scanner::kNoSourcePos, preamble_->scope()); |
| + ArgumentListNode* args = new (I) ArgumentListNode(Scanner::kNoSourcePos); |
| + args->Add(new (I) LoadLocalNode(Scanner::kNoSourcePos, async_op)); |
| + // TODO(mlippautz): Once continuations are supported, just call .then(). |
| + // is_future_branch->Add(new (I) InstanceCallNode( |
| + // Scanner::kNoSourcePos, load_result_param, Symbols::FutureThen(), args)); |
| + is_future_branch->Add(new (I) ReturnNode(Scanner::kNoSourcePos)); |
| + const Class& cls = Class::Handle(library_.LookupClass( |
| + String::Handle(String::New("Future")))); |
| + const AbstractType& future_type = AbstractType::ZoneHandle(I, |
| + cls.RareType()); |
| + ASSERT(!future_type.IsNull()); |
| + TypeNode* future_type_node = new (I) TypeNode( |
| + Scanner::kNoSourcePos, future_type); |
| + IfNode* is_future_if = new (I) IfNode( |
| + Scanner::kNoSourcePos, |
| + new (I) ComparisonNode(Scanner::kNoSourcePos, |
| + Token::kIS, |
| + load_result_param, |
| + future_type_node), |
| + is_future_branch, |
| + NULL); |
| + preamble_->Add(is_future_if); |
| + |
| + // TODO(mlippautz): Jump target (label) should be added here. |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar(new (I) LoadLocalNode( |
| + Scanner::kNoSourcePos, result_param)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +AstNode* AwaitTransformer::LazyTransform(const Token::Kind kind, |
| + AstNode* new_left, |
| + AstNode* right) { |
| + ASSERT(kind == Token::kAND || kind == Token::kOR); |
| + AstNode* result = NULL; |
| + const Token::Kind compare_kind = (kind == Token::kAND) ? |
| + Token::kEQ : Token::kNE; |
| + SequenceNode* eval = new SequenceNode( |
| + Scanner::kNoSourcePos, preamble_->scope()); |
| + SequenceNode* saved_preamble = preamble_; |
| + preamble_ = eval; |
| + right->Visit(this); |
| + result = result_; |
| + preamble_ = saved_preamble; |
| + IfNode* right_body = new (I) IfNode( |
| + Scanner::kNoSourcePos, |
| + new (I) ComparisonNode( |
| + Scanner::kNoSourcePos, |
| + compare_kind, |
| + new_left, |
| + new (I) LiteralNode(Scanner::kNoSourcePos, Bool::True())), |
| + eval, |
| + NULL); |
| + preamble_->Add(right_body); |
| + return result; |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitBinaryOpNode(BinaryOpNode* node) { |
| + node->left()->Visit(this); |
| + AstNode* new_left = result_; |
| + AstNode* new_right = new (I) LiteralNode( |
| + Scanner::kNoSourcePos, |
| + Object::null_instance()); |
| + // Preserve lazy evaluaton. |
| + if (node->kind() == Token::kAND || node->kind() == Token::kOR) { |
| + new_right = LazyTransform(node->kind(), new_left, node->right()); |
| + } else { |
| + node->right()->Visit(this); |
| + new_right = result_; |
| + } |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) BinaryOpNode(node->token_pos(), |
| + node->kind(), |
| + new_left, |
| + new_right)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitBinaryOpWithMask32Node( |
| + BinaryOpWithMask32Node* node) { |
| + node->left()->Visit(this); |
| + AstNode* new_left = result_; |
| + AstNode* new_right = new (I) LiteralNode( |
| + Scanner::kNoSourcePos, |
| + Object::null_instance()); |
| + // Preserve lazy evaluaton. |
| + if (node->kind() == Token::kAND || node->kind() == Token::kOR) { |
| + new_right = LazyTransform(node->kind(), new_left, node->right()); |
| + } else { |
| + node->right()->Visit(this); |
| + new_right = result_; |
| + } |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) BinaryOpWithMask32Node(node->token_pos(), |
| + node->kind(), |
| + new_left, |
| + new_right, |
| + node->mask32())); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitComparisonNode(ComparisonNode* node) { |
| + node->left()->Visit(this); |
| + AstNode* new_left = result_; |
| + node->right()->Visit(this); |
| + AstNode* new_right = result_; |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) ComparisonNode(node->token_pos(), |
| + node->kind(), |
| + new_left, |
| + new_right)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitUnaryOpNode(UnaryOpNode* node) { |
| + node->operand()->Visit(this); |
| + AstNode* new_operand = result_; |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) UnaryOpNode(node->token_pos(), |
| + node->kind(), |
| + new_operand)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| +// ::= (<condition>) ? <true-branch> : <false-branch> |
| +void AwaitTransformer::VisitConditionalExprNode(ConditionalExprNode* node) { |
| + // TODO(mlippautz): Optimize variable assingments and branching. |
| + node->condition()->Visit(this); |
| + AstNode* new_condition = result_; |
| + SequenceNode* new_true = new (I) SequenceNode( |
| + Scanner::kNoSourcePos, preamble_->scope()); |
| + SequenceNode* saved_preamble = preamble_; |
| + preamble_ = new_true; |
| + node->true_expr()->Visit(this); |
| + AstNode* new_true_result = result_; |
| + SequenceNode* new_false = new (I) SequenceNode( |
| + Scanner::kNoSourcePos, preamble_->scope()); |
| + preamble_ = new_false; |
| + node->false_expr()->Visit(this); |
| + AstNode* new_false_result = result_; |
| + preamble_ = saved_preamble; |
| + IfNode* new_if = new (I) IfNode(Scanner::kNoSourcePos, |
| + new_condition, |
| + new_true, |
| + new_false); |
| + preamble_->Add(new_if); |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) ConditionalExprNode(Scanner::kNoSourcePos, |
| + new_condition, |
| + new_true_result, |
| + new_false_result)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitArgumentListNode(ArgumentListNode* node) { |
| + ArgumentListNode* new_args = new (I) ArgumentListNode(node->token_pos()); |
| + for (intptr_t i = 0; i < node->length(); i++) { |
| + node->NodeAt(i)->Visit(this); |
| + new_args->Add(result_); |
| + } |
| + result_ = new_args; |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitArrayNode(ArrayNode* node) { |
| + GrowableArray<AstNode*> new_elements; |
| + for (intptr_t i = 0; i < node->length(); i++) { |
| + node->ElementAt(i)->Visit(this); |
| + new_elements.Add(result_); |
| + } |
| + result_ = new (I) ArrayNode(node->token_pos(), node->type(), new_elements); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitStringInterpolateNode(StringInterpolateNode* node) { |
| + node->value()->Visit(this); |
| + ArrayNode* new_value = result_->AsArrayNode(); |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) StringInterpolateNode(node->token_pos(), |
| + new_value)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitClosureNode(ClosureNode* node) { |
| + AstNode* new_receiver = node->receiver(); |
| + if (new_receiver != NULL) { |
| + new_receiver->Visit(this); |
| + new_receiver = result_; |
| + } |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) ClosureNode(node->token_pos(), |
| + node->function(), |
| + new_receiver, |
| + node->scope())); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitInstanceCallNode(InstanceCallNode* node) { |
| + node->receiver()->Visit(this); |
| + AstNode* new_receiver = result_; |
| + node->arguments()->Visit(this); |
| + ArgumentListNode* new_args = result_->AsArgumentListNode(); |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) InstanceCallNode(node->token_pos(), |
| + new_receiver, |
| + node->function_name(), |
| + new_args)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitStaticCallNode(StaticCallNode* node) { |
| + node->arguments()->Visit(this); |
| + ArgumentListNode* new_args = result_->AsArgumentListNode(); |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) StaticCallNode(node->token_pos(), |
| + node->function(), |
| + new_args)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitConstructorCallNode(ConstructorCallNode* node) { |
| + node->arguments()->Visit(this); |
| + ArgumentListNode* new_args = result_->AsArgumentListNode(); |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) ConstructorCallNode(node->token_pos(), |
| + node->type_arguments(), |
| + node->constructor(), |
| + new_args)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitInstanceGetterNode(InstanceGetterNode* node) { |
| + node->receiver()->Visit(this); |
| + AstNode* new_receiver = result_; |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) InstanceGetterNode(node->token_pos(), |
| + new_receiver, |
| + node->field_name())); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitInstanceSetterNode(InstanceSetterNode* node) { |
| + AstNode* new_receiver = node->receiver(); |
| + if (new_receiver != NULL) { |
| + new_receiver->Visit(this); |
| + new_receiver = result_; |
| + } |
| + |
| + node->value()->Visit(this); |
| + AstNode* new_value = result_; |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) InstanceSetterNode(node->token_pos(), |
| + new_receiver, |
| + node->field_name(), |
| + new_value)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitStaticGetterNode(StaticGetterNode* node) { |
| + AstNode* new_receiver = node->receiver(); |
| + if (new_receiver != NULL) { |
| + new_receiver->Visit(this); |
| + new_receiver = result_; |
| + } |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) StaticGetterNode(node->token_pos(), |
| + new_receiver, |
| + node->is_super_getter(), |
| + node->cls(), |
| + node->field_name())); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitStaticSetterNode(StaticSetterNode* node) { |
| + AstNode* new_receiver = node->receiver(); |
| + if (new_receiver != NULL) { |
| + new_receiver->Visit(this); |
| + new_receiver = result_; |
| + } |
| + node->value()->Visit(this); |
| + AstNode* new_value = result_; |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) StaticSetterNode(node->token_pos(), |
| + new_receiver, |
| + node->cls(), |
| + node->field_name(), |
| + new_value)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitLoadLocalNode(LoadLocalNode* node) { |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) LoadLocalNode(node->token_pos(), &node->local())); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitStoreLocalNode(StoreLocalNode* node) { |
| + node->value()->Visit(this); |
| + AstNode* new_value = result_; |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) StoreLocalNode(node->token_pos(), |
| + &node->local(), |
| + new_value)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) { |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) LoadStaticFieldNode(node->token_pos(), |
| + node->field())); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) { |
| + node->value()->Visit(this); |
| + AstNode* new_value = result_; |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) StoreStaticFieldNode(node->token_pos(), |
| + node->field(), |
| + new_value)); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitLoadIndexedNode(LoadIndexedNode* node) { |
| + node->array()->Visit(this); |
| + AstNode* new_array = result_; |
| + node->index_expr()->Visit(this); |
| + AstNode* new_index = result_; |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) LoadIndexedNode(node->token_pos(), |
| + new_array, |
| + new_index, |
| + node->super_class())); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitStoreIndexedNode(StoreIndexedNode* node) { |
| + node->array()->Visit(this); |
| + AstNode* new_array = result_; |
| + node->index_expr()->Visit(this); |
| + AstNode* new_index = result_; |
| + node->value()->Visit(this); |
| + AstNode* new_value = result_; |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) StoreIndexedNode(node->token_pos(), |
| + new_array, |
| + new_index, |
| + new_value, |
| + node->super_class())); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitAssignableNode(AssignableNode* node) { |
| + node->expr()->Visit(this); |
| + AstNode* new_expr = result_; |
| + LocalVariable* result = AddToPreambleNewTempVar( |
| + new (I) AssignableNode(node->token_pos(), |
| + new_expr, |
| + node->type(), |
| + node->dst_name())); |
| + result_ = new (I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitLetNode(LetNode* node) { |
| + // TODO(mlippautz): Check initializers and their temps. |
|
Michael Lippautz (Google)
2014/08/07 00:18:55
#initializers and #temps should 0 here?
|
| + LetNode* result = new (I) LetNode(node->token_pos()); |
| + for (intptr_t i = 0; i < node->nodes().length(); i++) { |
| + node->nodes()[i]->Visit(this); |
| + result->AddNode(result_); |
| + } |
| + result_ = result; |
| +} |
| + |
| + |
| +void AwaitTransformer::VisitThrowNode(ThrowNode* node) { |
|
Michael Lippautz (Google)
2014/08/07 00:18:55
``throws'' are not actual expressions, but the par
|
| + // TODO(mlippautz): Check if relevant. |
| + node->exception()->Visit(this); |
| + AstNode* new_exception = result_; |
| + node->stacktrace()->Visit(this); |
| + AstNode* new_stacktrace = result_; |
| + result_ = new (I) ThrowNode(node->token_pos(), |
| + new_exception, |
| + new_stacktrace); |
| +} |
| + |
| +} // namespace dart |