| // 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(AwaitMarker) \ |
| V(Case) \ |
| V(CatchClause) \ |
| V(CloneContext) \ |
| V(ClosureCall) \ |
| V(DoWhile) \ |
| V(If) \ |
| V(InitStaticField) \ |
| 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 |
| |
| |
| AstNode* AwaitTransformer::Transform(AstNode* expr) { |
| expr->Visit(this); |
| return result_; |
| } |
| |
| |
| LocalVariable* AwaitTransformer::EnsureCurrentTempVar() { |
| const char* await_temp_prefix = ":await_temp_var_"; |
| const String& cnt_str = String::ZoneHandle( |
| I, String::NewFormatted("%s%d", 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) { |
| // AwaitMarker(kNewContinuationState); |
| // :result_param.then(:async_op); |
| // return; // (return_type() == kContinuation) |
| // } |
| // AwaitMarker(kTargetForContinuation); // Join happens here. |
| // :saved_try_ctx_var = :await_saved_try_ctx_var_y; |
| // :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()); |
| AwaitMarkerNode* await_marker = |
| new(I) AwaitMarkerNode(AwaitMarkerNode::kNewContinuationState); |
| await_marker->set_scope(preamble_->scope()); |
| is_future_branch->Add(await_marker); |
| ArgumentListNode* args = new(I) ArgumentListNode(Scanner::kNoSourcePos); |
| args->Add(new(I) LoadLocalNode(Scanner::kNoSourcePos, async_op)); |
| is_future_branch->Add(new(I) InstanceCallNode( |
| Scanner::kNoSourcePos, load_result_param, Symbols::FutureThen(), args)); |
| ReturnNode* continuation_return = new(I) ReturnNode(Scanner::kNoSourcePos); |
| continuation_return->set_return_type(ReturnNode::kContinuation); |
| is_future_branch->Add(continuation_return); |
| |
| const Class& cls = Class::ZoneHandle( |
| I, library_.LookupClass(Symbols::Future())); |
| const AbstractType& future_type = AbstractType::ZoneHandle(I, cls.RareType()); |
| ASSERT(!future_type.IsNull()); |
| preamble_->Add(new(I) IfNode( |
| Scanner::kNoSourcePos, |
| new (I) ComparisonNode( |
| Scanner::kNoSourcePos, |
| Token::kIS, |
| load_result_param, |
| new (I) TypeNode(Scanner::kNoSourcePos, future_type)), |
| is_future_branch, |
| NULL)); |
| preamble_->Add(new (I) AwaitMarkerNode( |
| AwaitMarkerNode::kTargetForContinuation)); |
| // If this expression is part of a try block, also append the code for |
| // restoring the saved try context that lives on the stack. |
| if (parsed_function_->saved_try_ctx() != NULL) { |
| preamble_->Add(new (I) StoreLocalNode( |
| Scanner::kNoSourcePos, |
| parsed_function_->saved_try_ctx(), |
| new (I) LoadLocalNode( |
| Scanner::kNoSourcePos, parsed_function_->async_saved_try_ctx()))); |
| } |
| |
| 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 false |
| // |
| // Transformation (roughly): |
| // |
| // t_1 = a; |
| // if (!t_1) { |
| // 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; |
| result = Transform(right); |
| 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 { |
| new_right = Transform(node->right()); |
| } |
| 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 { |
| new_right = Transform(node->right()); |
| } |
| 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) { |
| AstNode* new_left = Transform(node->left()); |
| AstNode* new_right = Transform(node->right()); |
| 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) { |
| AstNode* new_operand = Transform(node->operand()); |
| 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) { |
| AstNode* new_condition = Transform(node->condition()); |
| SequenceNode* new_true = new(I) SequenceNode( |
| Scanner::kNoSourcePos, preamble_->scope()); |
| SequenceNode* saved_preamble = preamble_; |
| preamble_ = new_true; |
| AstNode* new_true_result = Transform(node->true_expr()); |
| SequenceNode* new_false = new(I) SequenceNode( |
| Scanner::kNoSourcePos, preamble_->scope()); |
| preamble_ = new_false; |
| AstNode* new_false_result = Transform(node->false_expr()); |
| 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++) { |
| new_args->Add(Transform(node->NodeAt(i))); |
| } |
| result_ = new_args; |
| } |
| |
| |
| void AwaitTransformer::VisitArrayNode(ArrayNode* node) { |
| GrowableArray<AstNode*> new_elements; |
| for (intptr_t i = 0; i < node->length(); i++) { |
| new_elements.Add(Transform(node->ElementAt(i))); |
| } |
| result_ = new(I) ArrayNode(node->token_pos(), node->type(), new_elements); |
| } |
| |
| |
| void AwaitTransformer::VisitStringInterpolateNode(StringInterpolateNode* node) { |
| ArrayNode* new_value = Transform(node->value())->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 = Transform(new_receiver); |
| } |
| 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) { |
| AstNode* new_receiver = Transform(node->receiver()); |
| ArgumentListNode* new_args = |
| Transform(node->arguments())->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) { |
| ArgumentListNode* new_args = |
| Transform(node->arguments())->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) { |
| ArgumentListNode* new_args = |
| Transform(node->arguments())->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) { |
| AstNode* new_receiver = Transform(node->receiver()); |
| 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 = Transform(new_receiver); |
| } |
| AstNode* new_value = Transform(node->value()); |
| 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 = Transform(new_receiver); |
| } |
| 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 = Transform(new_receiver); |
| } |
| AstNode* new_value = Transform(node->value()); |
| 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) { |
| AstNode* new_value = Transform(node->value()); |
| 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) { |
| AstNode* new_value = Transform(node->value()); |
| 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) { |
| AstNode* new_array = Transform(node->array()); |
| AstNode* new_index = Transform(node->index_expr()); |
| 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) { |
| AstNode* new_array = Transform(node->array()); |
| AstNode* new_index = Transform(node->index_expr()); |
| AstNode* new_value = Transform(node->value()); |
| 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) { |
| AstNode* new_expr = Transform(node->expr()); |
| 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++) { |
| result->AddNode(Transform(node->nodes()[i])); |
| } |
| result_ = result; |
| } |
| |
| |
| void AwaitTransformer::VisitThrowNode(ThrowNode* node) { |
| // TODO(mlippautz): Check if relevant. |
| AstNode* new_exception = Transform(node->exception()); |
| AstNode* new_stacktrace = Transform(node->stacktrace()); |
| result_ = new(I) ThrowNode(node->token_pos(), |
| new_exception, |
| new_stacktrace); |
| } |
| |
| } // namespace dart |