blob: 95d8d4f37dc8ce33dab80a2117276d9731a2f5dc [file] [log] [blame]
// 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/object_store.h"
#include "vm/parser.h"
#include "vm/thread.h"
namespace dart {
// Quick access to the current zone.
#define Z (thread()->zone())
// 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("%d", temp_cnt_));
symbol = Symbols::FromConcat(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(
Scanner::kNoSourcePos, symbol, Type::ZoneHandle(Type::DynamicType()));
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::AddToPreambleNewTempVar(AstNode* node) {
LocalVariable* tmp_var = EnsureCurrentTempVar();
preamble_->Add(new(Z) StoreLocalNode(Scanner::kNoSourcePos, tmp_var, node));
NextTempVar();
return tmp_var;
}
void AwaitTransformer::VisitLiteralNode(LiteralNode* node) {
result_ = node;
}
void AwaitTransformer::VisitTypeNode(TypeNode* node) {
result_ = new(Z) TypeNode(node->token_pos(), node->type());
}
void AwaitTransformer::VisitAwaitNode(AwaitNode* node) {
// Await transformation:
//
// :await_temp_var_X = <expr>;
// AwaitMarker(kNewContinuationState);
// :result_param = _awaitHelper(
// :await_temp_var_X, :async_then_callback, :async_catch_error_callback);
// return; // (return_type() == kContinuationTarget)
//
// :saved_try_ctx_var = :await_saved_try_ctx_var_y;
// :await_temp_var_(X+1) = :result_param;
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 = AddToPreambleNewTempVar(transformed_expr);
AwaitMarkerNode* await_marker =
new (Z) AwaitMarkerNode(async_temp_scope_, node->scope());
preamble_->Add(await_marker);
// :result_param = _awaitHelper(
// :await_temp, :async_then_callback, :async_catch_error_callback)
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(
Scanner::kNoSourcePos);
async_await_helper_args->Add(
new(Z) LoadLocalNode(Scanner::kNoSourcePos, await_temp));
async_await_helper_args->Add(
new(Z) LoadLocalNode(Scanner::kNoSourcePos, async_then_callback));
async_await_helper_args->Add(
new(Z) LoadLocalNode(Scanner::kNoSourcePos, async_catch_error_callback));
StaticCallNode* await_helper_call = new (Z) StaticCallNode(
node->token_pos(),
async_await_helper,
async_await_helper_args);
preamble_->Add(new(Z) StoreLocalNode(
Scanner::kNoSourcePos, result_param, await_helper_call));
ReturnNode* continuation_return = new(Z) ReturnNode(Scanner::kNoSourcePos);
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(
Scanner::kNoSourcePos,
node->saved_try_ctx(),
new (Z) LoadLocalNode(Scanner::kNoSourcePos,
node->async_saved_try_ctx())));
if (node->outer_saved_try_ctx() != NULL) {
preamble_->Add(new (Z) StoreLocalNode(
Scanner::kNoSourcePos,
node->outer_saved_try_ctx(),
new (Z) LoadLocalNode(Scanner::kNoSourcePos,
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(
Scanner::kNoSourcePos, async_op);
preamble_->Add(load_async_op);
LoadLocalNode* load_error_param = new (Z) LoadLocalNode(
Scanner::kNoSourcePos, error_param);
LoadLocalNode* load_stack_trace_param = new (Z) LoadLocalNode(
Scanner::kNoSourcePos, stack_trace_param);
SequenceNode* error_ne_null_branch = new (Z) SequenceNode(
Scanner::kNoSourcePos, ChainNewScope(preamble_->scope()));
error_ne_null_branch->Add(new (Z) ThrowNode(
Scanner::kNoSourcePos,
load_error_param,
load_stack_trace_param));
preamble_->Add(new (Z) IfNode(
Scanner::kNoSourcePos,
new (Z) ComparisonNode(
Scanner::kNoSourcePos,
Token::kNE,
load_error_param,
new (Z) LiteralNode(Scanner::kNoSourcePos,
Object::null_instance())),
error_ne_null_branch,
NULL));
LocalVariable* result = AddToPreambleNewTempVar(new(Z) LoadLocalNode(
Scanner::kNoSourcePos, result_param));
result_ = new(Z) 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 (Z) SequenceNode(
Scanner::kNoSourcePos, ChainNewScope(preamble_->scope()));
SequenceNode* saved_preamble = preamble_;
preamble_ = eval;
result = Transform(right);
preamble_ = saved_preamble;
IfNode* right_body = new(Z) IfNode(
Scanner::kNoSourcePos,
new(Z) ComparisonNode(
Scanner::kNoSourcePos,
compare_logical_op,
new_left,
new(Z) LiteralNode(Scanner::kNoSourcePos, 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 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(Z) BinaryOpNode(node->token_pos(),
node->kind(),
new_left,
new_right));
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
void AwaitTransformer::VisitBinaryOpWithMask32Node(
BinaryOpWithMask32Node* node) {
ASSERT((node->kind() != Token::kAND) && (node->kind() != Token::kOR));
AstNode* new_left = Transform(node->left());
AstNode* new_right = Transform(node->right());
LocalVariable* result = AddToPreambleNewTempVar(
new(Z) BinaryOpWithMask32Node(node->token_pos(),
node->kind(),
new_left,
new_right,
node->mask32()));
result_ = new(Z) 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(Z) ComparisonNode(node->token_pos(),
node->kind(),
new_left,
new_right));
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
void AwaitTransformer::VisitUnaryOpNode(UnaryOpNode* node) {
AstNode* new_operand = Transform(node->operand());
LocalVariable* result = AddToPreambleNewTempVar(
new(Z) UnaryOpNode(node->token_pos(), node->kind(), new_operand));
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
// ::= (<condition>) ? <true-branch> : <false-branch>
//
void AwaitTransformer::VisitConditionalExprNode(ConditionalExprNode* node) {
AstNode* new_condition = Transform(node->condition());
SequenceNode* new_true = new (Z) SequenceNode(
Scanner::kNoSourcePos, ChainNewScope(preamble_->scope()));
SequenceNode* saved_preamble = preamble_;
preamble_ = new_true;
AstNode* new_true_result = Transform(node->true_expr());
SequenceNode* new_false = new (Z) SequenceNode(
Scanner::kNoSourcePos, ChainNewScope(preamble_->scope()));
preamble_ = new_false;
AstNode* new_false_result = Transform(node->false_expr());
preamble_ = saved_preamble;
IfNode* new_if = new(Z) IfNode(Scanner::kNoSourcePos,
new_condition,
new_true,
new_false);
preamble_->Add(new_if);
LocalVariable* result = AddToPreambleNewTempVar(
new(Z) ConditionalExprNode(Scanner::kNoSourcePos,
new_condition,
new_true_result,
new_false_result));
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
void AwaitTransformer::VisitArgumentListNode(ArgumentListNode* node) {
ArgumentListNode* new_args = new(Z) ArgumentListNode(node->token_pos());
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<AstNode*> 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();
LocalVariable* result = AddToPreambleNewTempVar(
new(Z) StringInterpolateNode(node->token_pos(),
new_value));
result_ = new(Z) 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(Z) ClosureNode(node->token_pos(),
node->function(),
new_receiver,
node->scope()));
result_ = new(Z) 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(Z) InstanceCallNode(node->token_pos(),
new_receiver,
node->function_name(),
new_args,
node->is_conditional()));
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
void AwaitTransformer::VisitStaticCallNode(StaticCallNode* node) {
ArgumentListNode* new_args =
Transform(node->arguments())->AsArgumentListNode();
LocalVariable* result = AddToPreambleNewTempVar(
new(Z) StaticCallNode(node->token_pos(),
node->function(),
new_args));
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
void AwaitTransformer::VisitConstructorCallNode(ConstructorCallNode* node) {
ArgumentListNode* new_args =
Transform(node->arguments())->AsArgumentListNode();
LocalVariable* result = AddToPreambleNewTempVar(
new(Z) ConstructorCallNode(node->token_pos(),
node->type_arguments(),
node->constructor(),
new_args));
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
void AwaitTransformer::VisitInstanceGetterNode(InstanceGetterNode* node) {
AstNode* new_receiver = Transform(node->receiver());
LocalVariable* result = AddToPreambleNewTempVar(
new(Z) InstanceGetterNode(node->token_pos(),
new_receiver,
node->field_name(),
node->is_conditional()));
result_ = new(Z) 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(Z) InstanceSetterNode(node->token_pos(),
new_receiver,
node->field_name(),
new_value,
node->is_conditional()));
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
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());
new_getter->set_owner(node->owner());
LocalVariable* result = AddToPreambleNewTempVar(new_getter);
result_ = new(Z) 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());
StaticSetterNode* new_setter =
node->function().IsNull()
? new(Z) StaticSetterNode(node->token_pos(),
new_receiver,
node->cls(),
node->field_name(),
new_value)
: new(Z) StaticSetterNode(node->token_pos(),
new_receiver,
node->field_name(),
node->function(),
new_value);
LocalVariable* result = AddToPreambleNewTempVar(new_setter);
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
void AwaitTransformer::VisitLoadLocalNode(LoadLocalNode* node) {
result_ = node;
}
void AwaitTransformer::VisitStoreLocalNode(StoreLocalNode* node) {
AstNode* new_value = Transform(node->value());
result_ = new(Z) StoreLocalNode(node->token_pos(), &node->local(), new_value);
}
void AwaitTransformer::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) {
result_ = node;
}
void AwaitTransformer::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) {
AstNode* new_value = Transform(node->value());
result_ = new(Z) StoreStaticFieldNode(node->token_pos(),
node->field(),
new_value);
}
void AwaitTransformer::VisitLoadIndexedNode(LoadIndexedNode* node) {
AstNode* new_array = Transform(node->array());
AstNode* new_index = Transform(node->index_expr());
LocalVariable* result = AddToPreambleNewTempVar(
new(Z) LoadIndexedNode(node->token_pos(),
new_array,
new_index,
node->super_class()));
result_ = new(Z) 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(Z) StoreIndexedNode(node->token_pos(),
new_array,
new_index,
new_value,
node->super_class()));
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
void AwaitTransformer::VisitAssignableNode(AssignableNode* node) {
AstNode* new_expr = Transform(node->expr());
LocalVariable* result = AddToPreambleNewTempVar(
new(Z) AssignableNode(node->token_pos(),
new_expr,
node->type(),
node->dst_name()));
result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
}
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_ = new(Z) ThrowNode(node->token_pos(),
new_exception,
node->stacktrace());
}
} // namespace dart