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..ff9849000d3def3f59708b553caeae608d44e188 |
| --- /dev/null |
| +++ b/runtime/vm/ast_transformer.cc |
| @@ -0,0 +1,549 @@ |
| +// 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 { |
| + |
| +// Quick access to the locally defined isolate() method. |
| +#define I (isolate()) |
| + |
| +// 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) { |
|
hausner
2014/08/15 18:09:31
Why not return the result_ field as a function res
Michael Lippautz (Google)
2014/08/18 18:39:30
Done.
I also adjusted the parser to take this ret
|
| + expr->Visit(this); |
| +} |
| + |
| + |
| +LocalVariable* AwaitTransformer::EnsureCurrentTempVar() { |
| + const char* await_temp_prefix = ":await_temp_var_"; |
| + const String& cnt_str = String::ZoneHandle(I, |
| + String::NewFormatted( |
| + "%s%" Pd "", await_temp_prefix, 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_ = node; |
| +} |
| + |
| + |
| +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)); |
| + // |
| + // For now, throw an exception. |
| + const String& exception = String::ZoneHandle( |
| + I, String::New("awaitable futures not yet supported", Heap::kOld)); |
| + is_future_branch->Add(new(I) ThrowNode( |
| + Scanner::kNoSourcePos, |
| + new(I) LiteralNode( |
| + Scanner::kNoSourcePos, |
| + String::ZoneHandle(I, Symbols::New(exception))), |
| + NULL)); |
| + const Class& cls = Class::ZoneHandle( |
| + I, library_.LookupClass(Symbols::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): Join for await needs to happen here. |
| + |
| + LocalVariable* result = AddToPreambleNewTempVar(new(I) LoadLocalNode( |
| + Scanner::kNoSourcePos, result_param)); |
| + result_ = new(I) LoadLocalNode(Scanner::kNoSourcePos, result); |
| +} |
| + |
| + |
| +// Transforms boolean expressions into a sequence of evaluatons that only lazily |
| +// evaluate subexpressions. |
| +// |
| +// Example: |
| +// |
| +// (a || b) only evaluates b if a is true |
|
hausner
2014/08/15 18:09:31
only evaluates b if a is *false*
Michael Lippautz (Google)
2014/08/18 18:39:30
Done.
|
| +// |
| +// Transformation (roughly): |
| +// |
| +// t_1 = a; |
| +// if (t_1) { |
|
hausner
2014/08/15 18:09:31
!t_1
Michael Lippautz (Google)
2014/08/18 18:39:30
Done.
|
| +// t_2 = b; |
| +// } |
| +// t_3 = t_1 || t_2; // Compiler takes care that lazy evaluation takes place |
| +// on this level. |
| +AstNode* AwaitTransformer::LazyTransform(const Token::Kind logical_op, |
| + AstNode* new_left, |
| + AstNode* right) { |
| + ASSERT(logical_op == Token::kAND || logical_op == Token::kOR); |
| + AstNode* result = NULL; |
| + const Token::Kind compare_logical_op = (logical_op == Token::kAND) ? |
| + Token::kEQ : Token::kNE; |
| + SequenceNode* eval = new(I) 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_logical_op, |
| + 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 = NULL; |
| + // 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_; |
|
hausner
2014/08/15 18:09:31
Would it not make sense to group these two stateme
Michael Lippautz (Google)
2014/08/18 18:39:30
Done.
|
| + } |
| + 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 = NULL; |
| + // 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) { |
| + 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. |
| + 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) { |
| + // 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 |