| 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..2a9ae4a9efa6ea4bb536bfeb94f9867a5b657294
|
| --- /dev/null
|
| +++ b/runtime/vm/ast_transformer.cc
|
| @@ -0,0 +1,508 @@
|
| +// 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
|
| +
|
| +
|
| +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%" 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 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
|
|
|