// 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. #if !defined(DART_PRECOMPILED_RUNTIME) #include "vm/ast_transformer.h" #include "vm/object_store.h" #include "vm/parser.h" #include "vm/thread.h" namespace dart { // Quick access to the current thread. #define T (thread()) // Quick access to the current zone. #define Z (thread()->zone()) // Quick synthetic token position. #define ST(token_pos) ((token_pos).ToSynthetic()) // 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(Stop) \ 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 AwaitTransformer::AwaitTransformer(SequenceNode* preamble, LocalScope* async_temp_scope) : preamble_(preamble), temp_cnt_(0), async_temp_scope_(async_temp_scope), thread_(Thread::Current()) { ASSERT(async_temp_scope_ != NULL); } AstNode* AwaitTransformer::Transform(AstNode* expr) { expr->Visit(this); return result_; } LocalVariable* AwaitTransformer::EnsureCurrentTempVar() { String& symbol = String::ZoneHandle(Z, Symbols::NewFormatted(T, "%d", temp_cnt_)); symbol = Symbols::FromConcat(T, Symbols::AwaitTempVarPrefix(), symbol); ASSERT(!symbol.IsNull()); // Look up the variable in the scope used for async temp variables. LocalVariable* await_tmp = async_temp_scope_->LocalLookupVariable(symbol); if (await_tmp == NULL) { // We need a new temp variable; add it to the function's top scope. await_tmp = new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, symbol, Object::dynamic_type()); async_temp_scope_->AddVariable(await_tmp); // After adding it to the top scope, we can look it up from the preamble. // The following call includes an ASSERT check. await_tmp = GetVariableInScope(preamble_->scope(), symbol); } return await_tmp; } LocalVariable* AwaitTransformer::GetVariableInScope(LocalScope* scope, const String& symbol) { LocalVariable* var = scope->LookupVariable(symbol, false); ASSERT(var != NULL); return var; } LocalVariable* AwaitTransformer::AddNewTempVarToPreamble( AstNode* node, TokenPosition token_pos) { LocalVariable* tmp_var = EnsureCurrentTempVar(); ASSERT(token_pos.IsSynthetic() || token_pos.IsNoSource()); preamble_->Add(new (Z) StoreLocalNode(token_pos, tmp_var, node)); NextTempVar(); return tmp_var; } LoadLocalNode* AwaitTransformer::MakeName(AstNode* node) { LocalVariable* temp = AddNewTempVarToPreamble(node, ST(node->token_pos())); return new (Z) LoadLocalNode(ST(node->token_pos()), temp); } void AwaitTransformer::VisitLiteralNode(LiteralNode* node) { result_ = node; } void AwaitTransformer::VisitTypeNode(TypeNode* node) { if (node->is_deferred_reference()) { // Deferred references must use a temporary even after loading // happened, so that the number of await temps is the same as // before the loading. result_ = MakeName(node); } else { result_ = node; } } void AwaitTransformer::VisitAwaitNode(AwaitNode* node) { // Await transformation: // // :await_temp_var_X = ; // AwaitMarker(kNewContinuationState); // :result_param = _awaitHelper( // :await_temp_var_X, // :async_then_callback, // :async_catch_error_callback, // :async_op); // return; // (return_type() == kContinuationTarget) // // :saved_try_ctx_var = :await_saved_try_ctx_var_y; // :await_temp_var_(X+1) = :result_param; const TokenPosition token_pos = ST(node->token_pos()); LocalVariable* async_op = GetVariableInScope(preamble_->scope(), Symbols::AsyncOperation()); LocalVariable* async_then_callback = GetVariableInScope(preamble_->scope(), Symbols::AsyncThenCallback()); LocalVariable* async_catch_error_callback = GetVariableInScope( preamble_->scope(), Symbols::AsyncCatchErrorCallback()); LocalVariable* result_param = GetVariableInScope(preamble_->scope(), Symbols::AsyncOperationParam()); LocalVariable* error_param = GetVariableInScope( preamble_->scope(), Symbols::AsyncOperationErrorParam()); LocalVariable* stack_trace_param = GetVariableInScope( preamble_->scope(), Symbols::AsyncOperationStackTraceParam()); AstNode* transformed_expr = Transform(node->expr()); LocalVariable* await_temp = AddNewTempVarToPreamble(transformed_expr, ST(node->token_pos())); AwaitMarkerNode* await_marker = new (Z) AwaitMarkerNode(async_temp_scope_, node->scope(), token_pos); preamble_->Add(await_marker); // :result_param = _awaitHelper( // :await_temp, // :async_then_callback, // :async_catch_error_callback, // :async_op) const Library& async_lib = Library::Handle(Library::AsyncLibrary()); const Function& async_await_helper = Function::ZoneHandle( Z, async_lib.LookupFunctionAllowPrivate(Symbols::AsyncAwaitHelper())); ASSERT(!async_await_helper.IsNull()); ArgumentListNode* async_await_helper_args = new (Z) ArgumentListNode(token_pos); async_await_helper_args->Add(new (Z) LoadLocalNode(token_pos, await_temp)); async_await_helper_args->Add( new (Z) LoadLocalNode(token_pos, async_then_callback)); async_await_helper_args->Add( new (Z) LoadLocalNode(token_pos, async_catch_error_callback)); async_await_helper_args->Add(new (Z) LoadLocalNode(token_pos, async_op)); StaticCallNode* await_helper_call = new (Z) StaticCallNode(node->token_pos(), async_await_helper, async_await_helper_args, StaticCallNode::kStatic); preamble_->Add( new (Z) StoreLocalNode(token_pos, result_param, await_helper_call)); ReturnNode* continuation_return = new (Z) ReturnNode(token_pos); continuation_return->set_return_type(ReturnNode::kContinuationTarget); preamble_->Add(continuation_return); // If this expression is part of a try block, also append the code for // restoring the saved try context that lives on the stack and possibly the // saved try context of the outer try block. if (node->saved_try_ctx() != NULL) { preamble_->Add(new (Z) StoreLocalNode( token_pos, node->saved_try_ctx(), new (Z) LoadLocalNode(token_pos, node->async_saved_try_ctx()))); if (node->outer_saved_try_ctx() != NULL) { preamble_->Add(new (Z) StoreLocalNode( token_pos, node->outer_saved_try_ctx(), new (Z) LoadLocalNode(token_pos, node->outer_async_saved_try_ctx()))); } } else { ASSERT(node->outer_saved_try_ctx() == NULL); } // Load the async_op variable. It is unused, but the observatory uses it // to determine if a breakpoint is inside an asynchronous function. LoadLocalNode* load_async_op = new (Z) LoadLocalNode(token_pos, async_op); preamble_->Add(load_async_op); LoadLocalNode* load_error_param = new (Z) LoadLocalNode(token_pos, error_param); LoadLocalNode* load_stack_trace_param = new (Z) LoadLocalNode(token_pos, stack_trace_param); SequenceNode* error_ne_null_branch = new (Z) SequenceNode(token_pos, ChainNewScope(preamble_->scope())); error_ne_null_branch->Add( new (Z) ThrowNode(token_pos, load_error_param, load_stack_trace_param)); preamble_->Add(new (Z) IfNode( token_pos, new (Z) ComparisonNode( token_pos, Token::kNE, load_error_param, new (Z) LiteralNode(token_pos, Object::null_instance())), error_ne_null_branch, NULL)); result_ = MakeName(new (Z) LoadLocalNode(token_pos, result_param)); } // Transforms boolean expressions into a sequence of evaluations 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 (Z) SequenceNode(ST(new_left->token_pos()), ChainNewScope(preamble_->scope())); SequenceNode* saved_preamble = preamble_; preamble_ = eval; result = Transform(right); preamble_ = saved_preamble; IfNode* right_body = new (Z) IfNode(ST(new_left->token_pos()), new (Z) ComparisonNode( ST(new_left->token_pos()), compare_logical_op, new_left, new (Z) LiteralNode(ST(new_left->token_pos()), Bool::True())), eval, NULL); preamble_->Add(right_body); return result; } LocalScope* AwaitTransformer::ChainNewScope(LocalScope* parent) { return new (Z) LocalScope(parent, parent->function_level(), parent->loop_level()); } void AwaitTransformer::VisitBinaryOpNode(BinaryOpNode* node) { AstNode* new_left = Transform(node->left()); AstNode* new_right = NULL; // Preserve lazy evaluation. if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) { new_right = LazyTransform(node->kind(), new_left, node->right()); } else { new_right = Transform(node->right()); } result_ = MakeName(new (Z) BinaryOpNode(node->token_pos(), node->kind(), new_left, new_right)); } void AwaitTransformer::VisitComparisonNode(ComparisonNode* node) { AstNode* new_left = Transform(node->left()); AstNode* new_right = Transform(node->right()); result_ = MakeName(new (Z) ComparisonNode(node->token_pos(), node->kind(), new_left, new_right)); } void AwaitTransformer::VisitUnaryOpNode(UnaryOpNode* node) { AstNode* new_operand = Transform(node->operand()); result_ = MakeName( new (Z) UnaryOpNode(node->token_pos(), node->kind(), new_operand)); } // ::= () ? : // void AwaitTransformer::VisitConditionalExprNode(ConditionalExprNode* node) { AstNode* new_condition = Transform(node->condition()); SequenceNode* new_true = new (Z) SequenceNode( ST(node->true_expr()->token_pos()), ChainNewScope(preamble_->scope())); SequenceNode* saved_preamble = preamble_; preamble_ = new_true; AstNode* new_true_result = Transform(node->true_expr()); SequenceNode* new_false = new (Z) SequenceNode( ST(node->false_expr()->token_pos()), ChainNewScope(preamble_->scope())); preamble_ = new_false; AstNode* new_false_result = Transform(node->false_expr()); preamble_ = saved_preamble; IfNode* new_if = new (Z) IfNode(ST(node->token_pos()), new_condition, new_true, new_false); preamble_->Add(new_if); result_ = MakeName(new (Z) ConditionalExprNode( ST(node->token_pos()), new_condition, new_true_result, new_false_result)); } void AwaitTransformer::VisitArgumentListNode(ArgumentListNode* node) { ArgumentListNode* new_args = new (Z) ArgumentListNode(node->token_pos(), node->type_arguments()); for (intptr_t i = 0; i < node->length(); i++) { new_args->Add(Transform(node->NodeAt(i))); } new_args->set_names(node->names()); result_ = new_args; } void AwaitTransformer::VisitArrayNode(ArrayNode* node) { GrowableArray new_elements; for (intptr_t i = 0; i < node->length(); i++) { new_elements.Add(Transform(node->ElementAt(i))); } result_ = new (Z) ArrayNode(node->token_pos(), node->type(), new_elements); } void AwaitTransformer::VisitStringInterpolateNode(StringInterpolateNode* node) { ArrayNode* new_value = Transform(node->value())->AsArrayNode(); result_ = MakeName(new (Z) StringInterpolateNode(node->token_pos(), new_value)); } void AwaitTransformer::VisitClosureNode(ClosureNode* node) { AstNode* new_receiver = node->receiver(); if (new_receiver != NULL) { new_receiver = Transform(new_receiver); } result_ = MakeName(new (Z) ClosureNode(node->token_pos(), node->function(), new_receiver, node->scope())); } void AwaitTransformer::VisitInstanceCallNode(InstanceCallNode* node) { AstNode* new_receiver = Transform(node->receiver()); ArgumentListNode* new_args = Transform(node->arguments())->AsArgumentListNode(); result_ = MakeName(new (Z) InstanceCallNode(node->token_pos(), new_receiver, node->function_name(), new_args, node->is_conditional())); } void AwaitTransformer::VisitStaticCallNode(StaticCallNode* node) { ArgumentListNode* new_args = Transform(node->arguments())->AsArgumentListNode(); result_ = MakeName(new (Z) StaticCallNode(node->token_pos(), node->function(), new_args, node->rebind_rule())); } void AwaitTransformer::VisitConstructorCallNode(ConstructorCallNode* node) { ArgumentListNode* new_args = Transform(node->arguments())->AsArgumentListNode(); result_ = MakeName( new (Z) ConstructorCallNode(node->token_pos(), node->type_arguments(), node->constructor(), new_args)); } void AwaitTransformer::VisitInstanceGetterNode(InstanceGetterNode* node) { AstNode* new_receiver = Transform(node->receiver()); result_ = MakeName(new (Z) InstanceGetterNode(node->token_pos(), new_receiver, node->field_name(), node->is_conditional())); } 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()); result_ = MakeName(new (Z) InstanceSetterNode(node->token_pos(), new_receiver, node->field_name(), new_value, node->is_conditional())); } void AwaitTransformer::VisitStaticGetterNode(StaticGetterNode* node) { AstNode* new_receiver = node->receiver(); if (new_receiver != NULL) { new_receiver = Transform(new_receiver); } StaticGetterNode* new_getter = new (Z) StaticGetterNode(node->token_pos(), new_receiver, node->cls(), node->field_name(), node->rebind_rule()); new_getter->set_owner(node->owner()); result_ = MakeName(new_getter); } 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()); StaticSetterNode* new_setter = node->function().IsNull() ? new (Z) StaticSetterNode(node->token_pos(), new_receiver, node->cls(), node->field_name(), new_value, node->rebind_rule()) : new (Z) StaticSetterNode(node->token_pos(), new_receiver, node->field_name(), node->function(), new_value, node->rebind_rule()); result_ = MakeName(new_setter); } void AwaitTransformer::VisitLoadLocalNode(LoadLocalNode* node) { result_ = MakeName(node); } void AwaitTransformer::VisitStoreLocalNode(StoreLocalNode* node) { AstNode* new_value = Transform(node->value()); result_ = MakeName( new (Z) StoreLocalNode(node->token_pos(), &node->local(), new_value)); } void AwaitTransformer::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) { result_ = MakeName(node); } void AwaitTransformer::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) { AstNode* new_value = Transform(node->value()); result_ = MakeName(new (Z) StoreStaticFieldNode( node->token_pos(), Field::ZoneHandle(Z, node->field().Original()), new_value)); } void AwaitTransformer::VisitLoadIndexedNode(LoadIndexedNode* node) { AstNode* new_array = Transform(node->array()); AstNode* new_index = Transform(node->index_expr()); result_ = MakeName(new (Z) LoadIndexedNode(node->token_pos(), new_array, new_index, node->super_class())); } void AwaitTransformer::VisitStoreIndexedNode(StoreIndexedNode* node) { AstNode* new_array = Transform(node->array()); AstNode* new_index = Transform(node->index_expr()); AstNode* new_value = Transform(node->value()); result_ = MakeName(new (Z) StoreIndexedNode( node->token_pos(), new_array, new_index, new_value, node->super_class())); } void AwaitTransformer::VisitAssignableNode(AssignableNode* node) { AstNode* new_expr = Transform(node->expr()); result_ = MakeName(new (Z) AssignableNode(node->token_pos(), new_expr, node->type(), node->dst_name())); } void AwaitTransformer::VisitLetNode(LetNode* node) { // Add all the initializer nodes to the preamble and the // temporary variables to the scope for async temporary variables. // The temporary variables will be captured as a side effect of being // added to a scope, and the subsequent nodes that are added to the // preample can access them. for (intptr_t i = 0; i < node->num_temps(); i++) { async_temp_scope_->AddVariable(node->TempAt(i)); AstNode* new_init_val = Transform(node->InitializerAt(i)); preamble_->Add(new (Z) StoreLocalNode(node->token_pos(), node->TempAt(i), new_init_val)); } // Add all expressions but the last to the preamble. We must do // this because subexpressions of the awaitable expression we // are currently transforming may depend on each other, // e.g. await foo(a++, a++). Thus we must preserve the order of the // transformed subexpressions. for (intptr_t i = 0; i < node->nodes().length() - 1; i++) { preamble_->Add(Transform(node->nodes()[i])); } // The last expression in the let node is the value of the node. // The result of the transformed let node is this expression. ASSERT(node->nodes().length() > 0); const intptr_t last_node_index = node->nodes().length() - 1; result_ = Transform(node->nodes()[last_node_index]); } void AwaitTransformer::VisitThrowNode(ThrowNode* node) { AstNode* new_exception = Transform(node->exception()); result_ = MakeName( new (Z) ThrowNode(node->token_pos(), new_exception, node->stacktrace())); } } // namespace dart #endif // !defined(DART_PRECOMPILED_RUNTIME)