Index: runtime/vm/kernel_to_il.cc |
diff --git a/runtime/vm/kernel_to_il.cc b/runtime/vm/kernel_to_il.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e3320efff78febc5b3ddc987439b851c84b0bfee |
--- /dev/null |
+++ b/runtime/vm/kernel_to_il.cc |
@@ -0,0 +1,5567 @@ |
+// Copyright (c) 2016, 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 <map> |
+#include <set> |
+#include <string> |
+ |
+#include "vm/kernel_to_il.h" |
+ |
+#include "vm/compiler.h" |
+#include "vm/intermediate_language.h" |
+#include "vm/kernel_reader.h" |
+#include "vm/longjump.h" |
+#include "vm/method_recognizer.h" |
+#include "vm/object_store.h" |
+#include "vm/report.h" |
+#include "vm/resolver.h" |
+#include "vm/stack_frame.h" |
+ |
+namespace dart { |
+ |
+DECLARE_FLAG(bool, support_externalizable_strings); |
+ |
+namespace kernel { |
+ |
+#define Z (zone_) |
+#define H (translation_helper_) |
+#define T (type_translator_) |
+#define I Isolate::Current() |
+ |
+ |
+void ScopeBuilder::EnterScope(TreeNode* node) { |
+ scope_ = new (Z) LocalScope(scope_, depth_.function_, depth_.loop_); |
+ result_->scopes.Insert(node, scope_); |
+} |
+ |
+ |
+void ScopeBuilder::ExitScope() { scope_ = scope_->parent(); } |
+ |
+ |
+LocalVariable* ScopeBuilder::MakeVariable(const dart::String& name) { |
+ return new (Z) |
+ LocalVariable(TokenPosition::kNoSource, name, Object::dynamic_type()); |
+} |
+ |
+ |
+LocalVariable* ScopeBuilder::MakeVariable(const dart::String& name, |
+ const Type& type) { |
+ return new (Z) LocalVariable(TokenPosition::kNoSource, name, type); |
+} |
+ |
+ |
+void ScopeBuilder::AddParameters(FunctionNode* function, intptr_t pos) { |
+ List<VariableDeclaration>& positional = function->positional_parameters(); |
+ for (intptr_t i = 0; i < positional.length(); ++i) { |
+ AddParameter(positional[i], pos++); |
+ } |
+ List<VariableDeclaration>& named = function->named_parameters(); |
+ for (intptr_t i = 0; i < named.length(); ++i) { |
+ AddParameter(named[i], pos++); |
+ } |
+} |
+ |
+ |
+void ScopeBuilder::AddParameter(VariableDeclaration* declaration, |
+ intptr_t pos) { |
+ // TODO(27590): Handle final. |
+ LocalVariable* variable = MakeVariable(H.DartSymbol(declaration->name())); |
+ scope_->InsertParameterAt(pos, variable); |
+ result_->locals.Insert(declaration, variable); |
+ |
+ // The default value may contain 'let' bindings for which the constant |
+ // evaluator needs scope bindings. |
+ Expression* defaultValue = declaration->initializer(); |
+ if (defaultValue != NULL) { |
+ defaultValue->AcceptExpressionVisitor(this); |
+ } |
+} |
+ |
+ |
+void ScopeBuilder::AddExceptionVariable( |
+ GrowableArray<LocalVariable*>* variables, const char* prefix, |
+ intptr_t nesting_depth) { |
+ LocalVariable* v = NULL; |
+ |
+ // If we are inside a function with yield points then Kernel transformer |
+ // could have lifted some of the auxiliary exception variables into the |
+ // context to preserve them across yield points because they might |
+ // be needed for rethrow. |
+ // Check if it did and capture such variables instead of introducing |
+ // new local ones. |
+ // Note: function that wrap kSyncYielding function does not contain |
+ // its own try/catches. |
+ if (current_function_node_->async_marker() == FunctionNode::kSyncYielding) { |
+ ASSERT(current_function_scope_->parent() != NULL); |
+ v = current_function_scope_->parent()->LocalLookupVariable( |
+ GenerateName(prefix, nesting_depth - 1)); |
+ if (v != NULL) { |
+ scope_->CaptureVariable(v); |
+ } |
+ } |
+ |
+ // No need to create variables for try/catch-statements inside |
+ // nested functions. |
+ if (depth_.function_ > 0) return; |
+ if (variables->length() >= nesting_depth) return; |
+ |
+ // If variable was not lifted by the transformer introduce a new |
+ // one into the current function scope. |
+ if (v == NULL) { |
+ v = MakeVariable(GenerateName(prefix, nesting_depth - 1)); |
+ |
+ // If transformer did not lift the variable then there is no need |
+ // to lift it into the context when we encouter a YieldStatement. |
+ v->set_is_forced_stack(); |
+ current_function_scope_->AddVariable(v); |
+ } |
+ |
+ variables->Add(v); |
+} |
+ |
+ |
+void ScopeBuilder::AddTryVariables() { |
+ AddExceptionVariable(&result_->catch_context_variables, |
+ ":saved_try_context_var", depth_.try_); |
+} |
+ |
+ |
+void ScopeBuilder::AddCatchVariables() { |
+ AddExceptionVariable(&result_->exception_variables, ":exception", |
+ depth_.catch_); |
+ AddExceptionVariable(&result_->stack_trace_variables, ":stack_trace", |
+ depth_.catch_); |
+} |
+ |
+ |
+void ScopeBuilder::AddIteratorVariable() { |
+ if (depth_.function_ > 0) return; |
+ if (result_->iterator_variables.length() >= depth_.for_in_) return; |
+ |
+ ASSERT(result_->iterator_variables.length() == depth_.for_in_ - 1); |
+ LocalVariable* iterator = |
+ MakeVariable(GenerateName(":iterator", depth_.for_in_ - 1)); |
+ current_function_scope_->AddVariable(iterator); |
+ result_->iterator_variables.Add(iterator); |
+} |
+ |
+ |
+void ScopeBuilder::LookupVariable(VariableDeclaration* declaration) { |
+ LocalVariable* variable = result_->locals.Lookup(declaration); |
+ if (variable == NULL) { |
+ // We have not seen a declaration of the variable, so it must be the |
+ // case that we are compiling a nested function and the variable is |
+ // declared in an outer scope. In that case, look it up in the scope by |
+ // name and add it to the variable map to simplify later lookup. |
+ ASSERT(current_function_scope_->parent() != NULL); |
+ const dart::String& name = H.DartSymbol(declaration->name()); |
+ variable = current_function_scope_->parent()->LookupVariable(name, true); |
+ ASSERT(variable != NULL); |
+ result_->locals.Insert(declaration, variable); |
+ } |
+ if (variable->owner()->function_level() < scope_->function_level()) { |
+ // We call `LocalScope->CaptureVariable(variable)` in two scenarios for two |
+ // different reasons: |
+ // Scenario 1: |
+ // We need to know which variables defined in this function |
+ // are closed over by nested closures in order to ensure we will |
+ // create a [Context] object of appropriate size and store captured |
+ // variables there instead of the stack. |
+ // Scenario 2: |
+ // We need to find out which variables defined in enclosing functions |
+ // are closed over by this function/closure or nested closures. This |
+ // is necessary in order to build a fat flattened [ContextScope] |
+ // object. |
+ scope_->CaptureVariable(variable); |
+ } else { |
+ ASSERT(variable->owner()->function_level() == scope_->function_level()); |
+ } |
+} |
+ |
+ |
+void ScopeBuilder::LookupCapturedVariableByName(LocalVariable** variable, |
+ const dart::String& name) { |
+ if (*variable == NULL) { |
+ *variable = scope_->LookupVariable(name, true); |
+ ASSERT(*variable != NULL); |
+ scope_->CaptureVariable(*variable); |
+ } |
+} |
+ |
+ |
+const dart::String& ScopeBuilder::GenerateName(const char* prefix, |
+ intptr_t suffix) { |
+ char name[64]; |
+ OS::SNPrint(name, 64, "%s%" Pd "", prefix, suffix); |
+ return H.DartSymbol(name); |
+} |
+ |
+ |
+void ScopeBuilder::AddVariable(VariableDeclaration* declaration) { |
+ // TODO(27590): Handle final and const, including function declarations. |
+ const dart::String& name = declaration->name()->is_empty() |
+ ? GenerateName(":var", name_index_++) |
+ : H.DartSymbol(declaration->name()); |
+ LocalVariable* variable = MakeVariable(name); |
+ scope_->AddVariable(variable); |
+ result_->locals.Insert(declaration, variable); |
+} |
+ |
+ |
+static bool IsStaticInitializer(const Function& function, Zone* zone) { |
+ return (function.kind() == RawFunction::kImplicitStaticFinalGetter) && |
+ dart::String::Handle(zone, function.name()) |
+ .StartsWith(Symbols::InitPrefix()); |
+} |
+ |
+ |
+ScopeBuildingResult* ScopeBuilder::BuildScopes() { |
+ if (result_ != NULL) return result_; |
+ |
+ ASSERT(scope_ == NULL && depth_.loop_ == 0 && depth_.function_ == 0); |
+ result_ = new (Z) ScopeBuildingResult(); |
+ |
+ ParsedFunction* parsed_function = parsed_function_; |
+ const dart::Function& function = parsed_function->function(); |
+ |
+ LocalScope* enclosing_scope = NULL; |
+ if (function.IsLocalFunction()) { |
+ enclosing_scope = LocalScope::RestoreOuterScope( |
+ ContextScope::Handle(Z, function.context_scope())); |
+ } |
+ current_function_scope_ = scope_ = new (Z) LocalScope(enclosing_scope, 0, 0); |
+ scope_->AddVariable(parsed_function->EnsureExpressionTemp()); |
+ scope_->AddVariable(parsed_function->current_context_var()); |
+ parsed_function->SetNodeSequence( |
+ new SequenceNode(TokenPosition::kNoSource, scope_)); |
+ |
+ switch (function.kind()) { |
+ case RawFunction::kClosureFunction: |
+ case RawFunction::kRegularFunction: |
+ case RawFunction::kGetterFunction: |
+ case RawFunction::kSetterFunction: |
+ case RawFunction::kConstructor: { |
+ FunctionNode* node; |
+ if (node_->IsProcedure()) { |
+ node = Procedure::Cast(node_)->function(); |
+ } else if (node_->IsConstructor()) { |
+ node = Constructor::Cast(node_)->function(); |
+ } else { |
+ node = FunctionNode::Cast(node_); |
+ } |
+ current_function_node_ = node; |
+ |
+ intptr_t pos = 0; |
+ if (function.IsClosureFunction()) { |
+ LocalVariable* variable = MakeVariable(Symbols::ClosureParameter()); |
+ scope_->InsertParameterAt(pos++, variable); |
+ } else if (!function.is_static()) { |
+ // We use [is_static] instead of [IsStaticFunction] because the latter |
+ // returns `false` for constructors. |
+ dart::Class& klass = dart::Class::Handle(Z, function.Owner()); |
+ Type& klass_type = H.GetCanonicalType(klass); |
+ LocalVariable* variable = MakeVariable(Symbols::This(), klass_type); |
+ scope_->InsertParameterAt(pos++, variable); |
+ result_->this_variable = variable; |
+ |
+ // We visit instance field initializers because they might contain |
+ // [Let] expressions and we need to have a mapping. |
+ if (node_->IsConstructor()) { |
+ Class* klass = Class::Cast(Constructor::Cast(node_)->parent()); |
+ |
+ for (intptr_t i = 0; i < klass->fields().length(); i++) { |
+ Field* field = klass->fields()[i]; |
+ if (!field->IsStatic() && (field->initializer() != NULL)) { |
+ EnterScope(field); |
+ field->initializer()->AcceptExpressionVisitor(this); |
+ ExitScope(); |
+ } |
+ } |
+ } |
+ } else if (function.IsFactory()) { |
+ LocalVariable* variable = MakeVariable( |
+ Symbols::TypeArgumentsParameter(), AbstractType::dynamic_type()); |
+ scope_->InsertParameterAt(pos++, variable); |
+ result_->type_arguments_variable = variable; |
+ } |
+ AddParameters(node, pos); |
+ |
+ // We generate a syntethic body for implicit closure functions - which |
+ // will forward the call to the real function. |
+ // -> see BuildGraphOfImplicitClosureFunction |
+ if (!function.IsImplicitClosureFunction()) { |
+ node_->AcceptVisitor(this); |
+ } |
+ break; |
+ } |
+ case RawFunction::kImplicitGetter: |
+ case RawFunction::kImplicitStaticFinalGetter: |
+ case RawFunction::kImplicitSetter: { |
+ ASSERT(node_->IsField()); |
+ if (IsStaticInitializer(function, Z)) { |
+ node_->AcceptVisitor(this); |
+ break; |
+ } |
+ bool is_setter = function.IsImplicitSetterFunction(); |
+ bool is_method = !function.IsStaticFunction(); |
+ intptr_t pos = 0; |
+ if (is_method) { |
+ dart::Class& klass = dart::Class::Handle(Z, function.Owner()); |
+ Type& klass_type = H.GetCanonicalType(klass); |
+ LocalVariable* variable = MakeVariable(Symbols::This(), klass_type); |
+ scope_->InsertParameterAt(pos++, variable); |
+ result_->this_variable = variable; |
+ } |
+ if (is_setter) { |
+ result_->setter_value = MakeVariable(Symbols::Value()); |
+ scope_->InsertParameterAt(pos++, result_->setter_value); |
+ } |
+ break; |
+ } |
+ case RawFunction::kMethodExtractor: { |
+ // Add a receiver parameter. Though it is captured, we emit code to |
+ // explicitly copy it to a fixed offset in a freshly-allocated context |
+ // instead of using the generic code for regular functions. |
+ // Therefore, it isn't necessary to mark it as captured here. |
+ dart::Class& klass = dart::Class::Handle(Z, function.Owner()); |
+ Type& klass_type = H.GetCanonicalType(klass); |
+ LocalVariable* variable = MakeVariable(Symbols::This(), klass_type); |
+ scope_->InsertParameterAt(0, variable); |
+ result_->this_variable = variable; |
+ break; |
+ } |
+ case RawFunction::kNoSuchMethodDispatcher: |
+ case RawFunction::kInvokeFieldDispatcher: |
+ for (intptr_t i = 0; i < function.NumParameters(); ++i) { |
+ LocalVariable* variable = MakeVariable( |
+ dart::String::ZoneHandle(Z, function.ParameterNameAt(i))); |
+ scope_->InsertParameterAt(i, variable); |
+ } |
+ break; |
+ case RawFunction::kSignatureFunction: |
+ case RawFunction::kIrregexpFunction: |
+ UNREACHABLE(); |
+ } |
+ |
+ parsed_function->AllocateVariables(); |
+ |
+ return result_; |
+} |
+ |
+ |
+void ScopeBuilder::VisitThisExpression(ThisExpression* node) { |
+ HandleSpecialLoad(&result_->this_variable, Symbols::This()); |
+} |
+ |
+ |
+void ScopeBuilder::VisitTypeParameterType(TypeParameterType* node) { |
+ Function& function = Function::Handle(Z, parsed_function_->function().raw()); |
+ while (function.IsClosureFunction()) { |
+ function = function.parent_function(); |
+ } |
+ |
+ if (function.IsFactory()) { |
+ // The type argument vector is passed as the very first argument to the |
+ // factory constructor function. |
+ HandleSpecialLoad(&result_->type_arguments_variable, |
+ Symbols::TypeArgumentsParameter()); |
+ } else { |
+ // The type argument vector is stored on the instance object. We therefore |
+ // need to capture `this`. |
+ HandleSpecialLoad(&result_->this_variable, Symbols::This()); |
+ } |
+} |
+ |
+ |
+void ScopeBuilder::VisitVariableGet(VariableGet* node) { |
+ LookupVariable(node->variable()); |
+} |
+ |
+ |
+void ScopeBuilder::VisitVariableSet(VariableSet* node) { |
+ LookupVariable(node->variable()); |
+ node->VisitChildren(this); |
+} |
+ |
+ |
+void ScopeBuilder::HandleLocalFunction(TreeNode* parent, |
+ FunctionNode* function) { |
+ LocalScope* saved_function_scope = current_function_scope_; |
+ FunctionNode* saved_function_node = current_function_node_; |
+ ScopeBuilder::DepthState saved_depth_state = depth_; |
+ depth_ = DepthState(depth_.function_ + 1); |
+ EnterScope(parent); |
+ current_function_scope_ = scope_; |
+ current_function_node_ = function; |
+ if (depth_.function_ == 1) { |
+ FunctionScope function_scope = {function, scope_}; |
+ result_->function_scopes.Add(function_scope); |
+ } |
+ AddParameters(function); |
+ VisitFunctionNode(function); |
+ ExitScope(); |
+ depth_ = saved_depth_state; |
+ current_function_scope_ = saved_function_scope; |
+ current_function_node_ = saved_function_node; |
+} |
+ |
+ |
+void ScopeBuilder::HandleSpecialLoad(LocalVariable** variable, |
+ const dart::String& symbol) { |
+ if (current_function_scope_->parent() != NULL) { |
+ // We are building the scope tree of a closure function and saw [node]. We |
+ // lazily populate the variable using the parent function scope. |
+ if (*variable == NULL) { |
+ *variable = |
+ current_function_scope_->parent()->LookupVariable(symbol, true); |
+ ASSERT(*variable != NULL); |
+ } |
+ } |
+ |
+ if ((current_function_scope_->parent() != NULL) || |
+ (scope_->function_level() > 0)) { |
+ // Every scope we use the [variable] from needs to be notified of the usage |
+ // in order to ensure that preserving the context scope on that particular |
+ // use-site also includes the [variable]. |
+ scope_->CaptureVariable(*variable); |
+ } |
+} |
+ |
+ |
+void ScopeBuilder::VisitFunctionExpression(FunctionExpression* node) { |
+ HandleLocalFunction(node, node->function()); |
+} |
+ |
+ |
+void ScopeBuilder::VisitLet(Let* node) { |
+ EnterScope(node); |
+ node->VisitChildren(this); |
+ ExitScope(); |
+} |
+ |
+ |
+void ScopeBuilder::VisitBlock(Block* node) { |
+ EnterScope(node); |
+ node->VisitChildren(this); |
+ ExitScope(); |
+} |
+ |
+ |
+void ScopeBuilder::VisitVariableDeclaration(VariableDeclaration* node) { |
+ AddVariable(node); |
+ node->VisitChildren(this); |
+} |
+ |
+ |
+void ScopeBuilder::VisitFunctionDeclaration(FunctionDeclaration* node) { |
+ VisitVariableDeclaration(node->variable()); |
+ HandleLocalFunction(node, node->function()); |
+} |
+ |
+ |
+void ScopeBuilder::VisitWhileStatement(WhileStatement* node) { |
+ ++depth_.loop_; |
+ node->VisitChildren(this); |
+ --depth_.loop_; |
+} |
+ |
+ |
+void ScopeBuilder::VisitDoStatement(DoStatement* node) { |
+ ++depth_.loop_; |
+ node->VisitChildren(this); |
+ --depth_.loop_; |
+} |
+ |
+ |
+void ScopeBuilder::VisitForStatement(ForStatement* node) { |
+ EnterScope(node); |
+ List<VariableDeclaration>& variables = node->variables(); |
+ for (intptr_t i = 0; i < variables.length(); ++i) { |
+ VisitVariableDeclaration(variables[i]); |
+ } |
+ ++depth_.loop_; |
+ if (node->condition() != NULL) { |
+ node->condition()->AcceptExpressionVisitor(this); |
+ } |
+ node->body()->AcceptStatementVisitor(this); |
+ List<Expression>& updates = node->updates(); |
+ for (intptr_t i = 0; i < updates.length(); ++i) { |
+ updates[i]->AcceptExpressionVisitor(this); |
+ } |
+ --depth_.loop_; |
+ ExitScope(); |
+} |
+ |
+ |
+void ScopeBuilder::VisitForInStatement(ForInStatement* node) { |
+ node->iterable()->AcceptExpressionVisitor(this); |
+ ++depth_.for_in_; |
+ AddIteratorVariable(); |
+ ++depth_.loop_; |
+ EnterScope(node); |
+ VisitVariableDeclaration(node->variable()); |
+ node->body()->AcceptStatementVisitor(this); |
+ ExitScope(); |
+ --depth_.loop_; |
+ --depth_.for_in_; |
+} |
+ |
+ |
+void ScopeBuilder::AddSwitchVariable() { |
+ if ((depth_.function_ == 0) && (result_->switch_variable == NULL)) { |
+ LocalVariable* variable = MakeVariable(Symbols::SwitchExpr()); |
+ current_function_scope_->AddVariable(variable); |
+ result_->switch_variable = variable; |
+ } |
+} |
+ |
+ |
+void ScopeBuilder::VisitSwitchStatement(SwitchStatement* node) { |
+ AddSwitchVariable(); |
+ node->VisitChildren(this); |
+} |
+ |
+ |
+void ScopeBuilder::VisitReturnStatement(ReturnStatement* node) { |
+ if ((depth_.function_ == 0) && (depth_.finally_ > 0) && |
+ (result_->finally_return_variable == NULL)) { |
+ const dart::String& name = H.DartSymbol(":try_finally_return_value"); |
+ LocalVariable* variable = MakeVariable(name); |
+ current_function_scope_->AddVariable(variable); |
+ result_->finally_return_variable = variable; |
+ } |
+ node->VisitChildren(this); |
+} |
+ |
+ |
+void ScopeBuilder::VisitTryCatch(TryCatch* node) { |
+ ++depth_.try_; |
+ AddTryVariables(); |
+ node->body()->AcceptStatementVisitor(this); |
+ --depth_.try_; |
+ |
+ ++depth_.catch_; |
+ AddCatchVariables(); |
+ List<Catch>& catches = node->catches(); |
+ for (intptr_t i = 0; i < catches.length(); ++i) { |
+ Catch* ketch = catches[i]; |
+ EnterScope(ketch); |
+ if (ketch->exception() != NULL) { |
+ VisitVariableDeclaration(ketch->exception()); |
+ } |
+ if (ketch->stack_trace() != NULL) { |
+ VisitVariableDeclaration(ketch->stack_trace()); |
+ } |
+ ketch->body()->AcceptStatementVisitor(this); |
+ ExitScope(); |
+ } |
+ --depth_.catch_; |
+} |
+ |
+ |
+void ScopeBuilder::VisitTryFinally(TryFinally* node) { |
+ ++depth_.try_; |
+ ++depth_.finally_; |
+ AddTryVariables(); |
+ node->body()->AcceptStatementVisitor(this); |
+ --depth_.finally_; |
+ --depth_.try_; |
+ |
+ ++depth_.catch_; |
+ AddCatchVariables(); |
+ node->finalizer()->AcceptStatementVisitor(this); |
+ --depth_.catch_; |
+} |
+ |
+ |
+void ScopeBuilder::VisitFunctionNode(FunctionNode* node) { |
+ List<TypeParameter>& type_parameters = node->type_parameters(); |
+ for (intptr_t i = 0; i < type_parameters.length(); ++i) { |
+ VisitTypeParameter(type_parameters[i]); |
+ } |
+ // Do not visit the positional and named parameters, because they've |
+ // already been added to the scope. |
+ if (node->body() != NULL) { |
+ node->body()->AcceptStatementVisitor(this); |
+ } |
+ |
+ // Ensure that :await_jump_var and :await_ctx_var are captured. |
+ if (node->async_marker() == FunctionNode::kSyncYielding) { |
+ { |
+ LocalVariable* temp = NULL; |
+ LookupCapturedVariableByName( |
+ (depth_.function_ == 0) ? &result_->yield_jump_variable : &temp, |
+ Symbols::AwaitJumpVar()); |
+ } |
+ { |
+ LocalVariable* temp = NULL; |
+ LookupCapturedVariableByName( |
+ (depth_.function_ == 0) ? &result_->yield_context_variable : &temp, |
+ Symbols::AwaitContextVar()); |
+ } |
+ } |
+} |
+ |
+ |
+void ScopeBuilder::VisitYieldStatement(YieldStatement* node) { |
+ ASSERT(node->is_native()); |
+ if (depth_.function_ == 0) { |
+ AddSwitchVariable(); |
+ // Promote all currently visible local variables into the context. |
+ // TODO(27590) CaptureLocalVariables promotes to many variables into |
+ // the scope. Mark those variables as stack_local. |
+ // TODO(27590) we don't need to promote those variables that are |
+ // not used across yields. |
+ scope_->CaptureLocalVariables(current_function_scope_); |
+ } |
+} |
+ |
+ |
+void ScopeBuilder::VisitAssertStatement(AssertStatement* node) { |
+ if (I->asserts()) { |
+ RecursiveVisitor::VisitAssertStatement(node); |
+ } |
+} |
+ |
+ |
+void ScopeBuilder::VisitConstructor(Constructor* node) { |
+ // Field initializers that come from non-static field declarations are |
+ // compiled as if they appear in the constructor initializer list. This is |
+ // important for closure-valued field initializers because the VM expects the |
+ // corresponding closure functions to appear as if they were nested inside the |
+ // constructor. |
+ List<Field>& fields = Class::Cast(node->parent())->fields(); |
+ for (intptr_t i = 0; i < fields.length(); ++i) { |
+ Field* field = fields[i]; |
+ Expression* initializer = field->initializer(); |
+ if (!field->IsStatic() && (initializer != NULL)) { |
+ initializer->AcceptExpressionVisitor(this); |
+ } |
+ } |
+ node->VisitChildren(this); |
+} |
+ |
+ |
+class BreakableBlock { |
+ public: |
+ BreakableBlock(FlowGraphBuilder* builder, LabeledStatement* statement) |
+ : builder_(builder), |
+ labeled_statement_(statement), |
+ outer_(builder->breakable_block_), |
+ destination_(NULL), |
+ outer_finally_(builder->try_finally_block_), |
+ context_depth_(builder->context_depth_) { |
+ builder_->breakable_block_ = this; |
+ } |
+ ~BreakableBlock() { builder_->breakable_block_ = outer_; } |
+ |
+ bool HadJumper() { return destination_ != NULL; } |
+ |
+ JoinEntryInstr* destination() { return destination_; } |
+ |
+ JoinEntryInstr* BreakDestination(LabeledStatement* label, |
+ TryFinallyBlock** outer_finally, |
+ intptr_t* context_depth) { |
+ BreakableBlock* block = builder_->breakable_block_; |
+ while (block->labeled_statement_ != label) { |
+ block = block->outer_; |
+ } |
+ ASSERT(block != NULL); |
+ *outer_finally = block->outer_finally_; |
+ *context_depth = block->context_depth_; |
+ return block->EnsureDestination(); |
+ } |
+ |
+ private: |
+ JoinEntryInstr* EnsureDestination() { |
+ if (destination_ == NULL) { |
+ destination_ = builder_->BuildJoinEntry(); |
+ } |
+ return destination_; |
+ } |
+ |
+ FlowGraphBuilder* builder_; |
+ LabeledStatement* labeled_statement_; |
+ BreakableBlock* outer_; |
+ JoinEntryInstr* destination_; |
+ TryFinallyBlock* outer_finally_; |
+ intptr_t context_depth_; |
+}; |
+ |
+ |
+class SwitchBlock { |
+ public: |
+ SwitchBlock(FlowGraphBuilder* builder, SwitchStatement* switch_stmt) |
+ : builder_(builder), |
+ outer_(builder->switch_block_), |
+ outer_finally_(builder->try_finally_block_), |
+ switch_statement_(switch_stmt), |
+ context_depth_(builder->context_depth_) { |
+ builder_->switch_block_ = this; |
+ } |
+ ~SwitchBlock() { builder_->switch_block_ = outer_; } |
+ |
+ bool HadJumper(SwitchCase* switch_case) { |
+ return destinations_.Lookup(switch_case) != NULL; |
+ } |
+ |
+ JoinEntryInstr* Destination(SwitchCase* label, |
+ TryFinallyBlock** outer_finally = NULL, |
+ intptr_t* context_depth = NULL) { |
+ // Find corresponding [SwitchStatement]. |
+ SwitchBlock* block = this; |
+ while (true) { |
+ block->EnsureSwitchCaseMapping(); |
+ if (block->Contains(label)) break; |
+ block = block->outer_; |
+ } |
+ |
+ // Set the outer finally block. |
+ if (outer_finally != NULL) { |
+ *outer_finally = block->outer_finally_; |
+ *context_depth = block->context_depth_; |
+ } |
+ |
+ // Ensure there's [JoinEntryInstr] for that [SwitchCase]. |
+ return block->EnsureDestination(label); |
+ } |
+ |
+ private: |
+ typedef std::set<SwitchCase*> DestinationSwitches; |
+ |
+ JoinEntryInstr* EnsureDestination(SwitchCase* switch_case) { |
+ JoinEntryInstr* cached_inst = destinations_.Lookup(switch_case); |
+ if (cached_inst == NULL) { |
+ JoinEntryInstr* inst = builder_->BuildJoinEntry(); |
+ destinations_.Insert(switch_case, inst); |
+ return inst; |
+ } |
+ return cached_inst; |
+ } |
+ |
+ void EnsureSwitchCaseMapping() { |
+ if (destination_switches_.begin() == destination_switches_.end()) { |
+ List<SwitchCase>& cases = switch_statement_->cases(); |
+ for (intptr_t i = 0; i < cases.length(); i++) { |
+ destination_switches_.insert(cases[i]); |
+ } |
+ } |
+ } |
+ |
+ bool Contains(SwitchCase* sc) { |
+ return destination_switches_.find(sc) != destination_switches_.end(); |
+ } |
+ |
+ FlowGraphBuilder* builder_; |
+ SwitchBlock* outer_; |
+ |
+ Map<SwitchCase, JoinEntryInstr*> destinations_; |
+ DestinationSwitches destination_switches_; |
+ |
+ TryFinallyBlock* outer_finally_; |
+ SwitchStatement* switch_statement_; |
+ intptr_t context_depth_; |
+}; |
+ |
+ |
+class TryFinallyBlock { |
+ public: |
+ TryFinallyBlock(FlowGraphBuilder* builder, Statement* finalizer) |
+ : builder_(builder), |
+ outer_(builder->try_finally_block_), |
+ finalizer_(finalizer), |
+ context_depth_(builder->context_depth_), |
+ // Finalizers are executed outside of the try block hence |
+ // try depth of finalizers are one less than current try |
+ // depth. |
+ try_depth_(builder->try_depth_ - 1) { |
+ builder_->try_finally_block_ = this; |
+ } |
+ ~TryFinallyBlock() { builder_->try_finally_block_ = outer_; } |
+ |
+ Statement* finalizer() const { return finalizer_; } |
+ intptr_t context_depth() const { return context_depth_; } |
+ intptr_t try_depth() const { return try_depth_; } |
+ TryFinallyBlock* outer() const { return outer_; } |
+ |
+ private: |
+ FlowGraphBuilder* const builder_; |
+ TryFinallyBlock* const outer_; |
+ Statement* const finalizer_; |
+ const intptr_t context_depth_; |
+ const intptr_t try_depth_; |
+}; |
+ |
+ |
+class TryCatchBlock { |
+ public: |
+ explicit TryCatchBlock(FlowGraphBuilder* builder, |
+ intptr_t try_handler_index = -1) |
+ : builder_(builder), |
+ outer_(builder->try_catch_block_), |
+ try_index_(try_handler_index) { |
+ if (try_index_ == -1) try_index_ = builder->AllocateTryIndex(); |
+ builder->try_catch_block_ = this; |
+ } |
+ ~TryCatchBlock() { builder_->try_catch_block_ = outer_; } |
+ |
+ intptr_t TryIndex() { return try_index_; } |
+ |
+ private: |
+ FlowGraphBuilder* builder_; |
+ TryCatchBlock* outer_; |
+ intptr_t try_index_; |
+}; |
+ |
+ |
+class CatchBlock { |
+ public: |
+ CatchBlock(FlowGraphBuilder* builder, LocalVariable* exception_var, |
+ LocalVariable* stack_trace_var, intptr_t catch_try_index) |
+ : builder_(builder), |
+ outer_(builder->catch_block_), |
+ exception_var_(exception_var), |
+ stack_trace_var_(stack_trace_var), |
+ catch_try_index_(catch_try_index) { |
+ builder_->catch_block_ = this; |
+ } |
+ ~CatchBlock() { builder_->catch_block_ = outer_; } |
+ |
+ LocalVariable* exception_var() { return exception_var_; } |
+ LocalVariable* stack_trace_var() { return stack_trace_var_; } |
+ intptr_t catch_try_index() { return catch_try_index_; } |
+ |
+ private: |
+ FlowGraphBuilder* builder_; |
+ CatchBlock* outer_; |
+ LocalVariable* exception_var_; |
+ LocalVariable* stack_trace_var_; |
+ intptr_t catch_try_index_; |
+}; |
+ |
+ |
+Fragment& Fragment::operator+=(const Fragment& other) { |
+ if (entry == NULL) { |
+ entry = other.entry; |
+ current = other.current; |
+ } else if (current != NULL && other.entry != NULL) { |
+ current->LinkTo(other.entry); |
+ current = other.current; |
+ } |
+ return *this; |
+} |
+ |
+ |
+Fragment& Fragment::operator<<=(Instruction* next) { |
+ if (entry == NULL) { |
+ entry = current = next; |
+ } else if (current != NULL) { |
+ current->LinkTo(next); |
+ current = next; |
+ } |
+ return *this; |
+} |
+ |
+ |
+Fragment Fragment::closed() { |
+ ASSERT(entry != NULL); |
+ return Fragment(entry, NULL); |
+} |
+ |
+ |
+Fragment operator+(const Fragment& first, const Fragment& second) { |
+ Fragment result = first; |
+ result += second; |
+ return result; |
+} |
+ |
+ |
+Fragment operator<<(const Fragment& fragment, Instruction* next) { |
+ Fragment result = fragment; |
+ result <<= next; |
+ return result; |
+} |
+ |
+ |
+RawInstance* TranslationHelper::Canonicalize(const Instance& instance) { |
+ if (instance.IsNull()) return instance.raw(); |
+ |
+ const char* error_str = NULL; |
+ RawInstance* result = instance.CheckAndCanonicalize(thread(), &error_str); |
+ if (result == Object::null()) { |
+ ReportError("Invalid const object %s", error_str); |
+ } |
+ return result; |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartString(const char* content, |
+ Heap::Space space) { |
+ return dart::String::ZoneHandle(Z, dart::String::New(content, space)); |
+} |
+ |
+ |
+dart::String& TranslationHelper::DartString(String* content, |
+ Heap::Space space) { |
+ return dart::String::ZoneHandle( |
+ Z, dart::String::FromUTF8(content->buffer(), content->size(), space)); |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartSymbol(const char* content) const { |
+ return dart::String::ZoneHandle(Z, Symbols::New(thread_, content)); |
+} |
+ |
+ |
+dart::String& TranslationHelper::DartSymbol(String* content) const { |
+ return dart::String::ZoneHandle( |
+ Z, dart::Symbols::FromUTF8(thread_, content->buffer(), content->size())); |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartClassName( |
+ kernel::Class* kernel_klass) { |
+ if (kernel_klass->name() != NULL) { |
+ ASSERT(kernel_klass->IsNormalClass()); |
+ dart::String& name = DartString(kernel_klass->name()); |
+ return ManglePrivateName(kernel_klass->parent(), &name); |
+ } else { |
+ // Mixin class names are not mangled. |
+ ASSERT(kernel_klass->IsMixinClass()); |
+ |
+ // We construct the string from right to left: |
+ // "Base&Mixin1&Mixin2&...&MixinN" |
+ dart::String& partial = dart::String::Handle(Z, dart::String::New("")); |
+ dart::String& amp = dart::String::Handle(Z, dart::String::New("&")); |
+ dart::String& tmp = dart::String::Handle(Z); |
+ while (kernel_klass->name() == NULL) { |
+ ASSERT(kernel_klass->IsMixinClass()); |
+ |
+ MixinClass* kernel_mixin_class = MixinClass::Cast(kernel_klass); |
+ InterfaceType* base_type = kernel_mixin_class->first(); |
+ InterfaceType* mixin_type = kernel_mixin_class->second(); |
+ |
+ String* mixin_name = NormalClass::Cast(mixin_type->klass())->name(); |
+ |
+ tmp = dart::String::FromUTF8(mixin_name->buffer(), mixin_name->size()); |
+ |
+ partial = dart::String::Concat(amp, partial); |
+ partial = dart::String::Concat(tmp, partial); |
+ |
+ kernel_klass = base_type->klass(); |
+ } |
+ |
+ tmp = dart::String::FromUTF8(kernel_klass->name()->buffer(), |
+ kernel_klass->name()->size()); |
+ |
+ partial = dart::String::Concat(amp, partial); |
+ partial = dart::String::Concat(tmp, partial); |
+ |
+ partial = dart::Symbols::New(thread_, partial); |
+ return partial; |
+ } |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartConstructorName(Constructor* node) { |
+ Class* klass = Class::Cast(node->parent()); |
+ return DartFactoryName(klass, node->name()); |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartProcedureName(Procedure* procedure) { |
+ if (procedure->kind() == Procedure::kSetter) { |
+ return DartSetterName(procedure->name()); |
+ } else if (procedure->kind() == Procedure::kGetter) { |
+ return DartGetterName(procedure->name()); |
+ } else if (procedure->kind() == Procedure::kFactory) { |
+ return DartFactoryName(Class::Cast(procedure->parent()), procedure->name()); |
+ } else { |
+ return DartMethodName(procedure->name()); |
+ } |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartSetterName(Name* kernel_name) { |
+ // The names flowing into [content] are coming from the Kernel file: |
+ // * user-defined setters: `fieldname=` |
+ // * property-set expressions: `fieldname` |
+ // |
+ // The VM uses `get:fieldname` and `set:fieldname`. |
+ // |
+ // => In order to be consistent, we remove the `=` always and adopt the VM |
+ // conventions. |
+ String* content = kernel_name->string(); |
+ ASSERT(content->size() > 0); |
+ intptr_t skip = 0; |
+ if (content->buffer()[content->size() - 1] == '=') { |
+ skip = 1; |
+ } |
+ dart::String& name = dart::String::ZoneHandle( |
+ Z, dart::String::FromUTF8(content->buffer(), content->size() - skip)); |
+ ManglePrivateName(kernel_name->library(), &name, false); |
+ name = dart::Field::SetterSymbol(name); |
+ return name; |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartGetterName(Name* kernel_name) { |
+ dart::String& name = DartString(kernel_name->string()); |
+ ManglePrivateName(kernel_name->library(), &name, false); |
+ name = dart::Field::GetterSymbol(name); |
+ return name; |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartFieldName(Name* kernel_name) { |
+ dart::String& name = DartString(kernel_name->string()); |
+ return ManglePrivateName(kernel_name->library(), &name); |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartInitializerName(Name* kernel_name) { |
+ // The [DartFieldName] will take care of mangling the name. |
+ dart::String& name = |
+ dart::String::Handle(Z, DartFieldName(kernel_name).raw()); |
+ name = Symbols::FromConcat(thread_, Symbols::InitPrefix(), name); |
+ return name; |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartMethodName(Name* kernel_name) { |
+ dart::String& name = DartString(kernel_name->string()); |
+ return ManglePrivateName(kernel_name->library(), &name); |
+} |
+ |
+ |
+const dart::String& TranslationHelper::DartFactoryName(Class* klass, |
+ Name* method_name) { |
+ // [DartMethodName] will mangle the name. |
+ dart::String& name = |
+ dart::String::Handle(Z, DartMethodName(method_name).raw()); |
+ |
+ // We build a String which looks like <classname>.<constructor-name>. |
+ // [DartClassName] will mangle the name. |
+ dart::String& temp = dart::String::Handle(Z, DartClassName(klass).raw()); |
+ temp = dart::String::Concat(temp, Symbols::Dot()); |
+ temp = dart::String::Concat(temp, name); |
+ return dart::String::ZoneHandle(Z, dart::Symbols::New(thread_, temp)); |
+} |
+ |
+ |
+dart::RawLibrary* TranslationHelper::LookupLibraryByKernelLibrary( |
+ Library* kernel_library) { |
+ const dart::String& library_name = DartSymbol(kernel_library->import_uri()); |
+ ASSERT(!library_name.IsNull()); |
+ dart::RawLibrary* library = |
+ dart::Library::LookupLibrary(thread_, library_name); |
+ ASSERT(library != Object::null()); |
+ return library; |
+} |
+ |
+ |
+dart::RawClass* TranslationHelper::LookupClassByKernelClass( |
+ Class* kernel_klass) { |
+ dart::RawClass* klass = NULL; |
+ |
+ const dart::String& class_name = DartClassName(kernel_klass); |
+ Library* kernel_library = Library::Cast(kernel_klass->parent()); |
+ dart::Library& library = |
+ dart::Library::Handle(Z, LookupLibraryByKernelLibrary(kernel_library)); |
+ klass = library.LookupClassAllowPrivate(class_name); |
+ |
+ ASSERT(klass != Object::null()); |
+ return klass; |
+} |
+ |
+ |
+dart::RawField* TranslationHelper::LookupFieldByKernelField( |
+ Field* kernel_field) { |
+ TreeNode* node = kernel_field->parent(); |
+ |
+ dart::Class& klass = dart::Class::Handle(Z); |
+ if (node->IsClass()) { |
+ klass = LookupClassByKernelClass(Class::Cast(node)); |
+ } else { |
+ ASSERT(node->IsLibrary()); |
+ dart::Library& library = dart::Library::Handle( |
+ Z, LookupLibraryByKernelLibrary(Library::Cast(node))); |
+ klass = library.toplevel_class(); |
+ } |
+ dart::RawField* field = |
+ klass.LookupFieldAllowPrivate(DartSymbol(kernel_field->name()->string())); |
+ ASSERT(field != Object::null()); |
+ return field; |
+} |
+ |
+ |
+dart::RawFunction* TranslationHelper::LookupStaticMethodByKernelProcedure( |
+ Procedure* procedure) { |
+ ASSERT(procedure->IsStatic()); |
+ const dart::String& procedure_name = DartProcedureName(procedure); |
+ |
+ // The parent is either a library or a class (in which case the procedure is a |
+ // static method). |
+ TreeNode* parent = procedure->parent(); |
+ if (parent->IsClass()) { |
+ dart::Class& klass = |
+ dart::Class::Handle(Z, LookupClassByKernelClass(Class::Cast(parent))); |
+ dart::RawFunction* raw_function = |
+ klass.LookupFunctionAllowPrivate(procedure_name); |
+ ASSERT(raw_function != Object::null()); |
+ |
+ // TODO(27590): We can probably get rid of this after no longer using |
+ // core libraries from the source. |
+ dart::Function& function = dart::Function::ZoneHandle(Z, raw_function); |
+ if (function.IsRedirectingFactory()) { |
+ ClassFinalizer::ResolveRedirectingFactory(klass, function); |
+ function = function.RedirectionTarget(); |
+ } |
+ return function.raw(); |
+ } else { |
+ ASSERT(parent->IsLibrary()); |
+ dart::Library& library = dart::Library::Handle( |
+ Z, LookupLibraryByKernelLibrary(Library::Cast(parent))); |
+ dart::RawFunction* function = |
+ library.LookupFunctionAllowPrivate(procedure_name); |
+ ASSERT(function != Object::null()); |
+ return function; |
+ } |
+} |
+ |
+ |
+dart::RawFunction* TranslationHelper::LookupConstructorByKernelConstructor( |
+ Constructor* constructor) { |
+ Class* kernel_klass = Class::Cast(constructor->parent()); |
+ dart::Class& klass = |
+ dart::Class::Handle(Z, LookupClassByKernelClass(kernel_klass)); |
+ return LookupConstructorByKernelConstructor(klass, constructor); |
+} |
+ |
+ |
+dart::RawFunction* TranslationHelper::LookupConstructorByKernelConstructor( |
+ const dart::Class& owner, Constructor* constructor) { |
+ dart::RawFunction* function = |
+ owner.LookupConstructorAllowPrivate(DartConstructorName(constructor)); |
+ ASSERT(function != Object::null()); |
+ return function; |
+} |
+ |
+ |
+dart::Type& TranslationHelper::GetCanonicalType(const dart::Class& klass) { |
+ ASSERT(!klass.IsNull()); |
+ // Note that if cls is _Closure, the returned type will be _Closure, |
+ // and not the signature type. |
+ Type& type = Type::ZoneHandle(Z, klass.CanonicalType()); |
+ if (!type.IsNull()) { |
+ return type; |
+ } |
+ type = Type::New(klass, TypeArguments::Handle(Z, klass.type_parameters()), |
+ klass.token_pos()); |
+ if (klass.is_type_finalized()) { |
+ type ^= ClassFinalizer::FinalizeType( |
+ klass, type, ClassFinalizer::kCanonicalizeWellFormed); |
+ // Note that the receiver type may now be a malbounded type. |
+ klass.SetCanonicalType(type); |
+ } |
+ return type; |
+} |
+ |
+ |
+void TranslationHelper::ReportError(const char* format, ...) { |
+ const Script& null_script = Script::Handle(Z); |
+ |
+ va_list args; |
+ va_start(args, format); |
+ Report::MessageV(Report::kError, null_script, TokenPosition::kNoSource, |
+ Report::AtLocation, format, args); |
+ va_end(args); |
+ UNREACHABLE(); |
+} |
+ |
+ |
+void TranslationHelper::ReportError(const Error& prev_error, const char* format, |
+ ...) { |
+ const Script& null_script = Script::Handle(Z); |
+ |
+ va_list args; |
+ va_start(args, format); |
+ Report::LongJumpV(prev_error, null_script, TokenPosition::kNoSource, format, |
+ args); |
+ va_end(args); |
+ UNREACHABLE(); |
+} |
+ |
+ |
+dart::String& TranslationHelper::ManglePrivateName(Library* kernel_library, |
+ dart::String* name_to_modify, |
+ bool symbolize) { |
+ if (name_to_modify->Length() >= 1 && name_to_modify->CharAt(0) == '_') { |
+ const dart::Library& library = |
+ dart::Library::Handle(Z, LookupLibraryByKernelLibrary(kernel_library)); |
+ *name_to_modify = library.PrivateName(*name_to_modify); |
+ } else if (symbolize) { |
+ *name_to_modify = Symbols::New(thread_, *name_to_modify); |
+ } |
+ return *name_to_modify; |
+} |
+ |
+ |
+const Array& TranslationHelper::ArgumentNames(List<NamedExpression>* named) { |
+ if (named->length() == 0) return Array::ZoneHandle(Z); |
+ |
+ const Array& names = Array::ZoneHandle(Z, Array::New(named->length())); |
+ for (intptr_t i = 0; i < named->length(); ++i) { |
+ names.SetAt(i, DartSymbol((*named)[i]->name())); |
+ } |
+ return names; |
+} |
+ |
+ |
+Instance& ConstantEvaluator::EvaluateExpression(Expression* expression) { |
+ expression->AcceptExpressionVisitor(this); |
+ // We return a new `ZoneHandle` here on purpose: The intermediate language |
+ // instructions do not make a copy of the handle, so we do it. |
+ return dart::Instance::ZoneHandle(Z, result_.raw()); |
+} |
+ |
+ |
+Object& ConstantEvaluator::EvaluateExpressionSafe(Expression* expression) { |
+ LongJumpScope jump; |
+ if (setjmp(*jump.Set()) == 0) { |
+ return EvaluateExpression(expression); |
+ } else { |
+ Thread* thread = Thread::Current(); |
+ Error& error = Error::Handle(Z); |
+ error = thread->sticky_error(); |
+ thread->clear_sticky_error(); |
+ return error; |
+ } |
+} |
+ |
+ |
+Instance& ConstantEvaluator::EvaluateConstructorInvocation( |
+ ConstructorInvocation* node) { |
+ VisitConstructorInvocation(node); |
+ // We return a new `ZoneHandle` here on purpose: The intermediate language |
+ // instructions do not make a copy of the handle, so we do it. |
+ return dart::Instance::ZoneHandle(Z, result_.raw()); |
+} |
+ |
+ |
+Instance& ConstantEvaluator::EvaluateListLiteral(ListLiteral* node) { |
+ VisitListLiteral(node); |
+ // We return a new `ZoneHandle` here on purpose: The intermediate language |
+ // instructions do not make a copy of the handle, so we do it. |
+ return dart::Instance::ZoneHandle(Z, result_.raw()); |
+} |
+ |
+ |
+Instance& ConstantEvaluator::EvaluateMapLiteral(MapLiteral* node) { |
+ VisitMapLiteral(node); |
+ // We return a new `ZoneHandle` here on purpose: The intermediate language |
+ // instructions do not make a copy of the handle, so we do it. |
+ return dart::Instance::ZoneHandle(Z, result_.raw()); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitBigintLiteral(BigintLiteral* node) { |
+ const dart::String& value = H.DartString(node->value()); |
+ result_ = Integer::New(value, Heap::kOld); |
+ result_ = H.Canonicalize(result_); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitBoolLiteral(BoolLiteral* node) { |
+ result_ = dart::Bool::Get(node->value()).raw(); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitDoubleLiteral(DoubleLiteral* node) { |
+ result_ = dart::Double::New(H.DartString(node->value()), Heap::kOld); |
+ result_ = H.Canonicalize(result_); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitIntLiteral(IntLiteral* node) { |
+ result_ = dart::Integer::New(node->value(), Heap::kOld); |
+ result_ = H.Canonicalize(result_); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitNullLiteral(NullLiteral* node) { |
+ result_ = dart::Instance::null(); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitStringLiteral(StringLiteral* node) { |
+ result_ = H.DartSymbol(node->value()).raw(); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitTypeLiteral(TypeLiteral* node) { |
+ const AbstractType& type = T.TranslateType(node->type()); |
+ if (type.IsMalformed()) { |
+ H.ReportError("Malformed type literal in constant expression."); |
+ } |
+ result_ = type.raw(); |
+} |
+ |
+ |
+RawObject* ConstantEvaluator::EvaluateConstConstructorCall( |
+ const dart::Class& type_class, const TypeArguments& type_arguments, |
+ const Function& constructor, const Object& argument) { |
+ // Factories have one extra argument: the type arguments. |
+ // Constructors have 1 extra arguments: receiver. |
+ const int kNumArgs = 1; |
+ const int kNumExtraArgs = 1; |
+ const int num_arguments = kNumArgs + kNumExtraArgs; |
+ const Array& arg_values = |
+ Array::Handle(Z, Array::New(num_arguments, Heap::kOld)); |
+ Instance& instance = Instance::Handle(Z); |
+ if (!constructor.IsFactory()) { |
+ instance = Instance::New(type_class, Heap::kOld); |
+ if (!type_arguments.IsNull()) { |
+ ASSERT(type_arguments.IsInstantiated()); |
+ instance.SetTypeArguments( |
+ TypeArguments::Handle(Z, type_arguments.Canonicalize())); |
+ } |
+ arg_values.SetAt(0, instance); |
+ } else { |
+ // Prepend type_arguments to list of arguments to factory. |
+ ASSERT(type_arguments.IsZoneHandle()); |
+ arg_values.SetAt(0, type_arguments); |
+ } |
+ arg_values.SetAt((0 + kNumExtraArgs), argument); |
+ const Array& args_descriptor = Array::Handle( |
+ Z, ArgumentsDescriptor::New(num_arguments, Object::empty_array())); |
+ const Object& result = Object::Handle( |
+ Z, DartEntry::InvokeFunction(constructor, arg_values, args_descriptor)); |
+ ASSERT(!result.IsError()); |
+ if (constructor.IsFactory()) { |
+ // The factory method returns the allocated object. |
+ instance ^= result.raw(); |
+ } |
+ return H.Canonicalize(instance); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitSymbolLiteral(SymbolLiteral* node) { |
+ const dart::String& symbol_value = H.DartSymbol(node->value()); |
+ |
+ const dart::Class& symbol_class = |
+ dart::Class::ZoneHandle(Z, I->object_store()->symbol_class()); |
+ ASSERT(!symbol_class.IsNull()); |
+ const dart::Function& symbol_constructor = Function::ZoneHandle( |
+ Z, symbol_class.LookupConstructor(Symbols::SymbolCtor())); |
+ ASSERT(!symbol_constructor.IsNull()); |
+ result_ ^= EvaluateConstConstructorCall( |
+ symbol_class, TypeArguments::Handle(Z), symbol_constructor, symbol_value); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitListLiteral(ListLiteral* node) { |
+ DartType* types[] = {node->type()}; |
+ const TypeArguments& type_arguments = T.TranslateTypeArguments(types, 1); |
+ |
+ intptr_t length = node->expressions().length(); |
+ const Array& const_list = |
+ Array::ZoneHandle(Z, Array::New(length, Heap::kOld)); |
+ const_list.SetTypeArguments(type_arguments); |
+ for (intptr_t i = 0; i < length; i++) { |
+ const Instance& expression = EvaluateExpression(node->expressions()[i]); |
+ const_list.SetAt(i, expression); |
+ } |
+ const_list.MakeImmutable(); |
+ result_ = H.Canonicalize(const_list); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitMapLiteral(MapLiteral* node) { |
+ DartType* types[] = {node->key_type(), node->value_type()}; |
+ const TypeArguments& type_arguments = T.TranslateTypeArguments(types, 2); |
+ |
+ intptr_t length = node->entries().length(); |
+ |
+ Array& const_kv_array = |
+ Array::ZoneHandle(Z, Array::New(2 * length, Heap::kOld)); |
+ for (intptr_t i = 0; i < length; i++) { |
+ const_kv_array.SetAt(2 * i + 0, |
+ EvaluateExpression(node->entries()[i]->key())); |
+ const_kv_array.SetAt(2 * i + 1, |
+ EvaluateExpression(node->entries()[i]->value())); |
+ } |
+ |
+ const_kv_array.MakeImmutable(); |
+ const_kv_array ^= H.Canonicalize(const_kv_array); |
+ |
+ const dart::Class& map_class = dart::Class::Handle( |
+ Z, dart::Library::LookupCoreClass(Symbols::ImmutableMap())); |
+ ASSERT(!map_class.IsNull()); |
+ ASSERT(map_class.NumTypeArguments() == 2); |
+ |
+ const dart::Field& field = dart::Field::Handle( |
+ Z, map_class.LookupInstanceFieldAllowPrivate(H.DartSymbol("_kvPairs"))); |
+ ASSERT(!field.IsNull()); |
+ |
+ // NOTE: This needs to be kept in sync with `runtime/lib/immutable_map.dart`! |
+ result_ = Instance::New(map_class, Heap::kOld); |
+ ASSERT(!result_.IsNull()); |
+ result_.SetTypeArguments(type_arguments); |
+ result_.SetField(field, const_kv_array); |
+ result_ = H.Canonicalize(result_); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitConstructorInvocation( |
+ ConstructorInvocation* node) { |
+ Arguments* kernel_arguments = node->arguments(); |
+ |
+ const Function& constructor = Function::Handle( |
+ Z, H.LookupConstructorByKernelConstructor(node->target())); |
+ dart::Class& klass = dart::Class::Handle(Z, constructor.Owner()); |
+ |
+ // Build the type arguments vector (if necessary). |
+ const TypeArguments* type_arguments = |
+ TranslateTypeArguments(constructor, &klass, kernel_arguments); |
+ |
+ // Prepare either the instance or the type argument vector for the constructor |
+ // call. |
+ Instance* receiver = NULL; |
+ const TypeArguments* type_arguments_argument = NULL; |
+ if (!constructor.IsFactory()) { |
+ receiver = &Instance::ZoneHandle(Z, Instance::New(klass, Heap::kOld)); |
+ if (type_arguments != NULL) { |
+ receiver->SetTypeArguments(*type_arguments); |
+ } |
+ } else { |
+ type_arguments_argument = type_arguments; |
+ } |
+ |
+ const Object& result = RunFunction(constructor, kernel_arguments, receiver, |
+ type_arguments_argument); |
+ if (constructor.IsFactory()) { |
+ // Factories return the new object. |
+ result_ ^= result.raw(); |
+ result_ = H.Canonicalize(result_); |
+ } else { |
+ ASSERT(!receiver->IsNull()); |
+ result_ = H.Canonicalize(*receiver); |
+ } |
+} |
+ |
+ |
+void ConstantEvaluator::VisitMethodInvocation(MethodInvocation* node) { |
+ Arguments* kernel_arguments = node->arguments(); |
+ |
+ // Dart does not support generic methods yet. |
+ ASSERT(kernel_arguments->types().length() == 0); |
+ |
+ const dart::Instance& receiver = EvaluateExpression(node->receiver()); |
+ dart::Class& klass = dart::Class::Handle( |
+ Z, isolate_->class_table()->At(receiver.GetClassId())); |
+ ASSERT(!klass.IsNull()); |
+ |
+ // Search the superclass chain for the selector. |
+ // TODO(27590): Can we assume this will never be a no-such-method error? |
+ dart::Function& function = dart::Function::Handle(Z); |
+ const dart::String& method_name = H.DartMethodName(node->name()); |
+ while (!klass.IsNull()) { |
+ function = klass.LookupDynamicFunctionAllowPrivate(method_name); |
+ if (!function.IsNull()) break; |
+ klass = klass.SuperClass(); |
+ } |
+ ASSERT(!function.IsNull()); |
+ |
+ // Run the method and canonicalize the result. |
+ const Object& result = RunFunction(function, kernel_arguments, &receiver); |
+ result_ ^= result.raw(); |
+ result_ = H.Canonicalize(result_); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitStaticGet(StaticGet* node) { |
+ Member* member = node->target(); |
+ if (member->IsField()) { |
+ Field* kernel_field = Field::Cast(member); |
+ const dart::Field& field = |
+ dart::Field::Handle(Z, H.LookupFieldByKernelField(kernel_field)); |
+ if (field.StaticValue() == Object::sentinel().raw() || |
+ field.StaticValue() == Object::transition_sentinel().raw()) { |
+ field.EvaluateInitializer(); |
+ result_ = field.StaticValue(); |
+ result_ = H.Canonicalize(result_); |
+ field.SetStaticValue(result_, true); |
+ } else { |
+ result_ = field.StaticValue(); |
+ } |
+ } else if (member->IsProcedure()) { |
+ Procedure* procedure = Procedure::Cast(member); |
+ const Function& target = Function::ZoneHandle( |
+ Z, H.LookupStaticMethodByKernelProcedure(procedure)); |
+ |
+ if (procedure->kind() == Procedure::kMethod) { |
+ ASSERT(procedure->IsStatic()); |
+ Function& closure_function = |
+ Function::ZoneHandle(Z, target.ImplicitClosureFunction()); |
+ closure_function.set_kernel_function(target.kernel_function()); |
+ result_ = closure_function.ImplicitStaticClosure(); |
+ result_ = H.Canonicalize(result_); |
+ } else if (procedure->kind() == Procedure::kGetter) { |
+ UNIMPLEMENTED(); |
+ } else { |
+ UNIMPLEMENTED(); |
+ } |
+ } |
+} |
+ |
+ |
+void ConstantEvaluator::VisitVariableGet(VariableGet* node) { |
+ // When we see a [VariableGet] the corresponding [VariableDeclaration] must've |
+ // been executed already. It therefore must have a constant object associated |
+ // with it. |
+ LocalVariable* variable = builder_->LookupVariable(node->variable()); |
+ ASSERT(variable->IsConst()); |
+ result_ = variable->ConstValue()->raw(); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitLet(Let* node) { |
+ VariableDeclaration* variable = node->variable(); |
+ LocalVariable* local = builder_->LookupVariable(variable); |
+ local->SetConstValue(EvaluateExpression(variable->initializer())); |
+ node->body()->AcceptExpressionVisitor(this); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitStaticInvocation(StaticInvocation* node) { |
+ const Function& function = Function::ZoneHandle( |
+ Z, H.LookupStaticMethodByKernelProcedure(node->procedure())); |
+ dart::Class& klass = dart::Class::Handle(Z, function.Owner()); |
+ |
+ // Build the type arguments vector (if necessary). |
+ const TypeArguments* type_arguments = |
+ TranslateTypeArguments(function, &klass, node->arguments()); |
+ |
+ const Object& result = |
+ RunFunction(function, node->arguments(), NULL, type_arguments); |
+ result_ ^= result.raw(); |
+ result_ = H.Canonicalize(result_); |
+} |
+ |
+ |
+void ConstantEvaluator::VisitStringConcatenation(StringConcatenation* node) { |
+ intptr_t length = node->expressions().length(); |
+ |
+ bool all_string = true; |
+ const Array& strings = Array::Handle(Z, Array::New(length)); |
+ for (intptr_t i = 0; i < length; i++) { |
+ EvaluateExpression(node->expressions()[i]); |
+ strings.SetAt(i, result_); |
+ all_string = all_string && result_.IsString(); |
+ } |
+ if (all_string) { |
+ result_ = dart::String::ConcatAll(strings, Heap::kOld); |
+ result_ = H.Canonicalize(result_); |
+ } else { |
+ // Get string interpolation function. |
+ const dart::Class& cls = dart::Class::Handle( |
+ Z, dart::Library::LookupCoreClass(Symbols::StringBase())); |
+ ASSERT(!cls.IsNull()); |
+ const Function& func = Function::Handle( |
+ Z, cls.LookupStaticFunction( |
+ dart::Library::PrivateCoreLibName(Symbols::Interpolate()))); |
+ ASSERT(!func.IsNull()); |
+ |
+ // Build argument array to pass to the interpolation function. |
+ const Array& interpolate_arg = Array::Handle(Z, Array::New(1, Heap::kOld)); |
+ interpolate_arg.SetAt(0, strings); |
+ |
+ // Run and canonicalize. |
+ const Object& result = |
+ RunFunction(func, interpolate_arg, Array::null_array()); |
+ result_ = H.Canonicalize(dart::String::Cast(result)); |
+ } |
+} |
+ |
+ |
+void ConstantEvaluator::VisitConditionalExpression( |
+ ConditionalExpression* node) { |
+ EvaluateExpression(node->condition()); |
+ if (Bool::Cast(result_).value()) { |
+ EvaluateExpression(node->then()); |
+ } else { |
+ EvaluateExpression(node->otherwise()); |
+ } |
+} |
+ |
+ |
+void ConstantEvaluator::VisitLogicalExpression(LogicalExpression* node) { |
+ if (node->op() == LogicalExpression::kAnd) { |
+ EvaluateExpression(node->left()); |
+ if (Bool::Cast(result_).value()) { |
+ EvaluateExpression(node->right()); |
+ } |
+ } else { |
+ ASSERT(node->op() == LogicalExpression::kOr); |
+ EvaluateExpression(node->left()); |
+ if (!Bool::Cast(result_).value()) { |
+ EvaluateExpression(node->right()); |
+ } |
+ } |
+} |
+ |
+ |
+void ConstantEvaluator::VisitNot(Not* node) { |
+ EvaluateExpression(node->expression()); |
+ ASSERT(result_.IsBool()); |
+ result_ = |
+ Bool::Cast(result_).value() ? Bool::False().raw() : Bool::True().raw(); |
+} |
+ |
+ |
+const TypeArguments* ConstantEvaluator::TranslateTypeArguments( |
+ const Function& target, dart::Class* target_klass, |
+ Arguments* kernel_arguments) { |
+ List<DartType>& kernel_type_arguments = kernel_arguments->types(); |
+ |
+ const TypeArguments* type_arguments = NULL; |
+ if (kernel_type_arguments.length() > 0) { |
+ type_arguments = &T.TranslateInstantiatedTypeArguments( |
+ *target_klass, kernel_type_arguments.raw_array(), |
+ kernel_type_arguments.length()); |
+ |
+ if (!(type_arguments->IsNull() || type_arguments->IsInstantiated())) { |
+ H.ReportError("Type must be constant in const constructor."); |
+ } |
+ } else if (target.IsFactory() && type_arguments == NULL) { |
+ // All factories take a type arguments vector as first argument (independent |
+ // of whether the class is generic or not). |
+ type_arguments = &TypeArguments::ZoneHandle(Z, TypeArguments::null()); |
+ } |
+ return type_arguments; |
+} |
+ |
+ |
+const Object& ConstantEvaluator::RunFunction(const Function& function, |
+ Arguments* kernel_arguments, |
+ const Instance* receiver, |
+ const TypeArguments* type_args) { |
+ // We do not support generic methods yet. |
+ ASSERT((receiver == NULL) || (type_args == NULL)); |
+ intptr_t extra_arguments = |
+ (receiver != NULL ? 1 : 0) + (type_args != NULL ? 1 : 0); |
+ |
+ // Build up arguments. |
+ const Array& arguments = Array::ZoneHandle( |
+ Z, Array::New(extra_arguments + kernel_arguments->count())); |
+ const Array& names = |
+ Array::ZoneHandle(Z, Array::New(kernel_arguments->named().length())); |
+ intptr_t pos = 0; |
+ if (receiver != NULL) { |
+ arguments.SetAt(pos++, *receiver); |
+ } |
+ if (type_args != NULL) { |
+ arguments.SetAt(pos++, *type_args); |
+ } |
+ for (intptr_t i = 0; i < kernel_arguments->positional().length(); i++) { |
+ EvaluateExpression(kernel_arguments->positional()[i]); |
+ arguments.SetAt(pos++, result_); |
+ } |
+ for (intptr_t i = 0; i < kernel_arguments->named().length(); i++) { |
+ NamedExpression* named_expression = kernel_arguments->named()[i]; |
+ EvaluateExpression(named_expression->expression()); |
+ arguments.SetAt(pos++, result_); |
+ names.SetAt(i, H.DartSymbol(named_expression->name())); |
+ } |
+ return RunFunction(function, arguments, names); |
+} |
+ |
+ |
+const Object& ConstantEvaluator::RunFunction(const Function& function, |
+ const Array& arguments, |
+ const Array& names) { |
+ const Array& args_descriptor = |
+ Array::Handle(Z, ArgumentsDescriptor::New(arguments.Length(), names)); |
+ const Object& result = Object::Handle( |
+ Z, DartEntry::InvokeFunction(function, arguments, args_descriptor)); |
+ if (result.IsError()) { |
+ H.ReportError(Error::Cast(result), "error evaluating constant constructor"); |
+ } |
+ return result; |
+} |
+ |
+ |
+FlowGraphBuilder::FlowGraphBuilder( |
+ TreeNode* node, ParsedFunction* parsed_function, |
+ const ZoneGrowableArray<const ICData*>& ic_data_array, |
+ InlineExitCollector* exit_collector, intptr_t osr_id, |
+ intptr_t first_block_id) |
+ : zone_(Thread::Current()->zone()), |
+ translation_helper_(Thread::Current(), zone_, |
+ Thread::Current()->isolate()), |
+ node_(node), |
+ parsed_function_(parsed_function), |
+ osr_id_(osr_id), |
+ ic_data_array_(ic_data_array), |
+ exit_collector_(exit_collector), |
+ next_block_id_(first_block_id), |
+ next_function_id_(0), |
+ context_depth_(0), |
+ loop_depth_(0), |
+ try_depth_(0), |
+ catch_depth_(0), |
+ for_in_depth_(0), |
+ stack_(NULL), |
+ pending_argument_count_(0), |
+ graph_entry_(NULL), |
+ scopes_(NULL), |
+ breakable_block_(NULL), |
+ switch_block_(NULL), |
+ try_finally_block_(NULL), |
+ try_catch_block_(NULL), |
+ next_used_try_index_(0), |
+ catch_block_(NULL), |
+ type_translator_(&translation_helper_, &active_class_), |
+ constant_evaluator_(this, zone_, &translation_helper_, |
+ &type_translator_) {} |
+ |
+ |
+FlowGraphBuilder::~FlowGraphBuilder() {} |
+ |
+ |
+Fragment FlowGraphBuilder::TranslateFinallyFinalizers( |
+ TryFinallyBlock* outer_finally, intptr_t target_context_depth) { |
+ TryFinallyBlock* const saved_block = try_finally_block_; |
+ const intptr_t saved_depth = context_depth_; |
+ const intptr_t saved_try_depth = try_depth_; |
+ |
+ Fragment instructions; |
+ |
+ // While translating the body of a finalizer we need to set the try-finally |
+ // block which is active when translating the body. |
+ while (try_finally_block_ != outer_finally) { |
+ // Set correct try depth (in case there are nested try statements). |
+ try_depth_ = try_finally_block_->try_depth(); |
+ |
+ // Potentially restore the context to what is expected for the finally |
+ // block. |
+ instructions += AdjustContextTo(try_finally_block_->context_depth()); |
+ |
+ Statement* finalizer = try_finally_block_->finalizer(); |
+ try_finally_block_ = try_finally_block_->outer(); |
+ |
+ // This will potentially have exceptional cases as described in |
+ // [VisitTryFinally] and will handle them. |
+ instructions += TranslateStatement(finalizer); |
+ |
+ // We only need to make sure that if the finalizer ended normally, we |
+ // continue towards the next outer try-finally. |
+ if (!instructions.is_open()) break; |
+ } |
+ |
+ if (instructions.is_open() && target_context_depth != -1) { |
+ // A target context depth of -1 indicates that we the code after this |
+ // will not care about the context chain so we can leave it any way we |
+ // want after the last finalizer. That is used when returning. |
+ instructions += AdjustContextTo(target_context_depth); |
+ } |
+ |
+ try_finally_block_ = saved_block; |
+ context_depth_ = saved_depth; |
+ try_depth_ = saved_try_depth; |
+ |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::EnterScope(TreeNode* node, bool* new_context) { |
+ Fragment instructions; |
+ const intptr_t context_size = |
+ scopes_->scopes.Lookup(node)->num_context_variables(); |
+ if (context_size > 0) { |
+ instructions += PushContext(context_size); |
+ instructions += Drop(); |
+ if (new_context != NULL) { |
+ *new_context = true; |
+ } |
+ } |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::ExitScope(TreeNode* node) { |
+ Fragment instructions; |
+ const intptr_t context_size = |
+ scopes_->scopes.Lookup(node)->num_context_variables(); |
+ if (context_size > 0) { |
+ instructions += PopContext(); |
+ } |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::LoadContextAt(int depth) { |
+ intptr_t delta = context_depth_ - depth; |
+ ASSERT(delta >= 0); |
+ Fragment instructions = LoadLocal(parsed_function_->current_context_var()); |
+ while (delta-- > 0) { |
+ instructions += LoadField(Context::parent_offset()); |
+ } |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::AdjustContextTo(int depth) { |
+ ASSERT(depth <= context_depth_ && depth >= 0); |
+ Fragment instructions; |
+ if (depth < context_depth_) { |
+ instructions += LoadContextAt(depth); |
+ instructions += StoreLocal(parsed_function_->current_context_var()); |
+ instructions += Drop(); |
+ context_depth_ = depth; |
+ } |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::PushContext(int size) { |
+ ASSERT(size > 0); |
+ Fragment instructions = AllocateContext(size); |
+ LocalVariable* context = MakeTemporary(); |
+ instructions += LoadLocal(context); |
+ instructions += LoadLocal(parsed_function_->current_context_var()); |
+ instructions += StoreInstanceField(Context::parent_offset()); |
+ instructions += StoreLocal(parsed_function_->current_context_var()); |
+ ++context_depth_; |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::PopContext() { |
+ return AdjustContextTo(context_depth_ - 1); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::LoadInstantiatorTypeArguments() { |
+ // TODO(27590): We could use `active_class_->IsGeneric()`. |
+ Fragment instructions; |
+ if (scopes_->type_arguments_variable != NULL) { |
+#ifdef DEBUG |
+ Function& function = |
+ Function::Handle(Z, parsed_function_->function().raw()); |
+ while (function.IsClosureFunction()) { |
+ function = function.parent_function(); |
+ } |
+ ASSERT(function.IsFactory()); |
+#endif |
+ instructions += LoadLocal(scopes_->type_arguments_variable); |
+ } else if (scopes_->this_variable != NULL && |
+ active_class_.kernel_class != NULL && |
+ active_class_.kernel_class->type_parameters().length() > 0) { |
+ ASSERT(!parsed_function_->function().IsFactory()); |
+ intptr_t type_arguments_field_offset = |
+ active_class_.klass->type_arguments_field_offset(); |
+ ASSERT(type_arguments_field_offset != dart::Class::kNoTypeArguments); |
+ |
+ instructions += LoadLocal(scopes_->this_variable); |
+ instructions += LoadField(type_arguments_field_offset); |
+ } else { |
+ instructions += NullConstant(); |
+ } |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::InstantiateTypeArguments( |
+ const TypeArguments& type_arguments) { |
+ InstantiateTypeArgumentsInstr* instr = new (Z) InstantiateTypeArgumentsInstr( |
+ TokenPosition::kNoSource, type_arguments, *active_class_.klass, Pop()); |
+ Push(instr); |
+ return Fragment(instr); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::TranslateInstantiatedTypeArguments( |
+ const TypeArguments& type_arguments) { |
+ Fragment instructions; |
+ |
+ if (type_arguments.IsNull() || type_arguments.IsInstantiated()) { |
+ // There are no type references to type parameters so we can just take it. |
+ instructions += Constant(type_arguments); |
+ } else { |
+ // The [type_arguments] vector contains a type reference to a type |
+ // parameter we need to resolve it. |
+ const bool use_instantiator = |
+ type_arguments.IsUninstantiatedIdentity() || |
+ type_arguments.CanShareInstantiatorTypeArguments(*active_class_.klass); |
+ if (use_instantiator) { |
+ // If the instantiator type arguments are just passed on, we don't need to |
+ // resolve the type parameters. |
+ // |
+ // This is for example the case here: |
+ // class Foo<T> { |
+ // newList() => new List<T>(); |
+ // } |
+ // We just use the type argument vector from the [Foo] object and pass it |
+ // directly to the `new List<T>()` factory constructor. |
+ instructions += LoadInstantiatorTypeArguments(); |
+ } else { |
+ // Otherwise we need to resolve [TypeParameterType]s in the type |
+ // expression based on the current instantiator type argument vector. |
+ instructions += LoadInstantiatorTypeArguments(); |
+ instructions += InstantiateTypeArguments(type_arguments); |
+ } |
+ } |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::AllocateContext(int size) { |
+ AllocateContextInstr* allocate = |
+ new (Z) AllocateContextInstr(TokenPosition::kNoSource, size); |
+ Push(allocate); |
+ return Fragment(allocate); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::AllocateObject(const dart::Class& klass, |
+ intptr_t argument_count) { |
+ ArgumentArray arguments = GetArguments(argument_count); |
+ AllocateObjectInstr* allocate = |
+ new (Z) AllocateObjectInstr(TokenPosition::kNoSource, klass, arguments); |
+ Push(allocate); |
+ return Fragment(allocate); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::AllocateObject(const dart::Class& klass, |
+ const Function& closure_function) { |
+ ArgumentArray arguments = new (Z) ZoneGrowableArray<PushArgumentInstr*>(Z, 0); |
+ AllocateObjectInstr* allocate = |
+ new (Z) AllocateObjectInstr(TokenPosition::kNoSource, klass, arguments); |
+ allocate->set_closure_function(closure_function); |
+ Push(allocate); |
+ return Fragment(allocate); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::BooleanNegate() { |
+ BooleanNegateInstr* negate = new (Z) BooleanNegateInstr(Pop()); |
+ Push(negate); |
+ return Fragment(negate); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::StrictCompare(Token::Kind kind, |
+ bool number_check /* = false */) { |
+ Value* right = Pop(); |
+ Value* left = Pop(); |
+ StrictCompareInstr* compare = new (Z) StrictCompareInstr( |
+ TokenPosition::kNoSource, kind, left, right, number_check); |
+ Push(compare); |
+ return Fragment(compare); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::BranchIfTrue(TargetEntryInstr** then_entry, |
+ TargetEntryInstr** otherwise_entry, |
+ bool negate) { |
+ Fragment instructions = Constant(Bool::True()); |
+ return instructions + BranchIfEqual(then_entry, otherwise_entry, negate); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::BranchIfNull(TargetEntryInstr** then_entry, |
+ TargetEntryInstr** otherwise_entry, |
+ bool negate) { |
+ Fragment instructions = NullConstant(); |
+ return instructions + BranchIfEqual(then_entry, otherwise_entry, negate); |
+} |
+ |
+Fragment FlowGraphBuilder::BranchIfEqual(TargetEntryInstr** then_entry, |
+ TargetEntryInstr** otherwise_entry, |
+ bool negate) { |
+ Value* right_value = Pop(); |
+ Value* left_value = Pop(); |
+ StrictCompareInstr* compare = new (Z) StrictCompareInstr( |
+ TokenPosition::kNoSource, negate ? Token::kNE_STRICT : Token::kEQ_STRICT, |
+ left_value, right_value, false); |
+ BranchInstr* branch = new (Z) BranchInstr(compare); |
+ *then_entry = *branch->true_successor_address() = BuildTargetEntry(); |
+ *otherwise_entry = *branch->false_successor_address() = BuildTargetEntry(); |
+ return Fragment(branch).closed(); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::BranchIfStrictEqual( |
+ TargetEntryInstr** then_entry, TargetEntryInstr** otherwise_entry) { |
+ Value* rhs = Pop(); |
+ Value* lhs = Pop(); |
+ StrictCompareInstr* compare = new (Z) StrictCompareInstr( |
+ TokenPosition::kNoSource, Token::kEQ_STRICT, lhs, rhs, false); |
+ BranchInstr* branch = new (Z) BranchInstr(compare); |
+ *then_entry = *branch->true_successor_address() = BuildTargetEntry(); |
+ *otherwise_entry = *branch->false_successor_address() = BuildTargetEntry(); |
+ return Fragment(branch).closed(); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::CatchBlockEntry(const Array& handler_types, |
+ intptr_t handler_index) { |
+ ASSERT(CurrentException()->is_captured() == |
+ CurrentStackTrace()->is_captured()); |
+ const bool should_restore_closure_context = |
+ CurrentException()->is_captured() || |
+ CurrentCatchContext()->is_captured(); |
+ CatchBlockEntryInstr* entry = new (Z) CatchBlockEntryInstr( |
+ AllocateBlockId(), CurrentTryIndex(), graph_entry_, handler_types, |
+ handler_index, *CurrentException(), *CurrentStackTrace(), |
+ /* needs_stacktrace = */ true, Thread::Current()->GetNextDeoptId(), |
+ should_restore_closure_context); |
+ graph_entry_->AddCatchEntry(entry); |
+ Fragment instructions(entry); |
+ |
+ // :saved_try_context_var can be captured in the context of |
+ // of the closure, in this case CatchBlockEntryInstr restores |
+ // :current_context_var to point to closure context in the |
+ // same way as normal function prologue does. |
+ // Update current context depth to reflect that. |
+ const intptr_t saved_context_depth = context_depth_; |
+ ASSERT(!CurrentCatchContext()->is_captured() || |
+ CurrentCatchContext()->owner()->context_level() == 0); |
+ context_depth_ = 0; |
+ instructions += LoadLocal(CurrentCatchContext()); |
+ instructions += StoreLocal(parsed_function_->current_context_var()); |
+ instructions += Drop(); |
+ context_depth_ = saved_context_depth; |
+ |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::TryCatch(int try_handler_index) { |
+ // The body of the try needs to have it's own block in order to get a new try |
+ // index. |
+ // |
+ // => We therefore create a block for the body (fresh try index) and another |
+ // join block (with current try index). |
+ Fragment body; |
+ JoinEntryInstr* entry = |
+ new (Z) JoinEntryInstr(AllocateBlockId(), try_handler_index); |
+ body += LoadLocal(parsed_function_->current_context_var()); |
+ body += StoreLocal(CurrentCatchContext()); |
+ body += Drop(); |
+ body += Goto(entry); |
+ return Fragment(body.entry, entry); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::CheckStackOverflowInPrologue() { |
+ if (IsInlining()) { |
+ // If we are inlining don't actually attach the stack check. We must still |
+ // create the stack check in order to allocate a deopt id. |
+ CheckStackOverflow(); |
+ return Fragment(); |
+ } |
+ return CheckStackOverflow(); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::CheckStackOverflow() { |
+ return Fragment( |
+ new (Z) CheckStackOverflowInstr(TokenPosition::kNoSource, loop_depth_)); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::CloneContext() { |
+ LocalVariable* context_variable = parsed_function_->current_context_var(); |
+ |
+ Fragment instructions = LoadLocal(context_variable); |
+ |
+ CloneContextInstr* clone_instruction = |
+ new (Z) CloneContextInstr(TokenPosition::kNoSource, Pop()); |
+ instructions <<= clone_instruction; |
+ Push(clone_instruction); |
+ |
+ instructions += StoreLocal(context_variable); |
+ instructions += Drop(); |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::Constant(const Object& value) { |
+ ASSERT(value.IsNotTemporaryScopedHandle()); |
+ ConstantInstr* constant = new (Z) ConstantInstr(value); |
+ Push(constant); |
+ return Fragment(constant); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::CreateArray() { |
+ Value* element_count = Pop(); |
+ CreateArrayInstr* array = new (Z) CreateArrayInstr(TokenPosition::kNoSource, |
+ Pop(), // Element type. |
+ element_count); |
+ Push(array); |
+ return Fragment(array); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::Goto(JoinEntryInstr* destination) { |
+ return Fragment(new (Z) GotoInstr(destination)).closed(); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::IntConstant(int64_t value) { |
+ return Fragment( |
+ Constant(Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld)))); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::InstanceCall(const dart::String& name, |
+ Token::Kind kind, |
+ intptr_t argument_count, |
+ intptr_t num_args_checked) { |
+ return InstanceCall(name, kind, argument_count, Array::null_array(), |
+ num_args_checked); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::InstanceCall(const dart::String& name, |
+ Token::Kind kind, |
+ intptr_t argument_count, |
+ const Array& argument_names, |
+ intptr_t num_args_checked) { |
+ ArgumentArray arguments = GetArguments(argument_count); |
+ InstanceCallInstr* call = new (Z) |
+ InstanceCallInstr(TokenPosition::kNoSource, name, kind, arguments, |
+ argument_names, num_args_checked, ic_data_array_); |
+ Push(call); |
+ return Fragment(call); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::ClosureCall(int argument_count, |
+ const Array& argument_names) { |
+ Value* function = Pop(); |
+ ArgumentArray arguments = GetArguments(argument_count); |
+ ClosureCallInstr* call = new (Z) ClosureCallInstr( |
+ function, arguments, argument_names, TokenPosition::kNoSource); |
+ Push(call); |
+ return Fragment(call); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::ThrowException() { |
+ Fragment instructions; |
+ instructions += Drop(); |
+ instructions += |
+ Fragment(new (Z) ThrowInstr(TokenPosition::kNoSource)).closed(); |
+ // Use it's side effect of leaving a constant on the stack (does not change |
+ // the graph). |
+ NullConstant(); |
+ |
+ pending_argument_count_ -= 1; |
+ |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::RethrowException(int catch_try_index) { |
+ Fragment instructions; |
+ instructions += Drop(); |
+ instructions += Drop(); |
+ instructions += |
+ Fragment(new (Z) ReThrowInstr(TokenPosition::kNoSource, catch_try_index)) |
+ .closed(); |
+ // Use it's side effect of leaving a constant on the stack (does not change |
+ // the graph). |
+ NullConstant(); |
+ |
+ pending_argument_count_ -= 2; |
+ |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::LoadClassId() { |
+ LoadClassIdInstr* load = new (Z) LoadClassIdInstr(Pop()); |
+ Push(load); |
+ return Fragment(load); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::LoadField(const dart::Field& field) { |
+ LoadFieldInstr* load = new (Z) |
+ LoadFieldInstr(Pop(), &field, AbstractType::ZoneHandle(Z, field.type()), |
+ TokenPosition::kNoSource); |
+ Push(load); |
+ return Fragment(load); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::LoadField(intptr_t offset, intptr_t class_id) { |
+ LoadFieldInstr* load = new (Z) LoadFieldInstr( |
+ Pop(), offset, AbstractType::ZoneHandle(Z), TokenPosition::kNoSource); |
+ load->set_result_cid(class_id); |
+ Push(load); |
+ return Fragment(load); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::LoadNativeField(MethodRecognizer::Kind kind, |
+ intptr_t offset, const Type& type, |
+ intptr_t class_id, |
+ bool is_immutable) { |
+ LoadFieldInstr* load = |
+ new (Z) LoadFieldInstr(Pop(), offset, type, TokenPosition::kNoSource); |
+ load->set_recognized_kind(kind); |
+ load->set_result_cid(class_id); |
+ load->set_is_immutable(is_immutable); |
+ Push(load); |
+ return Fragment(load); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::LoadLocal(LocalVariable* variable) { |
+ Fragment instructions; |
+ if (variable->is_captured()) { |
+ instructions += LoadContextAt(variable->owner()->context_level()); |
+ instructions += LoadField(Context::variable_offset(variable->index())); |
+ } else { |
+ LoadLocalInstr* load = |
+ new (Z) LoadLocalInstr(*variable, TokenPosition::kNoSource); |
+ instructions <<= load; |
+ Push(load); |
+ } |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::InitStaticField(const dart::Field& field) { |
+ InitStaticFieldInstr* init = new (Z) InitStaticFieldInstr(Pop(), field); |
+ return Fragment(init); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::LoadStaticField() { |
+ LoadStaticFieldInstr* load = |
+ new (Z) LoadStaticFieldInstr(Pop(), TokenPosition::kNoSource); |
+ Push(load); |
+ return Fragment(load); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::NullConstant() { |
+ return Constant(Instance::ZoneHandle(Z, Instance::null())); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::NativeCall(const dart::String* name, |
+ const Function* function) { |
+ InlineBailout("kernel::FlowGraphBuilder::NativeCall"); |
+ NativeCallInstr* call = new (Z) NativeCallInstr( |
+ name, function, FLAG_link_natives_lazily, TokenPosition::kNoSource); |
+ Push(call); |
+ return Fragment(call); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::PushArgument() { |
+ PushArgumentInstr* argument = new (Z) PushArgumentInstr(Pop()); |
+ Push(argument); |
+ |
+ argument->set_temp_index(argument->temp_index() - 1); |
+ ++pending_argument_count_; |
+ |
+ return Fragment(argument); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::Return() { |
+ Value* value = Pop(); |
+ ASSERT(stack_ == NULL); |
+ ReturnInstr* return_instr = |
+ new (Z) ReturnInstr(TokenPosition::kNoSource, value); |
+ if (exit_collector_ != NULL) exit_collector_->AddExit(return_instr); |
+ return Fragment(return_instr).closed(); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::StaticCall(const Function& target, |
+ intptr_t argument_count) { |
+ return StaticCall(target, argument_count, Array::null_array()); |
+} |
+ |
+ |
+static intptr_t GetResultCidOfListFactory(Zone* zone, |
+ const Function& function, |
+ intptr_t argument_count) { |
+ if (!function.IsFactory()) { |
+ return kDynamicCid; |
+ } |
+ |
+ const dart::Class& owner = dart::Class::Handle(zone, function.Owner()); |
+ if ((owner.library() != dart::Library::CoreLibrary()) && |
+ (owner.library() != dart::Library::TypedDataLibrary())) { |
+ return kDynamicCid; |
+ } |
+ |
+ if ((owner.Name() == Symbols::List().raw()) && |
+ (function.name() == Symbols::ListFactory().raw())) { |
+ ASSERT(argument_count == 1 || argument_count == 2); |
+ return (argument_count == 1) ? kGrowableObjectArrayCid : kArrayCid; |
+ } |
+ return FactoryRecognizer::ResultCid(function); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::StaticCall(const Function& target, |
+ intptr_t argument_count, |
+ const Array& argument_names) { |
+ ArgumentArray arguments = GetArguments(argument_count); |
+ StaticCallInstr* call = |
+ new (Z) StaticCallInstr(TokenPosition::kNoSource, target, argument_names, |
+ arguments, ic_data_array_); |
+ const intptr_t list_cid = |
+ GetResultCidOfListFactory(Z, target, argument_count); |
+ if (list_cid != kDynamicCid) { |
+ call->set_result_cid(list_cid); |
+ call->set_is_known_list_constructor(true); |
+ } else if (target.recognized_kind() != MethodRecognizer::kUnknown) { |
+ call->set_result_cid(MethodRecognizer::ResultCid(target)); |
+ } |
+ Push(call); |
+ return Fragment(call); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::StoreIndexed(intptr_t class_id) { |
+ Value* value = Pop(); |
+ Value* index = Pop(); |
+ // TODO(27590): Omit store barrier when possible (e.g., storing |
+ // some constants). |
+ StoreIndexedInstr* store = new (Z) StoreIndexedInstr( |
+ Pop(), // Array. |
+ index, value, kEmitStoreBarrier, Instance::ElementSizeFor(class_id), |
+ class_id, Thread::kNoDeoptId, TokenPosition::kNoSource); |
+ Push(store); |
+ return Fragment(store); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::StoreInstanceField(const dart::Field& field) { |
+ Value* value = Pop(); |
+ // TODO(27590): Omit store barrier when possible (e.g., storing |
+ // some constants). |
+ StoreInstanceFieldInstr* store = new (Z) StoreInstanceFieldInstr( |
+ field, Pop(), value, kEmitStoreBarrier, TokenPosition::kNoSource); |
+ return Fragment(store); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::StoreInstanceField(intptr_t offset) { |
+ Value* value = Pop(); |
+ StoreInstanceFieldInstr* store = new (Z) StoreInstanceFieldInstr( |
+ offset, Pop(), value, kEmitStoreBarrier, TokenPosition::kNoSource); |
+ return Fragment(store); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::StoreLocal(LocalVariable* variable) { |
+ Fragment instructions; |
+ if (variable->is_captured()) { |
+ LocalVariable* value = MakeTemporary(); |
+ instructions += LoadContextAt(variable->owner()->context_level()); |
+ instructions += LoadLocal(value); |
+ instructions += |
+ StoreInstanceField(Context::variable_offset(variable->index())); |
+ } else { |
+ StoreLocalInstr* store = |
+ new (Z) StoreLocalInstr(*variable, Pop(), TokenPosition::kNoSource); |
+ instructions <<= store; |
+ Push(store); |
+ } |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::StoreStaticField(const dart::Field& field) { |
+ return Fragment( |
+ new (Z) StoreStaticFieldInstr(field, Pop(), TokenPosition::kNoSource)); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::StringInterpolate() { |
+ Value* array = Pop(); |
+ StringInterpolateInstr* interpolate = |
+ new (Z) StringInterpolateInstr(array, TokenPosition::kNoSource); |
+ Push(interpolate); |
+ return Fragment(interpolate); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::ThrowTypeError() { |
+ const dart::Class& klass = dart::Class::ZoneHandle( |
+ Z, dart::Library::LookupCoreClass(Symbols::TypeError())); |
+ ASSERT(!klass.IsNull()); |
+ const dart::Function& constructor = dart::Function::ZoneHandle( |
+ Z, |
+ klass.LookupConstructorAllowPrivate(H.DartSymbol("_TypeError._create"))); |
+ ASSERT(!constructor.IsNull()); |
+ |
+ const dart::String& url = H.DartString( |
+ parsed_function_->function().ToLibNamePrefixedQualifiedCString(), |
+ Heap::kOld); |
+ |
+ Fragment instructions; |
+ |
+ // Create instance of _FallThroughError |
+ instructions += AllocateObject(klass, 0); |
+ LocalVariable* instance = MakeTemporary(); |
+ |
+ // Call _AssertionError._create constructor. |
+ instructions += LoadLocal(instance); |
+ instructions += PushArgument(); // this |
+ |
+ instructions += Constant(url); |
+ instructions += PushArgument(); // url |
+ |
+ instructions += NullConstant(); |
+ instructions += PushArgument(); // line |
+ |
+ instructions += IntConstant(0); |
+ instructions += PushArgument(); // column |
+ |
+ instructions += Constant(H.DartSymbol("Malformed type.")); |
+ instructions += PushArgument(); // message |
+ |
+ instructions += StaticCall(constructor, 5); |
+ instructions += Drop(); |
+ |
+ // Throw the exception |
+ instructions += PushArgument(); |
+ instructions += ThrowException(); |
+ |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::ThrowNoSuchMethodError() { |
+ const dart::Class& klass = dart::Class::ZoneHandle( |
+ Z, dart::Library::LookupCoreClass(Symbols::NoSuchMethodError())); |
+ ASSERT(!klass.IsNull()); |
+ const dart::Function& throw_function = dart::Function::ZoneHandle( |
+ Z, klass.LookupStaticFunctionAllowPrivate(Symbols::ThrowNew())); |
+ ASSERT(!throw_function.IsNull()); |
+ |
+ Fragment instructions; |
+ |
+ // Call NoSuchMethodError._throwNew static function. |
+ instructions += NullConstant(); |
+ instructions += PushArgument(); // receiver |
+ |
+ instructions += Constant(H.DartString("<unknown>", Heap::kOld)); |
+ instructions += PushArgument(); // memberName |
+ |
+ instructions += IntConstant(-1); |
+ instructions += PushArgument(); // invocation_type |
+ |
+ instructions += NullConstant(); |
+ instructions += PushArgument(); // arguments |
+ |
+ instructions += NullConstant(); |
+ instructions += PushArgument(); // argumentNames |
+ |
+ instructions += NullConstant(); |
+ instructions += PushArgument(); // existingArgumentNames |
+ |
+ instructions += StaticCall(throw_function, 6); |
+ // Leave "result" on the stack since callers expect it to be there (even |
+ // though the function will result in an exception). |
+ |
+ return instructions; |
+} |
+ |
+ |
+dart::RawFunction* FlowGraphBuilder::LookupMethodByMember( |
+ Member* target, const dart::String& method_name) { |
+ Class* kernel_klass = Class::Cast(target->parent()); |
+ dart::Class& klass = |
+ dart::Class::Handle(Z, H.LookupClassByKernelClass(kernel_klass)); |
+ |
+ dart::RawFunction* function = klass.LookupFunctionAllowPrivate(method_name); |
+ ASSERT(function != Object::null()); |
+ return function; |
+} |
+ |
+ |
+LocalVariable* FlowGraphBuilder::MakeTemporary() { |
+ char name[64]; |
+ intptr_t index = stack_->definition()->temp_index(); |
+ OS::SNPrint(name, 64, ":temp%" Pd, index); |
+ LocalVariable* variable = new (Z) LocalVariable( |
+ TokenPosition::kNoSource, H.DartSymbol(name), Object::dynamic_type()); |
+ // Set the index relative to the base of the expression stack including |
+ // outgoing arguments. |
+ variable->set_index(parsed_function_->first_stack_local_index() - |
+ parsed_function_->num_stack_locals() - |
+ pending_argument_count_ - index); |
+ |
+ // The value has uses as if it were a local variable. Mark the definition |
+ // as used so that its temp index will not be cleared (causing it to never |
+ // be materialized in the expression stack). |
+ stack_->definition()->set_ssa_temp_index(0); |
+ |
+ return variable; |
+} |
+ |
+ |
+intptr_t FlowGraphBuilder::CurrentTryIndex() { |
+ if (try_catch_block_ == NULL) { |
+ return CatchClauseNode::kInvalidTryIndex; |
+ } else { |
+ return try_catch_block_->TryIndex(); |
+ } |
+} |
+ |
+ |
+dart::LocalVariable* FlowGraphBuilder::LookupVariable( |
+ VariableDeclaration* var) { |
+ LocalVariable* local = scopes_->locals.Lookup(var); |
+ ASSERT(local != NULL); |
+ return local; |
+} |
+ |
+ |
+void FlowGraphBuilder::SetTempIndex(Definition* definition) { |
+ definition->set_temp_index( |
+ stack_ == NULL ? 0 : stack_->definition()->temp_index() + 1); |
+} |
+ |
+ |
+void FlowGraphBuilder::Push(Definition* definition) { |
+ SetTempIndex(definition); |
+ Value::AddToList(new (Z) Value(definition), &stack_); |
+} |
+ |
+ |
+Value* FlowGraphBuilder::Pop() { |
+ ASSERT(stack_ != NULL); |
+ Value* value = stack_; |
+ stack_ = value->next_use(); |
+ if (stack_ != NULL) stack_->set_previous_use(NULL); |
+ |
+ value->set_next_use(NULL); |
+ value->set_previous_use(NULL); |
+ value->definition()->ClearSSATempIndex(); |
+ return value; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::Drop() { |
+ ASSERT(stack_ != NULL); |
+ Fragment instructions; |
+ Definition* definition = stack_->definition(); |
+ // The SSA renaming implementation doesn't like [LoadLocal]s without a |
+ // tempindex. |
+ if (definition->HasSSATemp() || definition->IsLoadLocal()) { |
+ instructions <<= new (Z) DropTempsInstr(1, NULL); |
+ } else { |
+ definition->ClearTempIndex(); |
+ } |
+ |
+ Pop(); |
+ return instructions; |
+} |
+ |
+ |
+// TODO(27590): This method should be shared with |
+// runtime/vm/object.cc:RecognizeArithmeticOp. |
+Token::Kind FlowGraphBuilder::MethodKind(const dart::String& name) { |
+ ASSERT(name.IsSymbol()); |
+ if (name.raw() == Symbols::Plus().raw()) { |
+ return Token::kADD; |
+ } else if (name.raw() == Symbols::Minus().raw()) { |
+ return Token::kSUB; |
+ } else if (name.raw() == Symbols::Star().raw()) { |
+ return Token::kMUL; |
+ } else if (name.raw() == Symbols::Slash().raw()) { |
+ return Token::kDIV; |
+ } else if (name.raw() == Symbols::TruncDivOperator().raw()) { |
+ return Token::kTRUNCDIV; |
+ } else if (name.raw() == Symbols::Percent().raw()) { |
+ return Token::kMOD; |
+ } else if (name.raw() == Symbols::BitOr().raw()) { |
+ return Token::kBIT_OR; |
+ } else if (name.raw() == Symbols::Ampersand().raw()) { |
+ return Token::kBIT_AND; |
+ } else if (name.raw() == Symbols::Caret().raw()) { |
+ return Token::kBIT_XOR; |
+ } else if (name.raw() == Symbols::LeftShiftOperator().raw()) { |
+ return Token::kSHL; |
+ } else if (name.raw() == Symbols::RightShiftOperator().raw()) { |
+ return Token::kSHR; |
+ } else if (name.raw() == Symbols::Tilde().raw()) { |
+ return Token::kBIT_NOT; |
+ } else if (name.raw() == Symbols::UnaryMinus().raw()) { |
+ return Token::kNEGATE; |
+ } else if (name.raw() == Symbols::EqualOperator().raw()) { |
+ return Token::kEQ; |
+ } else if (name.raw() == Symbols::Token(Token::kNE).raw()) { |
+ return Token::kNE; |
+ } else if (name.raw() == Symbols::LAngleBracket().raw()) { |
+ return Token::kLT; |
+ } else if (name.raw() == Symbols::RAngleBracket().raw()) { |
+ return Token::kGT; |
+ } else if (name.raw() == Symbols::LessEqualOperator().raw()) { |
+ return Token::kLTE; |
+ } else if (name.raw() == Symbols::GreaterEqualOperator().raw()) { |
+ return Token::kGTE; |
+ } else if (dart::Field::IsGetterName(name)) { |
+ return Token::kGET; |
+ } else if (dart::Field::IsSetterName(name)) { |
+ return Token::kSET; |
+ } |
+ return Token::kILLEGAL; |
+} |
+ |
+ |
+void FlowGraphBuilder::InlineBailout(const char* reason) { |
+ bool is_inlining = exit_collector_ != NULL; |
+ if (is_inlining) { |
+ parsed_function_->function().set_is_inlinable(false); |
+ parsed_function_->Bailout("kernel::FlowGraphBuilder", reason); |
+ } |
+} |
+ |
+ |
+FlowGraph* FlowGraphBuilder::BuildGraph() { |
+ const dart::Function& function = parsed_function_->function(); |
+ |
+ if (function.IsConstructorClosureFunction()) return NULL; |
+ |
+ dart::Class& klass = |
+ dart::Class::Handle(zone_, parsed_function_->function().Owner()); |
+ |
+ // Find out if there is an enclosing kernel class (which will be used to |
+ // resolve type parameters). |
+ Class* kernel_klass = NULL; |
+ dart::Function& topmost = dart::Function::Handle(Z, function.raw()); |
+ while (topmost.parent_function() != Object::null()) { |
+ topmost = topmost.parent_function(); |
+ } |
+ TreeNode* topmost_node = static_cast<TreeNode*>(topmost.kernel_function()); |
+ if (topmost_node != NULL) { |
+ // Going up the closure->parent chain needs to result in a Procedure or |
+ // Constructor. |
+ TreeNode* parent = NULL; |
+ if (topmost_node->IsProcedure()) { |
+ parent = Procedure::Cast(topmost_node)->parent(); |
+ } else if (topmost_node->IsConstructor()) { |
+ parent = Constructor::Cast(topmost_node)->parent(); |
+ } else if (topmost_node->IsField()) { |
+ parent = Field::Cast(topmost_node)->parent(); |
+ } |
+ if (parent != NULL && parent->IsClass()) kernel_klass = Class::Cast(parent); |
+ } |
+ |
+ // Mark that we are using [klass]/[kernell_klass] as active class. Resolving |
+ // of type parameters will get resolved via [kernell_klass] unless we are |
+ // nested inside a static factory in which case we will use [member]. |
+ ActiveClassScope active_class_scope(&active_class_, kernel_klass, &klass); |
+ Member* member = topmost_node != NULL && topmost_node->IsMember() |
+ ? Member::Cast(topmost_node) |
+ : NULL; |
+ ActiveMemberScope active_member(&active_class_, member); |
+ |
+ // The IR builder will create its own local variables and scopes, and it |
+ // will not need an AST. The code generator will assume that there is a |
+ // local variable stack slot allocated for the current context and (I |
+ // think) that the runtime will expect it to be at a fixed offset which |
+ // requires allocating an unused expression temporary variable. |
+ scopes_ = parsed_function_->EnsureKernelScopes(); |
+ |
+ switch (function.kind()) { |
+ case RawFunction::kClosureFunction: |
+ case RawFunction::kRegularFunction: |
+ case RawFunction::kGetterFunction: |
+ case RawFunction::kSetterFunction: { |
+ FunctionNode* kernel_function = node_->IsProcedure() |
+ ? Procedure::Cast(node_)->function() |
+ : FunctionNode::Cast(node_); |
+ ActiveFunctionScope active_function_scope(&active_class_, |
+ kernel_function); |
+ return function.IsImplicitClosureFunction() |
+ ? BuildGraphOfImplicitClosureFunction(kernel_function, |
+ function) |
+ : BuildGraphOfFunction(kernel_function); |
+ } |
+ case RawFunction::kConstructor: { |
+ bool is_factory = function.IsFactory(); |
+ if (is_factory) { |
+ Procedure* procedure = Procedure::Cast(node_); |
+ FunctionNode* function = procedure->function(); |
+ ActiveFunctionScope active_function_scope(&active_class_, function); |
+ return BuildGraphOfFunction(function, NULL); |
+ } else { |
+ Constructor* constructor = Constructor::Cast(node_); |
+ FunctionNode* function = constructor->function(); |
+ ActiveFunctionScope active_function_scope(&active_class_, function); |
+ return BuildGraphOfFunction(function, constructor); |
+ } |
+ } |
+ case RawFunction::kImplicitGetter: |
+ case RawFunction::kImplicitStaticFinalGetter: |
+ case RawFunction::kImplicitSetter: { |
+ Field* field = Field::Cast(node_); |
+ return IsStaticInitializer(function, Z) |
+ ? BuildGraphOfStaticFieldInitializer(field) |
+ : BuildGraphOfFieldAccessor(field, scopes_->setter_value); |
+ } |
+ case RawFunction::kMethodExtractor: |
+ return BuildGraphOfMethodExtractor(function); |
+ case RawFunction::kNoSuchMethodDispatcher: |
+ return BuildGraphOfNoSuchMethodDispatcher(function); |
+ case RawFunction::kInvokeFieldDispatcher: |
+ return BuildGraphOfInvokeFieldDispatcher(function); |
+ case RawFunction::kSignatureFunction: |
+ case RawFunction::kIrregexpFunction: |
+ break; |
+ } |
+ UNREACHABLE(); |
+ return NULL; |
+} |
+ |
+ |
+FlowGraph* FlowGraphBuilder::BuildGraphOfFunction(FunctionNode* function, |
+ Constructor* constructor) { |
+ const Function& dart_function = parsed_function_->function(); |
+ TargetEntryInstr* normal_entry = BuildTargetEntry(); |
+ graph_entry_ = new (Z) |
+ GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId); |
+ |
+ SetupDefaultParameterValues(function); |
+ |
+ Fragment body; |
+ if (!dart_function.is_native()) body += CheckStackOverflowInPrologue(); |
+ intptr_t context_size = |
+ parsed_function_->node_sequence()->scope()->num_context_variables(); |
+ if (context_size > 0) { |
+ body += PushContext(context_size); |
+ LocalVariable* context = MakeTemporary(); |
+ |
+ // Copy captured parameters from the stack into the context. |
+ LocalScope* scope = parsed_function_->node_sequence()->scope(); |
+ intptr_t parameter_count = dart_function.NumParameters(); |
+ intptr_t parameter_index = parsed_function_->first_parameter_index(); |
+ for (intptr_t i = 0; i < parameter_count; ++i, --parameter_index) { |
+ LocalVariable* variable = scope->VariableAt(i); |
+ if (variable->is_captured()) { |
+ // There is no LocalVariable describing the on-stack parameter so |
+ // create one directly. |
+ LocalVariable* parameter = |
+ new (Z) LocalVariable(TokenPosition::kNoSource, |
+ Symbols::TempParam(), Object::dynamic_type()); |
+ parameter->set_index(parameter_index); |
+ // Mark the stack variable so it will be ignored by the code for |
+ // try/catch. |
+ parameter->set_is_captured_parameter(true); |
+ |
+ // Copy the parameter from the stack to the context. Overwrite it |
+ // with a null constant on the stack so the original value is |
+ // eligible for garbage collection. |
+ body += LoadLocal(context); |
+ body += LoadLocal(parameter); |
+ body += StoreInstanceField(Context::variable_offset(variable->index())); |
+ body += NullConstant(); |
+ body += StoreLocal(parameter); |
+ body += Drop(); |
+ } |
+ } |
+ body += Drop(); // The context. |
+ } |
+ if (constructor != NULL) { |
+ // TODO(27590): Currently the [VariableDeclaration]s from the |
+ // initializers will be visible inside the entire body of the constructor. |
+ // We should make a separate scope for them. |
+ Class* kernel_klass = Class::Cast(constructor->parent()); |
+ body += TranslateInitializers(kernel_klass, &constructor->initializers()); |
+ } |
+ |
+ // The specification defines the result of `a == b` to be: |
+ // |
+ // a) if either side is `null` then the result is `identical(a, b)`. |
+ // b) else the result is `a.operator==(b)` |
+ // |
+ // For user-defined implementations of `operator==` we need therefore |
+ // implement the handling of a). |
+ // |
+ // The default `operator==` implementation in `Object` is implemented in terms |
+ // of identical (which we assume here!) which means that case a) is actually |
+ // included in b). So we just use the normal implementation in the body. |
+ if ((dart_function.NumParameters() == 2) && |
+ (dart_function.name() == Symbols::EqualOperator().raw()) && |
+ (dart_function.Owner() != I->object_store()->object_class())) { |
+ LocalVariable* parameter = |
+ LookupVariable(function->positional_parameters()[0]); |
+ |
+ TargetEntryInstr* null_entry; |
+ TargetEntryInstr* non_null_entry; |
+ |
+ body += LoadLocal(parameter); |
+ body += BranchIfNull(&null_entry, &non_null_entry); |
+ |
+ // The argument was `null` and the receiver is not the null class (we only |
+ // go into this branch for user-defined == operators) so we can return |
+ // false. |
+ Fragment null_fragment(null_entry); |
+ null_fragment += Constant(Bool::False()); |
+ null_fragment += Return(); |
+ |
+ body = Fragment(body.entry, non_null_entry); |
+ } |
+ |
+ if (dart_function.is_native()) { |
+ body += NativeFunctionBody(function, dart_function); |
+ } else if (function->body() != NULL) { |
+ body += TranslateStatement(function->body()); |
+ } |
+ if (body.is_open()) { |
+ body += NullConstant(); |
+ body += Return(); |
+ } |
+ |
+ // If functions body contains any yield points build switch statement that |
+ // selects a continuation point based on the value of :await_jump_var. |
+ if (!yield_continuations_.is_empty()) { |
+ // The code we are building will be executed right after we enter |
+ // the function and before any nested contexts are allocated. |
+ // Reset current context_depth_ to match this. |
+ intptr_t current_context_depth = context_depth_; |
+ context_depth_ = scopes_->yield_jump_variable->owner()->context_level(); |
+ |
+ // Prepend an entry corresponding to normal entry to the function. |
+ yield_continuations_.InsertAt( |
+ 0, YieldContinuation(new (Z) DropTempsInstr(0, NULL), |
+ CatchClauseNode::kInvalidTryIndex)); |
+ yield_continuations_[0].entry->LinkTo(body.entry); |
+ |
+ // Build a switch statement. |
+ Fragment dispatch; |
+ |
+ // Load :await_jump_var into a temporary. |
+ dispatch += LoadLocal(scopes_->yield_jump_variable); |
+ dispatch += StoreLocal(scopes_->switch_variable); |
+ dispatch += Drop(); |
+ |
+ BlockEntryInstr* block = NULL; |
+ for (intptr_t i = 0; i < yield_continuations_.length(); i++) { |
+ if (i == 1) { |
+ // This is not a normal entry but a resumption. Restore |
+ // :current_context_var from :await_ctx_var. |
+ // Note: after this point context_depth_ does not match current context |
+ // depth so we should not access any local variables anymore. |
+ dispatch += LoadLocal(scopes_->yield_context_variable); |
+ dispatch += StoreLocal(parsed_function_->current_context_var()); |
+ dispatch += Drop(); |
+ } |
+ if (i == (yield_continuations_.length() - 1)) { |
+ // We reached the last possility, no need to build more ifs. |
+ // Coninue to the last continuation. |
+ // Note: continuations start with nop DropTemps instruction |
+ // which acts like an anchor, so we need to skip it. |
+ block->set_try_index(yield_continuations_[i].try_index); |
+ dispatch <<= yield_continuations_[i].entry->next(); |
+ break; |
+ } |
+ |
+ // Build comparison: |
+ // |
+ // if (:await_ctx_var == i) { |
+ // -> yield_continuations_[i] |
+ // } else ... |
+ // |
+ TargetEntryInstr* then; |
+ TargetEntryInstr* otherwise; |
+ dispatch += LoadLocal(scopes_->switch_variable); |
+ dispatch += IntConstant(i); |
+ dispatch += BranchIfStrictEqual(&then, &otherwise); |
+ |
+ // True branch is linked to appropriate continuation point. |
+ // Note: continuations start with nop DropTemps instruction |
+ // which acts like an anchor, so we need to skip it. |
+ then->LinkTo(yield_continuations_[i].entry->next()); |
+ then->set_try_index(yield_continuations_[i].try_index); |
+ |
+ // False branch will contain the next comparison. |
+ dispatch = Fragment(dispatch.entry, otherwise); |
+ block = otherwise; |
+ } |
+ body = dispatch; |
+ |
+ context_depth_ = current_context_depth; |
+ } |
+ normal_entry->LinkTo(body.entry); |
+ |
+ return new (Z) FlowGraph(*parsed_function_, graph_entry_, next_block_id_ - 1); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::NativeFunctionBody(FunctionNode* kernel_function, |
+ const Function& function) { |
+ ASSERT(function.is_native()); |
+ // We explicitly build the graph for native functions in the same way that the |
+ // from-source backend does. We should find a way to have a single component |
+ // to build these graphs so that this code is not duplicated. |
+ |
+ Fragment body; |
+ MethodRecognizer::Kind kind = MethodRecognizer::RecognizeKind(function); |
+ switch (kind) { |
+ case MethodRecognizer::kObjectEquals: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadLocal( |
+ LookupVariable(kernel_function->positional_parameters()[0])); |
+ body += StrictCompare(Token::kEQ_STRICT); |
+ break; |
+ case MethodRecognizer::kStringBaseLength: |
+ case MethodRecognizer::kStringBaseIsEmpty: |
+ // Depending on FLAG_support_externalizable_strings, treat string length |
+ // loads as mutable so that the class check that precedes them will not be |
+ // hoisted. This is unsafe because string externalization can change the |
+ // class. |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadNativeField(MethodRecognizer::kStringBaseLength, |
+ dart::String::length_offset(), |
+ Type::ZoneHandle(Z, Type::SmiType()), kSmiCid, |
+ !FLAG_support_externalizable_strings); |
+ if (kind == MethodRecognizer::kStringBaseIsEmpty) { |
+ body += IntConstant(0); |
+ body += StrictCompare(Token::kEQ_STRICT); |
+ } |
+ break; |
+ case MethodRecognizer::kGrowableArrayLength: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadNativeField(kind, GrowableObjectArray::length_offset(), |
+ Type::ZoneHandle(Z, Type::SmiType()), kSmiCid); |
+ break; |
+ case MethodRecognizer::kObjectArrayLength: |
+ case MethodRecognizer::kImmutableArrayLength: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += |
+ LoadNativeField(kind, Array::length_offset(), |
+ Type::ZoneHandle(Z, Type::SmiType()), kSmiCid, true); |
+ break; |
+ case MethodRecognizer::kTypedDataLength: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += |
+ LoadNativeField(kind, TypedData::length_offset(), |
+ Type::ZoneHandle(Z, Type::SmiType()), kSmiCid, true); |
+ break; |
+ case MethodRecognizer::kClassIDgetID: |
+ body += LoadLocal( |
+ LookupVariable(kernel_function->positional_parameters()[0])); |
+ body += LoadClassId(); |
+ break; |
+ case MethodRecognizer::kGrowableArrayCapacity: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadField(Array::data_offset(), kArrayCid); |
+ body += LoadNativeField(MethodRecognizer::kObjectArrayLength, |
+ Array::length_offset(), |
+ Type::ZoneHandle(Z, Type::SmiType()), kSmiCid); |
+ break; |
+ case MethodRecognizer::kObjectArrayAllocate: |
+ body += LoadLocal(scopes_->type_arguments_variable); |
+ body += LoadLocal( |
+ LookupVariable(kernel_function->positional_parameters()[0])); |
+ body += CreateArray(); |
+ break; |
+ case MethodRecognizer::kBigint_getDigits: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadNativeField(kind, Bigint::digits_offset(), |
+ Object::dynamic_type(), kTypedDataUint32ArrayCid); |
+ break; |
+ case MethodRecognizer::kBigint_getUsed: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadNativeField(kind, Bigint::used_offset(), |
+ Type::ZoneHandle(Z, Type::SmiType()), kSmiCid); |
+ break; |
+ case MethodRecognizer::kLinkedHashMap_getIndex: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadNativeField(kind, LinkedHashMap::index_offset(), |
+ Object::dynamic_type(), kDynamicCid); |
+ break; |
+ case MethodRecognizer::kLinkedHashMap_setIndex: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadLocal( |
+ LookupVariable(kernel_function->positional_parameters()[0])); |
+ body += StoreInstanceField(LinkedHashMap::index_offset()); |
+ body += NullConstant(); |
+ break; |
+ case MethodRecognizer::kLinkedHashMap_getData: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadNativeField(kind, LinkedHashMap::data_offset(), |
+ Object::dynamic_type(), kArrayCid); |
+ break; |
+ case MethodRecognizer::kLinkedHashMap_setData: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadLocal( |
+ LookupVariable(kernel_function->positional_parameters()[0])); |
+ body += StoreInstanceField(LinkedHashMap::data_offset()); |
+ body += NullConstant(); |
+ break; |
+ case MethodRecognizer::kLinkedHashMap_getHashMask: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadNativeField(kind, LinkedHashMap::hash_mask_offset(), |
+ Type::ZoneHandle(Z, Type::SmiType()), kSmiCid); |
+ break; |
+ case MethodRecognizer::kLinkedHashMap_setHashMask: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadLocal( |
+ LookupVariable(kernel_function->positional_parameters()[0])); |
+ // TODO(27590): This store does not need a store barrier. |
+ body += StoreInstanceField(LinkedHashMap::hash_mask_offset()); |
+ body += NullConstant(); |
+ break; |
+ case MethodRecognizer::kLinkedHashMap_getUsedData: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadNativeField(kind, LinkedHashMap::used_data_offset(), |
+ Type::ZoneHandle(Z, Type::SmiType()), kSmiCid); |
+ break; |
+ case MethodRecognizer::kLinkedHashMap_setUsedData: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadLocal( |
+ LookupVariable(kernel_function->positional_parameters()[0])); |
+ // TODO(27590): This store does not need a store barrier. |
+ body += StoreInstanceField(LinkedHashMap::used_data_offset()); |
+ body += NullConstant(); |
+ break; |
+ case MethodRecognizer::kLinkedHashMap_getDeletedKeys: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadNativeField(kind, LinkedHashMap::deleted_keys_offset(), |
+ Type::ZoneHandle(Z, Type::SmiType()), kSmiCid); |
+ break; |
+ case MethodRecognizer::kLinkedHashMap_setDeletedKeys: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadLocal( |
+ LookupVariable(kernel_function->positional_parameters()[0])); |
+ // TODO(27590): This store does not need a store barrier. |
+ body += StoreInstanceField(LinkedHashMap::deleted_keys_offset()); |
+ body += NullConstant(); |
+ break; |
+ case MethodRecognizer::kBigint_getNeg: |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadNativeField(kind, Bigint::neg_offset(), |
+ Type::ZoneHandle(Z, Type::BoolType()), kBoolCid); |
+ break; |
+ default: { |
+ dart::String& name = dart::String::ZoneHandle(Z, function.native_name()); |
+ body += NativeCall(&name, &function); |
+ break; |
+ } |
+ } |
+ return body + Return(); |
+} |
+ |
+ |
+FlowGraph* FlowGraphBuilder::BuildGraphOfFieldAccessor( |
+ Field* kernel_field, LocalVariable* setter_value) { |
+ const dart::Function& function = parsed_function_->function(); |
+ |
+ bool is_setter = function.IsImplicitSetterFunction(); |
+ bool is_method = !function.IsStaticFunction(); |
+ dart::Field& field = |
+ dart::Field::ZoneHandle(Z, H.LookupFieldByKernelField(kernel_field)); |
+ |
+ TargetEntryInstr* normal_entry = BuildTargetEntry(); |
+ graph_entry_ = new (Z) |
+ GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId); |
+ |
+ // TODO(27590): Add support for FLAG_use_field_guards. |
+ Fragment body(normal_entry); |
+ if (is_setter) { |
+ if (is_method) { |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadLocal(setter_value); |
+ body += StoreInstanceField(field); |
+ } else { |
+ body += LoadLocal(setter_value); |
+ body += StoreStaticField(field); |
+ } |
+ body += NullConstant(); |
+ } else if (is_method) { |
+ body += LoadLocal(scopes_->this_variable); |
+ body += LoadField(field); |
+ } else if (field.is_const()) { |
+ // If the parser needs to know the value of an uninitialized constant field |
+ // it will set the value to the transition sentinel (used to detect circular |
+ // initialization) and then call the implicit getter. Thus, the getter |
+ // cannot contain the InitStaticField instruction that normal static getters |
+ // contain because it would detect spurious circular initialization when it |
+ // checks for the transition sentinel. |
+ Expression* initializer = kernel_field->initializer(); |
+ ASSERT(initializer != NULL); |
+ body += Constant(constant_evaluator_.EvaluateExpression(initializer)); |
+ } else { |
+ // The field always has an initializer because static fields without |
+ // initializers are initialized eagerly and do not have implicit getters. |
+ ASSERT(field.has_initializer()); |
+ body += Constant(field); |
+ body += InitStaticField(field); |
+ body += Constant(field); |
+ body += LoadStaticField(); |
+ } |
+ body += Return(); |
+ |
+ return new (Z) FlowGraph(*parsed_function_, graph_entry_, next_block_id_ - 1); |
+} |
+ |
+ |
+FlowGraph* FlowGraphBuilder::BuildGraphOfStaticFieldInitializer( |
+ Field* kernel_field) { |
+ ASSERT(kernel_field->IsStatic()); |
+ |
+ Expression* initializer = kernel_field->initializer(); |
+ |
+ TargetEntryInstr* normal_entry = BuildTargetEntry(); |
+ graph_entry_ = new (Z) |
+ GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId); |
+ |
+ Fragment body(normal_entry); |
+ body += CheckStackOverflowInPrologue(); |
+ if (kernel_field->IsConst()) { |
+ body += Constant(constant_evaluator_.EvaluateExpression(initializer)); |
+ } else { |
+ body += TranslateExpression(initializer); |
+ } |
+ body += Return(); |
+ |
+ return new (Z) FlowGraph(*parsed_function_, graph_entry_, next_block_id_ - 1); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::BuildImplicitClosureCreation( |
+ const Function& target) { |
+ Fragment fragment; |
+ const dart::Class& closure_class = |
+ dart::Class::ZoneHandle(Z, I->object_store()->closure_class()); |
+ fragment += AllocateObject(closure_class, target); |
+ LocalVariable* closure = MakeTemporary(); |
+ |
+ // Allocate a context that closes over `this`. |
+ fragment += AllocateContext(1); |
+ LocalVariable* context = MakeTemporary(); |
+ |
+ // Store the function and the context in the closure. |
+ fragment += LoadLocal(closure); |
+ fragment += Constant(target); |
+ fragment += StoreInstanceField(Closure::function_offset()); |
+ |
+ fragment += LoadLocal(closure); |
+ fragment += LoadLocal(context); |
+ fragment += StoreInstanceField(Closure::context_offset()); |
+ |
+ // The context is on top of the operand stack. Store `this`. The context |
+ // doesn't need a parent pointer because it doesn't close over anything |
+ // else. |
+ fragment += LoadLocal(scopes_->this_variable); |
+ fragment += StoreInstanceField(Context::variable_offset(0)); |
+ |
+ return fragment; |
+} |
+ |
+ |
+FlowGraph* FlowGraphBuilder::BuildGraphOfMethodExtractor( |
+ const Function& method) { |
+ // A method extractor is the implicit getter for a method. |
+ const Function& function = |
+ Function::ZoneHandle(Z, method.extracted_method_closure()); |
+ |
+ TargetEntryInstr* normal_entry = BuildTargetEntry(); |
+ graph_entry_ = new (Z) |
+ GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId); |
+ Fragment body(normal_entry); |
+ body += CheckStackOverflowInPrologue(); |
+ body += BuildImplicitClosureCreation(function); |
+ body += Return(); |
+ |
+ return new (Z) FlowGraph(*parsed_function_, graph_entry_, next_block_id_ - 1); |
+} |
+ |
+ |
+FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction( |
+ FunctionNode* kernel_function, const Function& function) { |
+ const Function& target = Function::ZoneHandle(Z, function.parent_function()); |
+ |
+ TargetEntryInstr* normal_entry = BuildTargetEntry(); |
+ graph_entry_ = new (Z) |
+ GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId); |
+ SetupDefaultParameterValues(kernel_function); |
+ |
+ Fragment body(normal_entry); |
+ body += CheckStackOverflowInPrologue(); |
+ |
+ // Load all the arguments. |
+ if (!target.is_static()) { |
+ // The context has a fixed shape: a single variable which is the |
+ // closed-over receiver. |
+ body += LoadLocal(parsed_function_->current_context_var()); |
+ body += LoadField(Context::variable_offset(0)); |
+ body += PushArgument(); |
+ } |
+ intptr_t positional_argument_count = |
+ kernel_function->positional_parameters().length(); |
+ for (intptr_t i = 0; i < positional_argument_count; i++) { |
+ body += |
+ LoadLocal(LookupVariable(kernel_function->positional_parameters()[i])); |
+ body += PushArgument(); |
+ } |
+ intptr_t named_argument_count = kernel_function->named_parameters().length(); |
+ Array& argument_names = Array::ZoneHandle(Z); |
+ if (named_argument_count > 0) { |
+ argument_names = Array::New(named_argument_count); |
+ for (intptr_t i = 0; i < named_argument_count; i++) { |
+ VariableDeclaration* variable = kernel_function->named_parameters()[i]; |
+ body += LoadLocal(LookupVariable(variable)); |
+ body += PushArgument(); |
+ argument_names.SetAt(i, H.DartSymbol(variable->name())); |
+ } |
+ } |
+ // Forward them to the target. |
+ intptr_t argument_count = positional_argument_count + named_argument_count; |
+ if (!target.is_static()) ++argument_count; |
+ body += StaticCall(target, argument_count, argument_names); |
+ |
+ // Return the result. |
+ body += Return(); |
+ |
+ return new (Z) FlowGraph(*parsed_function_, graph_entry_, next_block_id_ - 1); |
+} |
+ |
+ |
+FlowGraph* FlowGraphBuilder::BuildGraphOfNoSuchMethodDispatcher( |
+ const Function& function) { |
+ // This function is specialized for a receiver class, a method name, and |
+ // the arguments descriptor at a call site. |
+ |
+ TargetEntryInstr* normal_entry = BuildTargetEntry(); |
+ graph_entry_ = new (Z) |
+ GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId); |
+ |
+ // The backend will expect an array of default values for all the named |
+ // parameters, even if they are all known to be passed at the call site |
+ // because the call site matches the arguments descriptor. Use null for |
+ // the default values. |
+ const Array& descriptor_array = |
+ Array::ZoneHandle(Z, function.saved_args_desc()); |
+ ArgumentsDescriptor descriptor(descriptor_array); |
+ ZoneGrowableArray<const Instance*>* default_values = |
+ new ZoneGrowableArray<const Instance*>(Z, descriptor.NamedCount()); |
+ for (intptr_t i = 0; i < descriptor.NamedCount(); ++i) { |
+ default_values->Add(&Object::null_instance()); |
+ } |
+ parsed_function_->set_default_parameter_values(default_values); |
+ |
+ Fragment body(normal_entry); |
+ body += CheckStackOverflowInPrologue(); |
+ |
+ // The receiver is the first argument to noSuchMethod, and it is the first |
+ // argument passed to the dispatcher function. |
+ LocalScope* scope = parsed_function_->node_sequence()->scope(); |
+ body += LoadLocal(scope->VariableAt(0)); |
+ body += PushArgument(); |
+ |
+ // The second argument to noSuchMethod is an invocation mirror. Push the |
+ // arguments for allocating the invocation mirror. First, the name. |
+ body += Constant(dart::String::ZoneHandle(Z, function.name())); |
+ body += PushArgument(); |
+ |
+ // Second, the arguments descriptor. |
+ body += Constant(descriptor_array); |
+ body += PushArgument(); |
+ |
+ // Third, an array containing the original arguments. Create it and fill |
+ // it in. |
+ body += Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null())); |
+ body += IntConstant(descriptor.Count()); |
+ body += CreateArray(); |
+ LocalVariable* array = MakeTemporary(); |
+ for (intptr_t i = 0; i < descriptor.PositionalCount(); ++i) { |
+ body += LoadLocal(array); |
+ body += IntConstant(i); |
+ body += LoadLocal(scope->VariableAt(i)); |
+ body += StoreIndexed(kArrayCid); |
+ body += Drop(); |
+ } |
+ dart::String& name = dart::String::Handle(Z); |
+ for (intptr_t i = 0; i < descriptor.NamedCount(); ++i) { |
+ intptr_t parameter_index = descriptor.PositionalCount() + i; |
+ name = descriptor.NameAt(i); |
+ name = dart::Symbols::New(H.thread(), name); |
+ body += LoadLocal(array); |
+ body += IntConstant(descriptor.PositionAt(i)); |
+ body += LoadLocal(scope->VariableAt(parameter_index)); |
+ body += StoreIndexed(kArrayCid); |
+ body += Drop(); |
+ } |
+ body += PushArgument(); |
+ |
+ // Fourth, false indicating this is not a super NoSuchMethod. |
+ body += Constant(Bool::False()); |
+ body += PushArgument(); |
+ |
+ const dart::Class& mirror_class = dart::Class::Handle( |
+ Z, dart::Library::LookupCoreClass(Symbols::InvocationMirror())); |
+ ASSERT(!mirror_class.IsNull()); |
+ const Function& allocation_function = Function::ZoneHandle( |
+ Z, mirror_class.LookupStaticFunction(dart::Library::PrivateCoreLibName( |
+ Symbols::AllocateInvocationMirror()))); |
+ ASSERT(!allocation_function.IsNull()); |
+ body += StaticCall(allocation_function, 4); |
+ body += PushArgument(); // For the call to noSuchMethod. |
+ |
+ ArgumentsDescriptor two_arguments( |
+ Array::Handle(Z, ArgumentsDescriptor::New(2))); |
+ Function& no_such_method = |
+ Function::ZoneHandle(Z, Resolver::ResolveDynamicForReceiverClass( |
+ dart::Class::Handle(Z, function.Owner()), |
+ Symbols::NoSuchMethod(), two_arguments)); |
+ if (no_such_method.IsNull()) { |
+ // If noSuchMethod is not found on the receiver class, call |
+ // Object.noSuchMethod. |
+ no_such_method = Resolver::ResolveDynamicForReceiverClass( |
+ dart::Class::Handle(Z, I->object_store()->object_class()), |
+ Symbols::NoSuchMethod(), two_arguments); |
+ } |
+ body += StaticCall(no_such_method, 2); |
+ body += Return(); |
+ |
+ return new (Z) FlowGraph(*parsed_function_, graph_entry_, next_block_id_ - 1); |
+} |
+ |
+ |
+FlowGraph* FlowGraphBuilder::BuildGraphOfInvokeFieldDispatcher( |
+ const Function& function) { |
+ // Find the name of the field we should dispatch to. |
+ const dart::Class& owner = dart::Class::Handle(Z, function.Owner()); |
+ ASSERT(!owner.IsNull()); |
+ const dart::String& field_name = dart::String::Handle(Z, function.name()); |
+ const dart::String& getter_name = dart::String::ZoneHandle( |
+ Z, |
+ Symbols::New(H.thread(), dart::String::Handle( |
+ Z, dart::Field::GetterSymbol(field_name)))); |
+ |
+ // Determine if this is `class Closure { get call => this; }` |
+ const dart::Class& closure_class = |
+ dart::Class::Handle(Z, I->object_store()->closure_class()); |
+ const bool is_closure_call = (owner.raw() == closure_class.raw()) && |
+ field_name.Equals(Symbols::Call()); |
+ |
+ // Set default parameters & construct argument names array. |
+ // |
+ // The backend will expect an array of default values for all the named |
+ // parameters, even if they are all known to be passed at the call site |
+ // because the call site matches the arguments descriptor. Use null for |
+ // the default values. |
+ const Array& descriptor_array = |
+ Array::ZoneHandle(Z, function.saved_args_desc()); |
+ ArgumentsDescriptor descriptor(descriptor_array); |
+ const Array& argument_names = |
+ Array::ZoneHandle(Z, Array::New(descriptor.NamedCount(), Heap::kOld)); |
+ ZoneGrowableArray<const Instance*>* default_values = |
+ new ZoneGrowableArray<const Instance*>(Z, descriptor.NamedCount()); |
+ dart::String& string_handle = dart::String::Handle(Z); |
+ for (intptr_t i = 0; i < descriptor.NamedCount(); ++i) { |
+ default_values->Add(&Object::null_instance()); |
+ string_handle = descriptor.NameAt(i); |
+ argument_names.SetAt(i, string_handle); |
+ } |
+ parsed_function_->set_default_parameter_values(default_values); |
+ |
+ TargetEntryInstr* normal_entry = BuildTargetEntry(); |
+ graph_entry_ = new (Z) |
+ GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId); |
+ |
+ Fragment body(normal_entry); |
+ body += CheckStackOverflowInPrologue(); |
+ |
+ LocalScope* scope = parsed_function_->node_sequence()->scope(); |
+ |
+ LocalVariable* closure = NULL; |
+ if (is_closure_call) { |
+ closure = scope->VariableAt(0); |
+ |
+ // The closure itself is the first argument. |
+ body += LoadLocal(closure); |
+ } else { |
+ // Invoke the getter to get the field value. |
+ body += LoadLocal(scope->VariableAt(0)); |
+ body += PushArgument(); |
+ body += InstanceCall(getter_name, Token::kGET, 1); |
+ } |
+ |
+ body += PushArgument(); |
+ |
+ // Push all arguments onto the stack. |
+ intptr_t pos = 1; |
+ for (; pos < descriptor.Count(); pos++) { |
+ body += LoadLocal(scope->VariableAt(pos)); |
+ body += PushArgument(); |
+ } |
+ |
+ if (is_closure_call) { |
+ // Lookup the function in the closure. |
+ body += LoadLocal(closure); |
+ body += LoadField(Closure::function_offset()); |
+ |
+ body += ClosureCall(descriptor.Count(), argument_names); |
+ } else { |
+ body += InstanceCall(Symbols::Call(), Token::kILLEGAL, descriptor.Count(), |
+ argument_names); |
+ } |
+ |
+ body += Return(); |
+ |
+ return new (Z) FlowGraph(*parsed_function_, graph_entry_, next_block_id_ - 1); |
+} |
+ |
+ |
+void FlowGraphBuilder::SetupDefaultParameterValues(FunctionNode* function) { |
+ intptr_t num_optional_parameters = |
+ parsed_function_->function().NumOptionalParameters(); |
+ if (num_optional_parameters > 0) { |
+ ZoneGrowableArray<const Instance*>* default_values = |
+ new ZoneGrowableArray<const Instance*>(Z, num_optional_parameters); |
+ |
+ if (parsed_function_->function().HasOptionalNamedParameters()) { |
+ ASSERT(!parsed_function_->function().HasOptionalPositionalParameters()); |
+ for (intptr_t i = 0; i < num_optional_parameters; i++) { |
+ VariableDeclaration* variable = function->named_parameters()[i]; |
+ Instance* default_value; |
+ if (variable->initializer() != NULL) { |
+ default_value = |
+ &constant_evaluator_.EvaluateExpression(variable->initializer()); |
+ } else { |
+ default_value = &Instance::ZoneHandle(Z, Instance::null()); |
+ } |
+ default_values->Add(default_value); |
+ } |
+ } else { |
+ ASSERT(parsed_function_->function().HasOptionalPositionalParameters()); |
+ intptr_t required = function->required_parameter_count(); |
+ for (intptr_t i = 0; i < num_optional_parameters; i++) { |
+ VariableDeclaration* variable = |
+ function->positional_parameters()[required + i]; |
+ Instance* default_value; |
+ if (variable->initializer() != NULL) { |
+ default_value = |
+ &constant_evaluator_.EvaluateExpression(variable->initializer()); |
+ } else { |
+ default_value = &Instance::ZoneHandle(Z, Instance::null()); |
+ } |
+ default_values->Add(default_value); |
+ } |
+ } |
+ parsed_function_->set_default_parameter_values(default_values); |
+ } |
+} |
+ |
+ |
+TargetEntryInstr* FlowGraphBuilder::BuildTargetEntry() { |
+ return new (Z) TargetEntryInstr(AllocateBlockId(), CurrentTryIndex()); |
+} |
+ |
+ |
+JoinEntryInstr* FlowGraphBuilder::BuildJoinEntry() { |
+ return new (Z) JoinEntryInstr(AllocateBlockId(), CurrentTryIndex()); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::TranslateInitializers( |
+ Class* kernel_klass, List<Initializer>* initializers) { |
+ Fragment instructions; |
+ |
+ // These come from: |
+ // class A { |
+ // var x = (expr); |
+ // } |
+ for (intptr_t i = 0; i < kernel_klass->fields().length(); i++) { |
+ Field* kernel_field = kernel_klass->fields()[i]; |
+ Expression* init = kernel_field->initializer(); |
+ if (!kernel_field->IsStatic() && init != NULL) { |
+ dart::Field& field = |
+ dart::Field::ZoneHandle(Z, H.LookupFieldByKernelField(kernel_field)); |
+ |
+ EnterScope(kernel_field); |
+ // TODO(27590): Support FLAG_use_field_guards. |
+ instructions += LoadLocal(scopes_->this_variable); |
+ instructions += TranslateExpression(init); |
+ instructions += StoreInstanceField(field); |
+ ExitScope(kernel_field); |
+ } |
+ } |
+ |
+ // These to come from: |
+ // class A { |
+ // var x; |
+ // var y; |
+ // A(this.x) : super(expr), y = (expr); |
+ // } |
+ for (intptr_t i = 0; i < initializers->length(); i++) { |
+ Initializer* initializer = (*initializers)[i]; |
+ if (initializer->IsFieldInitializer()) { |
+ FieldInitializer* init = FieldInitializer::Cast(initializer); |
+ dart::Field& field = |
+ dart::Field::ZoneHandle(Z, H.LookupFieldByKernelField(init->field())); |
+ |
+ // TODO(27590): Support FLAG_use_field_guards. |
+ instructions += LoadLocal(scopes_->this_variable); |
+ instructions += TranslateExpression(init->value()); |
+ instructions += StoreInstanceField(field); |
+ } else if (initializer->IsSuperInitializer()) { |
+ SuperInitializer* init = SuperInitializer::Cast(initializer); |
+ |
+ instructions += LoadLocal(scopes_->this_variable); |
+ instructions += PushArgument(); |
+ |
+ ASSERT(init->arguments()->types().length() == 0); |
+ Array& argument_names = Array::ZoneHandle(Z); |
+ instructions += TranslateArguments(init->arguments(), &argument_names); |
+ |
+ const Function& target = Function::ZoneHandle( |
+ Z, H.LookupConstructorByKernelConstructor(init->target())); |
+ intptr_t argument_count = init->arguments()->count() + 1; |
+ instructions += StaticCall(target, argument_count, argument_names); |
+ instructions += Drop(); |
+ } else if (initializer->IsRedirectingInitializer()) { |
+ RedirectingInitializer* init = RedirectingInitializer::Cast(initializer); |
+ |
+ instructions += LoadLocal(scopes_->this_variable); |
+ instructions += PushArgument(); |
+ |
+ ASSERT(init->arguments()->types().length() == 0); |
+ Array& argument_names = Array::ZoneHandle(Z); |
+ instructions += TranslateArguments(init->arguments(), &argument_names); |
+ |
+ const Function& target = Function::ZoneHandle( |
+ Z, H.LookupConstructorByKernelConstructor(init->target())); |
+ intptr_t argument_count = init->arguments()->count() + 1; |
+ instructions += StaticCall(target, argument_count, argument_names); |
+ instructions += Drop(); |
+ } else if (initializer->IsLocalInitializer()) { |
+ // The other initializers following this one might read the variable. This |
+ // is used e.g. for evaluating the arguments to a super call first, run |
+ // normal field initializers next and then make the actual super call: |
+ // |
+ // The frontend converts |
+ // |
+ // class A { |
+ // var x; |
+ // A(a, b) : super(a + b), x = 2*b {} |
+ // } |
+ // |
+ // to |
+ // |
+ // class A { |
+ // var x; |
+ // A(a, b) : tmp = a + b, x = 2*b, super(tmp) {} |
+ // } |
+ // |
+ // (This is strictly speaking not what one should do in terms of the |
+ // specification but that is how it is currently implemented.) |
+ LocalInitializer* init = LocalInitializer::Cast(initializer); |
+ |
+ VariableDeclaration* declaration = init->variable(); |
+ LocalVariable* variable = LookupVariable(declaration); |
+ Expression* initializer = init->variable()->initializer(); |
+ ASSERT(initializer != NULL); |
+ ASSERT(!declaration->IsConst()); |
+ |
+ instructions += TranslateExpression(initializer); |
+ instructions += StoreLocal(variable); |
+ instructions += Drop(); |
+ |
+ fragment_ = instructions; |
+ } else { |
+ UNIMPLEMENTED(); |
+ } |
+ } |
+ return instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::TranslateStatement(Statement* statement) { |
+#ifdef DEBUG |
+ intptr_t original_context_depth = context_depth_; |
+#endif |
+ statement->AcceptStatementVisitor(this); |
+ DEBUG_ASSERT(context_depth_ == original_context_depth); |
+ return fragment_; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::TranslateCondition(Expression* expression, |
+ bool* negate) { |
+ *negate = expression->IsNot(); |
+ if (*negate) { |
+ return TranslateExpression(Not::Cast(expression)->expression()); |
+ } |
+ return TranslateExpression(expression); |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::TranslateExpression(Expression* expression) { |
+ expression->AcceptExpressionVisitor(this); |
+ return fragment_; |
+} |
+ |
+ |
+ArgumentArray FlowGraphBuilder::GetArguments(int count) { |
+ ArgumentArray arguments = |
+ new (Z) ZoneGrowableArray<PushArgumentInstr*>(Z, count); |
+ arguments->SetLength(count); |
+ for (intptr_t i = count - 1; i >= 0; --i) { |
+ ASSERT(stack_->definition()->IsPushArgument()); |
+ ASSERT(!stack_->definition()->HasSSATemp()); |
+ arguments->data()[i] = stack_->definition()->AsPushArgument(); |
+ Drop(); |
+ } |
+ pending_argument_count_ -= count; |
+ ASSERT(pending_argument_count_ >= 0); |
+ return arguments; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitInvalidExpression(InvalidExpression* node) { |
+ // TODO(27590): Once we have better error information we might need to |
+ // make some invalid expressions not NSM errors but type/compile-time/... |
+ // errors. |
+ fragment_ = ThrowNoSuchMethodError(); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitNullLiteral(NullLiteral* node) { |
+ fragment_ = Constant(Instance::ZoneHandle(Z, Instance::null())); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitBoolLiteral(BoolLiteral* node) { |
+ fragment_ = Constant(Bool::Get(node->value())); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitIntLiteral(IntLiteral* node) { |
+ fragment_ = IntConstant(node->value()); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitBigintLiteral(BigintLiteral* node) { |
+ const dart::String& value = H.DartString(node->value()); |
+ fragment_ = Constant(Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld))); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitDoubleLiteral(DoubleLiteral* node) { |
+ fragment_ = Constant(constant_evaluator_.EvaluateExpression(node)); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitStringLiteral(StringLiteral* node) { |
+ fragment_ = Constant(H.DartSymbol(node->value())); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitSymbolLiteral(SymbolLiteral* node) { |
+ fragment_ = Constant(constant_evaluator_.EvaluateExpression(node)); |
+} |
+ |
+ |
+AbstractType& DartTypeTranslator::TranslateType(DartType* node) { |
+ node->AcceptDartTypeVisitor(this); |
+ |
+ // We return a new `ZoneHandle` here on purpose: The intermediate language |
+ // instructions do not make a copy of the handle, so we do it. |
+ return dart::AbstractType::ZoneHandle(Z, result_.raw()); |
+} |
+ |
+ |
+AbstractType& DartTypeTranslator::TranslateTypeWithoutFinalization( |
+ DartType* node) { |
+ bool saved_finalize = finalize_; |
+ finalize_ = false; |
+ H.SetFinalize(false); |
+ AbstractType& result = TranslateType(node); |
+ finalize_ = saved_finalize; |
+ H.SetFinalize(saved_finalize); |
+ return result; |
+} |
+ |
+ |
+void DartTypeTranslator::VisitInvalidType(InvalidType* node) { |
+ result_ = ClassFinalizer::NewFinalizedMalformedType( |
+ Error::Handle(Z), // No previous error. |
+ dart::Script::Handle(Z, dart::Script::null()), TokenPosition::kNoSource, |
+ "[InvalidType] in Kernel IR."); |
+} |
+ |
+ |
+void DartTypeTranslator::VisitFunctionType(FunctionType* node) { |
+ // TODO(27590): Fix function types which are composed of malformed types. |
+ // We might need to convert them to dynamic types instead of making the |
+ // function type malformed. |
+ const Function& signature_function = Function::ZoneHandle( |
+ Z, Function::NewSignatureFunction(*active_class_->klass, |
+ TokenPosition::kNoSource)); |
+ |
+ node->return_type()->AcceptDartTypeVisitor(this); |
+ if (result_.IsMalformed()) return; |
+ signature_function.set_result_type(result_); |
+ |
+ const intptr_t positional_count = node->positional_parameters().length(); |
+ const intptr_t named_count = node->named_parameters().length(); |
+ const intptr_t all_count = positional_count + named_count; |
+ const intptr_t required_count = node->required_parameter_count(); |
+ |
+ // The additional first parameter is the receiver type (set to dynamic). |
+ signature_function.set_num_fixed_parameters(1 + required_count); |
+ signature_function.SetNumOptionalParameters( |
+ all_count - required_count, positional_count > required_count); |
+ |
+ const Array& parameter_types = |
+ Array::Handle(Z, Array::New(1 + all_count, Heap::kOld)); |
+ signature_function.set_parameter_types(parameter_types); |
+ const Array& parameter_names = |
+ Array::Handle(Z, Array::New(1 + all_count, Heap::kOld)); |
+ signature_function.set_parameter_names(parameter_names); |
+ |
+ intptr_t pos = 0; |
+ parameter_types.SetAt(pos, AbstractType::dynamic_type()); |
+ parameter_names.SetAt(pos, H.DartSymbol("_receiver_")); |
+ pos++; |
+ for (intptr_t i = 0; i < positional_count; i++, pos++) { |
+ node->positional_parameters()[i]->AcceptDartTypeVisitor(this); |
+ if (result_.IsMalformed()) return; |
+ parameter_types.SetAt(pos, result_); |
+ parameter_names.SetAt(pos, H.DartSymbol("noname")); |
+ } |
+ for (intptr_t i = 0; i < named_count; i++, pos++) { |
+ Tuple<String, DartType>* tuple = node->named_parameters()[i]; |
+ tuple->second()->AcceptDartTypeVisitor(this); |
+ if (result_.IsMalformed()) return; |
+ parameter_types.SetAt(pos, result_); |
+ parameter_names.SetAt(pos, H.DartSymbol(tuple->first())); |
+ } |
+ |
+ Type& signature_type = |
+ Type::ZoneHandle(Z, signature_function.SignatureType()); |
+ |
+ if (finalize_) { |
+ signature_type ^= ClassFinalizer::FinalizeType( |
+ *active_class_->klass, signature_type, ClassFinalizer::kCanonicalize); |
+ } |
+ signature_function.SetSignatureType(signature_type); |
+ |
+ result_ = signature_type.raw(); |
+} |
+ |
+ |
+void DartTypeTranslator::VisitTypeParameterType(TypeParameterType* node) { |
+ ASSERT(active_class_->kernel_class != NULL); |
+ |
+ List<TypeParameter>* parameters = |
+ &active_class_->kernel_class->type_parameters(); |
+ if ((active_class_->member != NULL) && active_class_->member->IsProcedure()) { |
+ Procedure* procedure = Procedure::Cast(active_class_->member); |
+ if ((procedure->function() != NULL) && |
+ (procedure->function()->type_parameters().length() > 0)) { |
+ // |
+ // WARNING: This is a little hackish: |
+ // |
+ // We have a static factory constructor. The kernel IR gives the factory |
+ // constructor function it's own type parameters (which are equal in name |
+ // and number to the ones of the enclosing class). |
+ // I.e., |
+ // |
+ // class A<T> { |
+ // factory A.x() { return new B<T>(); } |
+ // } |
+ // |
+ // is basically translated to this: |
+ // |
+ // class A<T> { |
+ // static A.x<T'>() { return new B<T'>(); } |
+ // } |
+ // |
+ parameters = &procedure->function()->type_parameters(); |
+ } |
+ } |
+ |
+ for (intptr_t i = 0; i < parameters->length(); i++) { |
+ TypeParameter* type_parameter = (*parameters)[i]; |
+ if (node->parameter() == type_parameter) { |
+ // The index of the type parameter in [parameters] is |
+ // the same index into the `klass->type_parameters()` array. |
+ result_ ^= dart::TypeArguments::Handle( |
+ Z, active_class_->klass->type_parameters()) |
+ .TypeAt(i); |
+ return; |
+ } |
+ } |
+ |
+ UNREACHABLE(); |
+} |
+ |
+ |
+void DartTypeTranslator::VisitInterfaceType(InterfaceType* node) { |
+ // NOTE: That an interface type like `T<A, B>` is considered to be |
+ // malformed iff `T` is malformed. |
+ // => We therefore ignore errors in `A` or `B`. |
+ const TypeArguments& type_arguments = TranslateTypeArguments( |
+ node->type_arguments().raw_array(), node->type_arguments().length()); |
+ |
+ const dart::Class& klass = |
+ dart::Class::Handle(Z, H.LookupClassByKernelClass(node->klass())); |
+ |
+ result_ = Type::New(klass, type_arguments, TokenPosition::kNoSource); |
+ result_.SetIsResolved(); |
+ if (finalize_) { |
+ result_ = ClassFinalizer::FinalizeType(klass, result_, |
+ ClassFinalizer::kCanonicalize); |
+ } |
+} |
+ |
+ |
+void DartTypeTranslator::VisitDynamicType(DynamicType* node) { |
+ result_ = Object::dynamic_type().raw(); |
+} |
+ |
+ |
+void DartTypeTranslator::VisitVoidType(VoidType* node) { |
+ result_ = Object::void_type().raw(); |
+} |
+ |
+ |
+const TypeArguments& DartTypeTranslator::TranslateTypeArguments( |
+ DartType** dart_types, intptr_t length) { |
+ bool only_dynamic = true; |
+ for (intptr_t i = 0; i < length; i++) { |
+ if (!dart_types[i]->IsDynamicType()) { |
+ only_dynamic = false; |
+ break; |
+ } |
+ } |
+ TypeArguments& type_arguments = TypeArguments::ZoneHandle(Z); |
+ if (!only_dynamic) { |
+ type_arguments = TypeArguments::New(length); |
+ for (intptr_t i = 0; i < length; i++) { |
+ dart_types[i]->AcceptDartTypeVisitor(this); |
+ if (result_.IsMalformed()) { |
+ type_arguments = TypeArguments::null(); |
+ return type_arguments; |
+ } |
+ type_arguments.SetTypeAt(i, result_); |
+ } |
+ if (finalize_) { |
+ type_arguments = type_arguments.Canonicalize(); |
+ } |
+ } |
+ return type_arguments; |
+} |
+ |
+ |
+const TypeArguments& DartTypeTranslator::TranslateInstantiatedTypeArguments( |
+ const dart::Class& receiver_class, DartType** receiver_type_arguments, |
+ intptr_t length) { |
+ const TypeArguments& type_arguments = |
+ TranslateTypeArguments(receiver_type_arguments, length); |
+ if (type_arguments.IsNull()) return type_arguments; |
+ |
+ // We make a temporary [Type] object and use `ClassFinalizer::FinalizeType` to |
+ // finalize the argument types. |
+ // (This can for example make the [type_arguments] vector larger) |
+ Type& type = Type::Handle( |
+ Z, Type::New(receiver_class, type_arguments, TokenPosition::kNoSource)); |
+ if (finalize_) { |
+ type ^= ClassFinalizer::FinalizeType( |
+ *active_class_->klass, type, ClassFinalizer::kCanonicalizeWellFormed); |
+ } |
+ |
+ const TypeArguments& instantiated_type_arguments = |
+ TypeArguments::ZoneHandle(Z, type.arguments()); |
+ return instantiated_type_arguments; |
+} |
+ |
+ |
+const Type& DartTypeTranslator::ReceiverType(const dart::Class& klass) { |
+ ASSERT(!klass.IsNull()); |
+ ASSERT(!klass.IsTypedefClass()); |
+ // Note that if klass is _Closure, the returned type will be _Closure, |
+ // and not the signature type. |
+ Type& type = Type::ZoneHandle(Z, klass.CanonicalType()); |
+ if (!type.IsNull()) { |
+ return type; |
+ } |
+ type = Type::New(klass, TypeArguments::Handle(Z, klass.type_parameters()), |
+ klass.token_pos()); |
+ return type; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitTypeLiteral(TypeLiteral* node) { |
+ const AbstractType& type = T.TranslateType(node->type()); |
+ if (type.IsMalformed()) H.ReportError("Malformed type literal"); |
+ |
+ fragment_ = Constant(type); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitVariableGet(VariableGet* node) { |
+ fragment_ = LoadLocal(LookupVariable(node->variable())); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitVariableSet(VariableSet* node) { |
+ Fragment instructions = TranslateExpression(node->expression()); |
+ // The IR should not include assignments to final or const variables. |
+ // This is https://github.com/dart-lang/rasta/issues/83. |
+ // |
+ // TODO(27590): simply ASSERT that the variable is not const or final |
+ // when that issue is fixed. |
+ fragment_ = instructions + |
+ ((node->variable()->IsFinal() || node->variable()->IsConst()) |
+ ? Drop() + ThrowNoSuchMethodError() |
+ : StoreLocal(LookupVariable(node->variable()))); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitStaticGet(StaticGet* node) { |
+ Member* target = node->target(); |
+ if (target->IsField()) { |
+ Field* kernel_field = Field::Cast(target); |
+ const dart::Field& field = |
+ dart::Field::ZoneHandle(Z, H.LookupFieldByKernelField(kernel_field)); |
+ if (kernel_field->IsConst()) { |
+ fragment_ = Constant(constant_evaluator_.EvaluateExpression(node)); |
+ } else { |
+ const dart::Class& owner = dart::Class::Handle(Z, field.Owner()); |
+ const dart::String& getter_name = H.DartGetterName(kernel_field->name()); |
+ const Function& getter = |
+ Function::ZoneHandle(Z, owner.LookupStaticFunction(getter_name)); |
+ if (getter.IsNull() || !field.has_initializer()) { |
+ Fragment instructions = Constant(field); |
+ fragment_ = instructions + LoadStaticField(); |
+ } else { |
+ // TODO(27590): figure out how to trigger this case and add tests. |
+ fragment_ = StaticCall(getter, 0); |
+ } |
+ } |
+ } else { |
+ Procedure* procedure = Procedure::Cast(target); |
+ const Function& target = Function::ZoneHandle( |
+ Z, H.LookupStaticMethodByKernelProcedure(procedure)); |
+ |
+ if (procedure->kind() == Procedure::kGetter) { |
+ fragment_ = StaticCall(target, 0); |
+ } else if (procedure->kind() == Procedure::kMethod) { |
+ ASSERT(procedure->IsStatic()); |
+ Function& closure_function = |
+ Function::ZoneHandle(Z, target.ImplicitClosureFunction()); |
+ closure_function.set_kernel_function(target.kernel_function()); |
+ const Instance& closure = |
+ Instance::ZoneHandle(Z, closure_function.ImplicitStaticClosure()); |
+ fragment_ = Constant(closure); |
+ } else { |
+ UNIMPLEMENTED(); |
+ } |
+ } |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitStaticSet(StaticSet* node) { |
+ Member* target = node->target(); |
+ if (target->IsField()) { |
+ Field* kernel_field = Field::Cast(target); |
+ const dart::Field& field = |
+ dart::Field::ZoneHandle(Z, H.LookupFieldByKernelField(kernel_field)); |
+ Fragment instructions = TranslateExpression(node->expression()); |
+ LocalVariable* variable = MakeTemporary(); |
+ instructions += LoadLocal(variable); |
+ fragment_ = instructions + StoreStaticField(field); |
+ } else { |
+ ASSERT(target->IsProcedure()); |
+ |
+ // Evaluate the expression on the right hand side. |
+ Fragment instructions = TranslateExpression(node->expression()); |
+ LocalVariable* variable = MakeTemporary(); |
+ |
+ // Prepare argument. |
+ instructions += LoadLocal(variable); |
+ instructions += PushArgument(); |
+ |
+ // Invoke the setter function. |
+ Procedure* procedure = Procedure::Cast(target); |
+ const Function& target = Function::ZoneHandle( |
+ Z, H.LookupStaticMethodByKernelProcedure(procedure)); |
+ instructions += StaticCall(target, 1); |
+ |
+ // Drop the unused result & leave the stored value on the stack. |
+ fragment_ = instructions + Drop(); |
+ } |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitPropertyGet(PropertyGet* node) { |
+ Fragment instructions = TranslateExpression(node->receiver()); |
+ instructions += PushArgument(); |
+ const dart::String& getter_name = H.DartGetterName(node->name()); |
+ fragment_ = instructions + InstanceCall(getter_name, Token::kGET, 1); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitPropertySet(PropertySet* node) { |
+ Fragment instructions(NullConstant()); |
+ LocalVariable* variable = MakeTemporary(); |
+ instructions += TranslateExpression(node->receiver()); |
+ instructions += PushArgument(); |
+ instructions += TranslateExpression(node->value()); |
+ instructions += StoreLocal(variable); |
+ instructions += PushArgument(); |
+ |
+ const dart::String& setter_name = H.DartSetterName(node->name()); |
+ instructions += InstanceCall(setter_name, Token::kSET, 2); |
+ fragment_ = instructions + Drop(); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitDirectPropertyGet(DirectPropertyGet* node) { |
+ Function& target = Function::ZoneHandle(Z); |
+ if (node->target()->IsProcedure()) { |
+ Procedure* kernel_procedure = Procedure::Cast(node->target()); |
+ Name* kernel_name = kernel_procedure->name(); |
+ if (kernel_procedure->kind() == Procedure::kGetter) { |
+ target = |
+ LookupMethodByMember(kernel_procedure, H.DartGetterName(kernel_name)); |
+ } else { |
+ target = |
+ LookupMethodByMember(kernel_procedure, H.DartMethodName(kernel_name)); |
+ target = target.ImplicitClosureFunction(); |
+ ASSERT(!target.IsNull()); |
+ fragment_ = BuildImplicitClosureCreation(target); |
+ return; |
+ } |
+ } else { |
+ ASSERT(node->target()->IsField()); |
+ const dart::String& getter_name = H.DartGetterName(node->target()->name()); |
+ target = LookupMethodByMember(node->target(), getter_name); |
+ ASSERT(target.IsGetterFunction() || target.IsImplicitGetterFunction()); |
+ } |
+ |
+ Fragment instructions = TranslateExpression(node->receiver()); |
+ instructions += PushArgument(); |
+ fragment_ = instructions + StaticCall(target, 1); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitDirectPropertySet(DirectPropertySet* node) { |
+ const dart::String& method_name = H.DartSetterName(node->target()->name()); |
+ const Function& target = Function::ZoneHandle( |
+ Z, LookupMethodByMember(node->target(), method_name)); |
+ ASSERT(target.IsSetterFunction() || target.IsImplicitSetterFunction()); |
+ |
+ Fragment instructions(NullConstant()); |
+ LocalVariable* value = MakeTemporary(); |
+ instructions += TranslateExpression(node->receiver()); |
+ instructions += PushArgument(); |
+ instructions += TranslateExpression(node->value()); |
+ instructions += StoreLocal(value); |
+ instructions += PushArgument(); |
+ instructions += StaticCall(target, 2); |
+ |
+ fragment_ = instructions + Drop(); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitStaticInvocation(StaticInvocation* node) { |
+ const Function& target = Function::ZoneHandle( |
+ Z, H.LookupStaticMethodByKernelProcedure(node->procedure())); |
+ const dart::Class& klass = dart::Class::ZoneHandle(Z, target.Owner()); |
+ intptr_t argument_count = node->arguments()->count(); |
+ if (target.IsGenerativeConstructor() || target.IsFactory()) { |
+ // The VM requires a TypeArguments object as first parameter for |
+ // every factory constructor. |
+ ++argument_count; |
+ } |
+ List<NamedExpression>& named = node->arguments()->named(); |
+ const Array& argument_names = H.ArgumentNames(&named); |
+ |
+ Fragment instructions; |
+ if (!target.AreValidArguments(argument_count, argument_names, NULL)) { |
+ // An argument mismatch for a static invocation really should not occur |
+ // in the IR. This is issue https://github.com/dart-lang/rasta/issues/76. |
+ // |
+ // TODO(27590): Change this to an ASSERT when that issue is fixed. |
+ List<Expression>& positional = node->arguments()->positional(); |
+ for (intptr_t i = 0; i < positional.length(); ++i) { |
+ instructions += TranslateExpression(positional[i]); |
+ instructions += Drop(); |
+ } |
+ |
+ for (intptr_t i = 0; i < named.length(); ++i) { |
+ instructions += TranslateExpression(named[i]->expression()); |
+ instructions += Drop(); |
+ } |
+ |
+ fragment_ = instructions + ThrowNoSuchMethodError(); |
+ return; |
+ } |
+ |
+ LocalVariable* instance_variable = NULL; |
+ |
+ // If we cross the Kernel -> VM core library boundary, a [StaticInvocation] |
+ // can appear, but the thing we're calling is not a static method, but a |
+ // factory constructor. |
+ // The `H.LookupStaticmethodByKernelProcedure` will potentially resolve to the |
+ // forwarded constructor. |
+ // In that case we'll make an instance and pass it as first argument. |
+ // |
+ // TODO(27590): Get rid of this after we're using core libraries compiled |
+ // into Kernel. |
+ if (target.IsGenerativeConstructor()) { |
+ if (klass.NumTypeArguments() > 0) { |
+ List<DartType>& kernel_type_arguments = node->arguments()->types(); |
+ const TypeArguments& type_arguments = |
+ T.TranslateInstantiatedTypeArguments( |
+ klass, kernel_type_arguments.raw_array(), |
+ kernel_type_arguments.length()); |
+ instructions += TranslateInstantiatedTypeArguments(type_arguments); |
+ instructions += PushArgument(); |
+ instructions += AllocateObject(klass, 1); |
+ } else { |
+ instructions += AllocateObject(klass, 0); |
+ } |
+ |
+ instance_variable = MakeTemporary(); |
+ |
+ instructions += LoadLocal(instance_variable); |
+ instructions += PushArgument(); |
+ } else if (target.IsFactory()) { |
+ // The VM requires currently a TypeArguments object as first parameter for |
+ // every factory constructor :-/ ! |
+ // |
+ // TODO(27590): Get rid of this after we're using core libraries compiled |
+ // into Kernel. |
+ List<DartType>& kernel_type_arguments = node->arguments()->types(); |
+ |
+ const TypeArguments& type_arguments = T.TranslateInstantiatedTypeArguments( |
+ klass, kernel_type_arguments.raw_array(), |
+ kernel_type_arguments.length()); |
+ |
+ instructions += TranslateInstantiatedTypeArguments(type_arguments); |
+ instructions += PushArgument(); |
+ } else { |
+ ASSERT(node->arguments()->types().length() == 0); |
+ } |
+ |
+ // Special case identical(x, y) call. |
+ // TODO(27590) consider moving this into the inliner and force inline it |
+ // there. |
+ if (klass.IsTopLevel() && (klass.library() == dart::Library::CoreLibrary()) && |
+ (target.name() == Symbols::Identical().raw())) { |
+ ASSERT(argument_count == 2); |
+ |
+ List<Expression>& positional = node->arguments()->positional(); |
+ for (intptr_t i = 0; i < positional.length(); ++i) { |
+ instructions += TranslateExpression(positional[i]); |
+ } |
+ instructions += StrictCompare(Token::kEQ_STRICT, /*number_check=*/true); |
+ } else { |
+ instructions += TranslateArguments(node->arguments(), NULL); |
+ instructions += StaticCall(target, argument_count, argument_names); |
+ |
+ if (target.IsGenerativeConstructor()) { |
+ // Drop the result of the constructor call and leave [instance_variable] |
+ // on top-of-stack. |
+ instructions += Drop(); |
+ } |
+ } |
+ |
+ fragment_ = instructions; |
+} |
+ |
+ |
+static bool IsNumberLiteral(Node* node) { |
+ return node->IsIntLiteral() || node->IsDoubleLiteral(); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitMethodInvocation(MethodInvocation* node) { |
+ const dart::String& name = H.DartMethodName(node->name()); |
+ const intptr_t argument_count = node->arguments()->count() + 1; |
+ const Token::Kind token_kind = MethodKind(name); |
+ if (IsNumberLiteral(node->receiver())) { |
+ if ((argument_count == 1) && (token_kind == Token::kNEGATE)) { |
+ const Object& result = constant_evaluator_.EvaluateExpressionSafe(node); |
+ if (!result.IsError()) { |
+ fragment_ = Constant(result); |
+ return; |
+ } |
+ } else if ((argument_count == 2) && |
+ Token::IsBinaryArithmeticOperator(token_kind) && |
+ IsNumberLiteral(node->arguments()->positional()[0])) { |
+ const Object& result = constant_evaluator_.EvaluateExpressionSafe(node); |
+ if (!result.IsError()) { |
+ fragment_ = Constant(result); |
+ return; |
+ } |
+ } |
+ } |
+ |
+ Fragment instructions = TranslateExpression(node->receiver()); |
+ instructions += PushArgument(); |
+ |
+ // Dart does not support generic methods yet. |
+ ASSERT(node->arguments()->types().length() == 0); |
+ |
+ Array& argument_names = Array::ZoneHandle(Z); |
+ instructions += TranslateArguments(node->arguments(), &argument_names); |
+ |
+ intptr_t num_args_checked = 1; |
+ // If we have a special operation (e.g. +/-/==) we mark both arguments as |
+ // to be checked. |
+ if (token_kind != Token::kILLEGAL) { |
+ ASSERT(argument_count <= 2); |
+ num_args_checked = argument_count; |
+ } |
+ |
+ fragment_ = instructions + InstanceCall(name, token_kind, argument_count, |
+ argument_names, num_args_checked); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitDirectMethodInvocation( |
+ DirectMethodInvocation* node) { |
+ const dart::String& method_name = H.DartMethodName(node->target()->name()); |
+ const Function& target = Function::ZoneHandle( |
+ Z, LookupMethodByMember(node->target(), method_name)); |
+ |
+ intptr_t argument_count = node->arguments()->count() + 1; |
+ Array& argument_names = Array::ZoneHandle(Z); |
+ |
+ ASSERT(node->arguments()->types().length() == 0); |
+ Fragment instructions = TranslateExpression(node->receiver()); |
+ instructions += PushArgument(); |
+ instructions += TranslateArguments(node->arguments(), &argument_names); |
+ fragment_ = instructions + StaticCall(target, argument_count, argument_names); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitConstructorInvocation(ConstructorInvocation* node) { |
+ if (node->is_const()) { |
+ fragment_ = |
+ Constant(constant_evaluator_.EvaluateConstructorInvocation(node)); |
+ return; |
+ } |
+ |
+ Class* kernel_class = Class::Cast(node->target()->parent()); |
+ |
+ dart::Class& klass = |
+ dart::Class::ZoneHandle(Z, H.LookupClassByKernelClass(kernel_class)); |
+ |
+ Fragment instructions; |
+ if (klass.NumTypeArguments() > 0) { |
+ List<DartType>& kernel_type_arguments = node->arguments()->types(); |
+ const TypeArguments& type_arguments = T.TranslateInstantiatedTypeArguments( |
+ klass, kernel_type_arguments.raw_array(), |
+ kernel_type_arguments.length()); |
+ |
+ if (type_arguments.IsNull() || type_arguments.IsInstantiated()) { |
+ instructions += TranslateInstantiatedTypeArguments(type_arguments); |
+ } else { |
+ if (!klass.IsGeneric()) { |
+ Type& type = Type::ZoneHandle(Z, T.ReceiverType(klass).raw()); |
+ |
+ // TODO(27590): Can we move this code into [ReceiverType]? |
+ type ^= ClassFinalizer::FinalizeType(*active_class_.klass, type, |
+ ClassFinalizer::kFinalize); |
+ ASSERT(!type.IsMalformedOrMalbounded()); |
+ |
+ TypeArguments& canonicalized_type_arguments = |
+ TypeArguments::ZoneHandle(Z, type.arguments()); |
+ canonicalized_type_arguments = |
+ canonicalized_type_arguments.Canonicalize(); |
+ instructions += Constant(canonicalized_type_arguments); |
+ } else { |
+ instructions += TranslateInstantiatedTypeArguments(type_arguments); |
+ } |
+ } |
+ |
+ instructions += PushArgument(); |
+ instructions += AllocateObject(klass, 1); |
+ } else { |
+ instructions += AllocateObject(klass, 0); |
+ } |
+ LocalVariable* variable = MakeTemporary(); |
+ |
+ instructions += LoadLocal(variable); |
+ instructions += PushArgument(); |
+ |
+ Array& argument_names = Array::ZoneHandle(Z); |
+ instructions += TranslateArguments(node->arguments(), &argument_names); |
+ |
+ const Function& target = Function::ZoneHandle( |
+ Z, H.LookupConstructorByKernelConstructor(klass, node->target())); |
+ intptr_t argument_count = node->arguments()->count() + 1; |
+ instructions += StaticCall(target, argument_count, argument_names); |
+ fragment_ = instructions + Drop(); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitIsExpression(IsExpression* node) { |
+ Fragment instructions = TranslateExpression(node->operand()); |
+ |
+ // The VM does not like an instanceOf call with a dynamic type. We need to |
+ // special case this situation. |
+ const Type& object_type = Type::Handle(Z, Type::ObjectType()); |
+ const AbstractType& type = T.TranslateType(node->type()); |
+ if (type.IsMalformed()) { |
+ instructions += Drop(); |
+ instructions += ThrowTypeError(); |
+ fragment_ = instructions; |
+ return; |
+ } |
+ |
+ if (type.IsInstantiated() && |
+ object_type.IsSubtypeOf(type, NULL, NULL, Heap::kOld)) { |
+ // Evaluate the expression on the left but ignore it's result. |
+ instructions += Drop(); |
+ |
+ // Let condition be always true. |
+ instructions += Constant(Bool::True()); |
+ } else { |
+ instructions += PushArgument(); |
+ |
+ if (!type.IsInstantiated()) { |
+ instructions += LoadInstantiatorTypeArguments(); |
+ } else { |
+ instructions += NullConstant(); |
+ } |
+ instructions += PushArgument(); // Type arguments. |
+ |
+ instructions += Constant(type); |
+ instructions += PushArgument(); // Type. |
+ |
+ instructions += Constant(Bool::False()); |
+ instructions += PushArgument(); // Negate?. |
+ |
+ instructions += |
+ InstanceCall(dart::Library::PrivateCoreLibName(Symbols::_instanceOf()), |
+ Token::kIS, 4); |
+ } |
+ |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitAsExpression(AsExpression* node) { |
+ Fragment instructions = TranslateExpression(node->operand()); |
+ |
+ // The VM does not like an Object_as call with a dynamic type. We need to |
+ // special case this situation. |
+ const Type& object_type = Type::Handle(Z, Type::ObjectType()); |
+ const AbstractType& type = T.TranslateType(node->type()); |
+ if (type.IsMalformed()) { |
+ instructions += Drop(); |
+ instructions += ThrowTypeError(); |
+ fragment_ = instructions; |
+ return; |
+ } |
+ |
+ if (type.IsInstantiated() && |
+ object_type.IsSubtypeOf(type, NULL, NULL, Heap::kOld)) { |
+ // We already evaluated the operand on the left and just leave it there as |
+ // the result of the `obj as dynamic` expression. |
+ } else { |
+ instructions += PushArgument(); |
+ |
+ if (!type.IsInstantiated()) { |
+ instructions += LoadInstantiatorTypeArguments(); |
+ } else { |
+ instructions += NullConstant(); |
+ } |
+ instructions += PushArgument(); // Type arguments. |
+ |
+ instructions += Constant(type); |
+ instructions += PushArgument(); // Type. |
+ |
+ instructions += InstanceCall( |
+ dart::Library::PrivateCoreLibName(Symbols::_as()), Token::kAS, 3); |
+ } |
+ |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitConditionalExpression(ConditionalExpression* node) { |
+ bool negate; |
+ Fragment instructions = TranslateCondition(node->condition(), &negate); |
+ |
+ TargetEntryInstr* then_entry; |
+ TargetEntryInstr* otherwise_entry; |
+ instructions += BranchIfTrue(&then_entry, &otherwise_entry, negate); |
+ |
+ Value* top = stack_; |
+ Fragment then_fragment(then_entry); |
+ then_fragment += TranslateExpression(node->then()); |
+ then_fragment += StoreLocal(parsed_function_->expression_temp_var()); |
+ then_fragment += Drop(); |
+ |
+ ASSERT(stack_ == top); |
+ Fragment otherwise_fragment(otherwise_entry); |
+ otherwise_fragment += TranslateExpression(node->otherwise()); |
+ otherwise_fragment += StoreLocal(parsed_function_->expression_temp_var()); |
+ otherwise_fragment += Drop(); |
+ |
+ JoinEntryInstr* join = BuildJoinEntry(); |
+ then_fragment += Goto(join); |
+ otherwise_fragment += Goto(join); |
+ |
+ fragment_ = Fragment(instructions.entry, join) + |
+ LoadLocal(parsed_function_->expression_temp_var()); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitLogicalExpression(LogicalExpression* node) { |
+ bool negate; |
+ Fragment instructions = TranslateCondition(node->left(), &negate); |
+ TargetEntryInstr* right_entry; |
+ TargetEntryInstr* constant_entry; |
+ |
+ if (node->op() == LogicalExpression::kAnd) { |
+ instructions += BranchIfTrue(&right_entry, &constant_entry, negate); |
+ } else { |
+ instructions += BranchIfTrue(&constant_entry, &right_entry, negate); |
+ } |
+ |
+ Value* top = stack_; |
+ Fragment right_fragment(right_entry); |
+ right_fragment += TranslateCondition(node->right(), &negate); |
+ right_fragment += Constant(Bool::True()); |
+ right_fragment += |
+ StrictCompare(negate ? Token::kNE_STRICT : Token::kEQ_STRICT); |
+ right_fragment += StoreLocal(parsed_function_->expression_temp_var()); |
+ right_fragment += Drop(); |
+ |
+ ASSERT(top == stack_); |
+ Fragment constant_fragment(constant_entry); |
+ constant_fragment += |
+ Constant(Bool::Get(node->op() == LogicalExpression::kOr)); |
+ constant_fragment += StoreLocal(parsed_function_->expression_temp_var()); |
+ constant_fragment += Drop(); |
+ |
+ JoinEntryInstr* join = BuildJoinEntry(); |
+ right_fragment += Goto(join); |
+ constant_fragment += Goto(join); |
+ |
+ fragment_ = Fragment(instructions.entry, join) + |
+ LoadLocal(parsed_function_->expression_temp_var()); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitNot(Not* node) { |
+ Fragment instructions = TranslateExpression(node->expression()); |
+ fragment_ = instructions + BooleanNegate(); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitThisExpression(ThisExpression* node) { |
+ fragment_ = LoadLocal(scopes_->this_variable); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitStringConcatenation(StringConcatenation* node) { |
+ List<Expression>& expressions = node->expressions(); |
+ |
+ Fragment instructions; |
+ |
+ // The type arguments for CreateArray. |
+ instructions += Constant(TypeArguments::ZoneHandle(Z)); |
+ instructions += IntConstant(expressions.length()); |
+ instructions += CreateArray(); |
+ LocalVariable* array = MakeTemporary(); |
+ |
+ for (intptr_t i = 0; i < node->expressions().length(); i++) { |
+ instructions += LoadLocal(array); |
+ instructions += IntConstant(i); |
+ instructions += TranslateExpression(node->expressions()[i]); |
+ instructions += StoreIndexed(kArrayCid); |
+ instructions += Drop(); |
+ } |
+ |
+ instructions += StringInterpolate(); |
+ |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitListLiteral(ListLiteral* node) { |
+ if (node->is_const()) { |
+ fragment_ = Constant(constant_evaluator_.EvaluateListLiteral(node)); |
+ return; |
+ } |
+ |
+ DartType* types[] = {node->type()}; |
+ const TypeArguments& type_arguments = T.TranslateTypeArguments(types, 1); |
+ |
+ // The type argument for the factory call. |
+ Fragment instructions = TranslateInstantiatedTypeArguments(type_arguments); |
+ instructions += PushArgument(); |
+ List<Expression>& expressions = node->expressions(); |
+ if (expressions.length() == 0) { |
+ instructions += Constant(Object::empty_array()); |
+ } else { |
+ // The type arguments for CreateArray. |
+ instructions += Constant(TypeArguments::ZoneHandle(Z)); |
+ instructions += IntConstant(expressions.length()); |
+ instructions += CreateArray(); |
+ |
+ LocalVariable* array = MakeTemporary(); |
+ for (intptr_t i = 0; i < expressions.length(); ++i) { |
+ instructions += LoadLocal(array); |
+ instructions += IntConstant(i); |
+ instructions += TranslateExpression(expressions[i]); |
+ instructions += StoreIndexed(kArrayCid); |
+ instructions += Drop(); |
+ } |
+ } |
+ instructions += PushArgument(); // The array. |
+ |
+ const dart::Class& factory_class = |
+ dart::Class::Handle(Z, dart::Library::LookupCoreClass(Symbols::List())); |
+ const Function& factory_method = Function::ZoneHandle( |
+ Z, factory_class.LookupFactory( |
+ dart::Library::PrivateCoreLibName(Symbols::ListLiteralFactory()))); |
+ fragment_ = instructions + StaticCall(factory_method, 2); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitMapLiteral(MapLiteral* node) { |
+ if (node->is_const()) { |
+ fragment_ = Constant(constant_evaluator_.EvaluateMapLiteral(node)); |
+ return; |
+ } |
+ |
+ const dart::Class& map_class = |
+ dart::Class::Handle(Z, dart::Library::LookupCoreClass(Symbols::Map())); |
+ const Function& factory_method = Function::ZoneHandle( |
+ Z, map_class.LookupFactory( |
+ dart::Library::PrivateCoreLibName(Symbols::MapLiteralFactory()))); |
+ |
+ DartType* types[] = {node->key_type(), node->value_type()}; |
+ const TypeArguments& type_arguments = T.TranslateTypeArguments(types, 2); |
+ |
+ // The type argument for the factory call `new Map<K, V>._fromLiteral(List)`. |
+ Fragment instructions = TranslateInstantiatedTypeArguments(type_arguments); |
+ instructions += PushArgument(); |
+ |
+ List<MapEntry>& entries = node->entries(); |
+ if (entries.length() == 0) { |
+ instructions += Constant(Object::empty_array()); |
+ } else { |
+ // The type arguments for `new List<X>(int len)`. |
+ instructions += Constant(TypeArguments::ZoneHandle(Z)); |
+ |
+ // We generate a list of tuples, i.e. [key1, value1, ..., keyN, valueN]. |
+ instructions += IntConstant(2 * entries.length()); |
+ instructions += CreateArray(); |
+ |
+ LocalVariable* array = MakeTemporary(); |
+ for (intptr_t i = 0; i < entries.length(); ++i) { |
+ instructions += LoadLocal(array); |
+ instructions += IntConstant(2 * i); |
+ instructions += TranslateExpression(entries[i]->key()); |
+ instructions += StoreIndexed(kArrayCid); |
+ instructions += Drop(); |
+ |
+ instructions += LoadLocal(array); |
+ instructions += IntConstant(2 * i + 1); |
+ instructions += TranslateExpression(entries[i]->value()); |
+ instructions += StoreIndexed(kArrayCid); |
+ instructions += Drop(); |
+ } |
+ } |
+ instructions += PushArgument(); // The array. |
+ |
+ fragment_ = instructions + StaticCall(factory_method, 2); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitFunctionExpression(FunctionExpression* node) { |
+ fragment_ = TranslateFunctionNode(node->function(), node); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitLet(Let* node) { |
+ Fragment instructions = TranslateStatement(node->variable()); |
+ instructions += TranslateExpression(node->body()); |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitThrow(Throw* node) { |
+ Fragment instructions; |
+ |
+ instructions += TranslateExpression(node->expression()); |
+ instructions += PushArgument(); |
+ instructions += ThrowException(); |
+ ASSERT(instructions.is_closed()); |
+ |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitRethrow(Rethrow* node) { |
+ Fragment instructions; |
+ |
+ instructions += LoadLocal(catch_block_->exception_var()); |
+ instructions += PushArgument(); |
+ instructions += LoadLocal(catch_block_->stack_trace_var()); |
+ instructions += PushArgument(); |
+ instructions += RethrowException(catch_block_->catch_try_index()); |
+ |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitBlockExpression(BlockExpression* node) { |
+ Fragment instructions = TranslateStatement(node->body()); |
+ instructions += TranslateExpression(node->value()); |
+ fragment_ = instructions; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::TranslateArguments(Arguments* node, |
+ Array* argument_names) { |
+ Fragment instructions; |
+ |
+ List<Expression>& positional = node->positional(); |
+ for (intptr_t i = 0; i < positional.length(); ++i) { |
+ instructions += TranslateExpression(positional[i]); |
+ instructions += PushArgument(); |
+ } |
+ |
+ List<NamedExpression>& named = node->named(); |
+ if (argument_names != NULL) { |
+ *argument_names = H.ArgumentNames(&named).raw(); |
+ } |
+ for (intptr_t i = 0; i < named.length(); ++i) { |
+ NamedExpression* named_expression = named[i]; |
+ instructions += TranslateExpression(named_expression->expression()); |
+ instructions += PushArgument(); |
+ } |
+ return instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitInvalidStatement(InvalidStatement* node) { |
+ H.ReportError("Invalid statements not implemented yet!"); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitEmptyStatement(EmptyStatement* node) { |
+ fragment_ = Fragment(); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitBlock(Block* node) { |
+ Fragment instructions; |
+ |
+ instructions += EnterScope(node); |
+ List<Statement>& statements = node->statements(); |
+ for (intptr_t i = 0; i < statements.length(); ++i) { |
+ instructions += TranslateStatement(statements[i]); |
+ } |
+ instructions += ExitScope(node); |
+ |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitReturnStatement(ReturnStatement* node) { |
+ bool inside_try_finally = try_finally_block_ != NULL; |
+ |
+ Fragment instructions = node->expression() == NULL |
+ ? NullConstant() |
+ : TranslateExpression(node->expression()); |
+ if (inside_try_finally) { |
+ ASSERT(scopes_->finally_return_variable != NULL); |
+ instructions += StoreLocal(scopes_->finally_return_variable); |
+ instructions += Drop(); |
+ instructions += TranslateFinallyFinalizers(NULL, -1); |
+ if (instructions.is_open()) { |
+ instructions += LoadLocal(scopes_->finally_return_variable); |
+ instructions += Return(); |
+ } |
+ } else { |
+ instructions += Return(); |
+ } |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitExpressionStatement(ExpressionStatement* node) { |
+ Fragment instructions = TranslateExpression(node->expression()); |
+ instructions += Drop(); |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitVariableDeclaration(VariableDeclaration* node) { |
+ LocalVariable* variable = LookupVariable(node); |
+ Expression* initializer = node->initializer(); |
+ |
+ Fragment instructions; |
+ if (initializer == NULL) { |
+ instructions += NullConstant(); |
+ } else { |
+ if (node->IsConst()) { |
+ const Instance& constant_value = |
+ constant_evaluator_.EvaluateExpression(initializer); |
+ variable->SetConstValue(constant_value); |
+ instructions += Constant(constant_value); |
+ } else { |
+ instructions += TranslateExpression(initializer); |
+ } |
+ } |
+ instructions += StoreLocal(variable); |
+ instructions += Drop(); |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* node) { |
+ Fragment instructions = TranslateFunctionNode(node->function(), node); |
+ instructions += StoreLocal(LookupVariable(node->variable())); |
+ instructions += Drop(); |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitIfStatement(IfStatement* node) { |
+ bool negate; |
+ Fragment instructions = TranslateCondition(node->condition(), &negate); |
+ TargetEntryInstr* then_entry; |
+ TargetEntryInstr* otherwise_entry; |
+ instructions += BranchIfTrue(&then_entry, &otherwise_entry, negate); |
+ |
+ Fragment then_fragment(then_entry); |
+ then_fragment += TranslateStatement(node->then()); |
+ |
+ Fragment otherwise_fragment(otherwise_entry); |
+ otherwise_fragment += TranslateStatement(node->otherwise()); |
+ |
+ if (then_fragment.is_open()) { |
+ if (otherwise_fragment.is_open()) { |
+ JoinEntryInstr* join = BuildJoinEntry(); |
+ then_fragment += Goto(join); |
+ otherwise_fragment += Goto(join); |
+ fragment_ = Fragment(instructions.entry, join); |
+ } else { |
+ fragment_ = Fragment(instructions.entry, then_fragment.current); |
+ } |
+ } else if (otherwise_fragment.is_open()) { |
+ fragment_ = Fragment(instructions.entry, otherwise_fragment.current); |
+ } else { |
+ fragment_ = instructions.closed(); |
+ } |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitWhileStatement(WhileStatement* node) { |
+ ++loop_depth_; |
+ bool negate; |
+ Fragment condition = TranslateCondition(node->condition(), &negate); |
+ TargetEntryInstr* body_entry; |
+ TargetEntryInstr* loop_exit; |
+ condition += BranchIfTrue(&body_entry, &loop_exit, negate); |
+ |
+ Fragment body(body_entry); |
+ body += TranslateStatement(node->body()); |
+ |
+ Instruction* entry; |
+ if (body.is_open()) { |
+ JoinEntryInstr* join = BuildJoinEntry(); |
+ body += Goto(join); |
+ |
+ Fragment loop(join); |
+ loop += CheckStackOverflow(); |
+ loop += condition; |
+ entry = new (Z) GotoInstr(join); |
+ } else { |
+ entry = condition.entry; |
+ } |
+ |
+ |
+ fragment_ = Fragment(entry, loop_exit); |
+ --loop_depth_; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitDoStatement(DoStatement* node) { |
+ ++loop_depth_; |
+ Fragment body = TranslateStatement(node->body()); |
+ |
+ if (body.is_closed()) { |
+ fragment_ = body; |
+ --loop_depth_; |
+ return; |
+ } |
+ |
+ bool negate; |
+ JoinEntryInstr* join = BuildJoinEntry(); |
+ Fragment loop(join); |
+ loop += CheckStackOverflow(); |
+ loop += body; |
+ loop += TranslateCondition(node->condition(), &negate); |
+ TargetEntryInstr* loop_repeat; |
+ TargetEntryInstr* loop_exit; |
+ loop += BranchIfTrue(&loop_repeat, &loop_exit, negate); |
+ |
+ Fragment repeat(loop_repeat); |
+ repeat += Goto(join); |
+ |
+ fragment_ = Fragment(new (Z) GotoInstr(join), loop_exit); |
+ --loop_depth_; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitForStatement(ForStatement* node) { |
+ Fragment declarations; |
+ |
+ bool new_context = false; |
+ declarations += EnterScope(node, &new_context); |
+ |
+ List<VariableDeclaration>& variables = node->variables(); |
+ for (intptr_t i = 0; i < variables.length(); ++i) { |
+ declarations += TranslateStatement(variables[i]); |
+ } |
+ |
+ ++loop_depth_; |
+ bool negate = false; |
+ Fragment condition = node->condition() == NULL |
+ ? Constant(Bool::True()) |
+ : TranslateCondition(node->condition(), &negate); |
+ TargetEntryInstr* body_entry; |
+ TargetEntryInstr* loop_exit; |
+ condition += BranchIfTrue(&body_entry, &loop_exit, negate); |
+ |
+ Fragment body(body_entry); |
+ body += TranslateStatement(node->body()); |
+ |
+ if (body.is_open()) { |
+ // We allocated a fresh context before the loop which contains captured |
+ // [ForStatement] variables. Before jumping back to the loop entry we clone |
+ // the context object (at same depth) which ensures the next iteration of |
+ // the body gets a fresh set of [ForStatement] variables (with the old |
+ // (possibly updated) values). |
+ if (new_context) body += CloneContext(); |
+ |
+ List<Expression>& updates = node->updates(); |
+ for (intptr_t i = 0; i < updates.length(); ++i) { |
+ body += TranslateExpression(updates[i]); |
+ body += Drop(); |
+ } |
+ JoinEntryInstr* join = BuildJoinEntry(); |
+ declarations += Goto(join); |
+ body += Goto(join); |
+ |
+ Fragment loop(join); |
+ loop += CheckStackOverflow(); |
+ loop += condition; |
+ } else { |
+ declarations += condition; |
+ } |
+ |
+ Fragment loop(declarations.entry, loop_exit); |
+ --loop_depth_; |
+ |
+ loop += ExitScope(node); |
+ |
+ fragment_ = loop; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitForInStatement(ForInStatement* node) { |
+ Fragment instructions = TranslateExpression(node->iterable()); |
+ instructions += PushArgument(); |
+ |
+ const dart::String& iterator_getter = dart::String::ZoneHandle( |
+ Z, dart::Field::GetterSymbol(Symbols::Iterator())); |
+ instructions += InstanceCall(iterator_getter, Token::kGET, 1); |
+ LocalVariable* iterator = scopes_->iterator_variables[for_in_depth_]; |
+ instructions += StoreLocal(iterator); |
+ instructions += Drop(); |
+ |
+ ++for_in_depth_; |
+ ++loop_depth_; |
+ Fragment condition = LoadLocal(iterator); |
+ condition += PushArgument(); |
+ condition += InstanceCall(Symbols::MoveNext(), Token::kILLEGAL, 1); |
+ TargetEntryInstr* body_entry; |
+ TargetEntryInstr* loop_exit; |
+ condition += BranchIfTrue(&body_entry, &loop_exit); |
+ |
+ Fragment body(body_entry); |
+ body += EnterScope(node); |
+ body += LoadLocal(iterator); |
+ body += PushArgument(); |
+ const dart::String& current_getter = dart::String::ZoneHandle( |
+ Z, dart::Field::GetterSymbol(Symbols::Current())); |
+ body += InstanceCall(current_getter, Token::kGET, 1); |
+ body += StoreLocal(LookupVariable(node->variable())); |
+ body += Drop(); |
+ body += TranslateStatement(node->body()); |
+ body += ExitScope(node); |
+ |
+ if (body.is_open()) { |
+ JoinEntryInstr* join = BuildJoinEntry(); |
+ instructions += Goto(join); |
+ body += Goto(join); |
+ |
+ Fragment loop(join); |
+ loop += CheckStackOverflow(); |
+ loop += condition; |
+ } else { |
+ instructions += condition; |
+ } |
+ |
+ fragment_ = Fragment(instructions.entry, loop_exit); |
+ --loop_depth_; |
+ --for_in_depth_; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitLabeledStatement(LabeledStatement* node) { |
+ // There can be serveral cases: |
+ // |
+ // * the body contains a break |
+ // * the body doesn't contain a break |
+ // |
+ // * translating the body results in a closed fragment |
+ // * translating the body results in a open fragment |
+ // |
+ // => We will only know which case we are in after the body has been |
+ // traversed. |
+ |
+ BreakableBlock block(this, node); |
+ Fragment instructions = TranslateStatement(node->body()); |
+ if (block.HadJumper()) { |
+ if (instructions.is_open()) { |
+ instructions += Goto(block.destination()); |
+ } |
+ fragment_ = Fragment(instructions.entry, block.destination()); |
+ } else { |
+ fragment_ = instructions; |
+ } |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitBreakStatement(BreakStatement* node) { |
+ TryFinallyBlock* outer_finally = NULL; |
+ intptr_t target_context_depth = -1; |
+ JoinEntryInstr* destination = breakable_block_->BreakDestination( |
+ node->target(), &outer_finally, &target_context_depth); |
+ |
+ Fragment instructions; |
+ instructions += |
+ TranslateFinallyFinalizers(outer_finally, target_context_depth); |
+ if (instructions.is_open()) { |
+ instructions += Goto(destination); |
+ } |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitSwitchStatement(SwitchStatement* node) { |
+ SwitchBlock block(this, node); |
+ |
+ // Instead of using a variable we should reuse the expression on the stack, |
+ // since it won't be assigned again, we don't need phi nodes. |
+ Fragment head_instructions = TranslateExpression(node->condition()); |
+ head_instructions += StoreLocal(scopes_->switch_variable); |
+ head_instructions += Drop(); |
+ |
+ // Phase 1: Generate bodies and try to find out whether a body will be target |
+ // of a jump due to: |
+ // * `continue case_label` |
+ // * `case e1: case e2: body` |
+ Fragment* body_fragments = new Fragment[node->cases().length()]; |
+ |
+ intptr_t num_cases = node->cases().length(); |
+ for (intptr_t i = 0; i < num_cases; i++) { |
+ SwitchCase* switch_case = node->cases()[i]; |
+ Fragment& body_fragment = body_fragments[i] = |
+ TranslateStatement(switch_case->body()); |
+ |
+ if (body_fragment.entry == NULL) { |
+ // Make a NOP in order to ensure linking works properly. |
+ body_fragment = NullConstant(); |
+ body_fragment += Drop(); |
+ } |
+ |
+ // The Dart language specification mandates fall-throughs in [SwitchCase]es |
+ // to be runtime errors. |
+ if (!switch_case->is_default() && body_fragment.is_open() && |
+ (i < (node->cases().length() - 1))) { |
+ const dart::Class& klass = dart::Class::ZoneHandle( |
+ Z, dart::Library::LookupCoreClass(Symbols::FallThroughError())); |
+ ASSERT(!klass.IsNull()); |
+ const dart::Function& constructor = dart::Function::ZoneHandle( |
+ Z, klass.LookupConstructorAllowPrivate( |
+ H.DartSymbol("FallThroughError._create"))); |
+ ASSERT(!constructor.IsNull()); |
+ const dart::String& url = H.DartString( |
+ parsed_function_->function().ToLibNamePrefixedQualifiedCString(), |
+ Heap::kOld); |
+ |
+ // Create instance of _FallThroughError |
+ body_fragment += AllocateObject(klass, 0); |
+ LocalVariable* instance = MakeTemporary(); |
+ |
+ // Call _AssertionError._create constructor. |
+ body_fragment += LoadLocal(instance); |
+ body_fragment += PushArgument(); // this |
+ |
+ body_fragment += Constant(url); |
+ body_fragment += PushArgument(); // url |
+ |
+ body_fragment += NullConstant(); |
+ body_fragment += PushArgument(); // line |
+ |
+ body_fragment += StaticCall(constructor, 3); |
+ body_fragment += Drop(); |
+ |
+ // Throw the exception |
+ body_fragment += PushArgument(); |
+ body_fragment += ThrowException(); |
+ body_fragment += Drop(); |
+ } |
+ |
+ // If there is an implicit fall-through we have one [SwitchCase] and |
+ // multiple expressions, e.g. |
+ // |
+ // switch(expr) { |
+ // case a: |
+ // case b: |
+ // <stmt-body> |
+ // } |
+ // |
+ // This means that the <stmt-body> will have more than 1 incoming edge (one |
+ // from `a == expr` and one from `a != expr && b == expr`). The |
+ // `block.Destination()` records the additional jump. |
+ if (switch_case->expressions().length() > 1) { |
+ block.Destination(switch_case); |
+ } |
+ } |
+ |
+ // Phase 2: Generate everything except the real bodies: |
+ // * jump directly to a body (if there is no jumper) |
+ // * jump to a wrapper block which jumps to the body (if there is a jumper) |
+ Fragment current_instructions = head_instructions; |
+ for (intptr_t i = 0; i < num_cases; i++) { |
+ SwitchCase* switch_case = node->cases()[i]; |
+ |
+ if (switch_case->is_default()) { |
+ ASSERT(i == (node->cases().length() - 1)); |
+ |
+ // Evaluate the conditions for the default [SwitchCase] just for the |
+ // purpose of potentially triggering a compile-time error. |
+ for (intptr_t k = 0; k < switch_case->expressions().length(); k++) { |
+ constant_evaluator_.EvaluateExpression(switch_case->expressions()[k]); |
+ } |
+ |
+ if (block.HadJumper(switch_case)) { |
+ // There are several branches to the body, so we will make a goto to |
+ // the join block (and prepend a join instruction to the real body). |
+ JoinEntryInstr* join = block.Destination(switch_case); |
+ current_instructions += Goto(join); |
+ |
+ current_instructions = Fragment(current_instructions.entry, join); |
+ current_instructions += body_fragments[i]; |
+ } else { |
+ current_instructions += body_fragments[i]; |
+ } |
+ } else { |
+ JoinEntryInstr* body_join = NULL; |
+ if (block.HadJumper(switch_case)) { |
+ body_join = block.Destination(switch_case); |
+ body_fragments[i] = Fragment(body_join) + body_fragments[i]; |
+ } |
+ |
+ for (intptr_t j = 0; j < switch_case->expressions().length(); j++) { |
+ TargetEntryInstr* then; |
+ TargetEntryInstr* otherwise; |
+ |
+ current_instructions += Constant(constant_evaluator_.EvaluateExpression( |
+ switch_case->expressions()[j])); |
+ current_instructions += PushArgument(); |
+ current_instructions += LoadLocal(scopes_->switch_variable); |
+ current_instructions += PushArgument(); |
+ current_instructions += |
+ InstanceCall(Symbols::EqualOperator(), Token::kEQ, |
+ /*argument_count=*/2, |
+ /*num_args_checked=*/2); |
+ current_instructions += BranchIfTrue(&then, &otherwise); |
+ |
+ Fragment then_fragment(then); |
+ |
+ if (body_join != NULL) { |
+ // There are several branches to the body, so we will make a goto to |
+ // the join block (the real body has already been prepended with a |
+ // join instruction). |
+ then_fragment += Goto(body_join); |
+ } else { |
+ // There is only a signle branch to the body, so we will just append |
+ // the body fragment. |
+ then_fragment += body_fragments[i]; |
+ } |
+ |
+ current_instructions = Fragment(otherwise); |
+ } |
+ } |
+ } |
+ |
+ bool has_no_default = |
+ num_cases > 0 && !node->cases()[num_cases - 1]->is_default(); |
+ if (has_no_default) { |
+ // There is no default, which means we have an open [current_instructions] |
+ // (which is a [TargetEntryInstruction] for the last "otherwise" branch). |
+ // |
+ // Furthermore the last [SwitchCase] can be open as well. If so, we need |
+ // to join these two. |
+ Fragment& last_body = body_fragments[node->cases().length() - 1]; |
+ if (last_body.is_open()) { |
+ ASSERT(current_instructions.is_open()); |
+ ASSERT(current_instructions.current->IsTargetEntry()); |
+ |
+ // Join the last "otherwise" branch and the last [SwitchCase] fragment. |
+ JoinEntryInstr* join = BuildJoinEntry(); |
+ current_instructions += Goto(join); |
+ last_body += Goto(join); |
+ |
+ current_instructions = Fragment(join); |
+ } |
+ } else { |
+ // All non-default cases will be closed (i.e. break/continue/throw/return) |
+ // So it is fine to just let more statements after the switch append to the |
+ // default case. |
+ } |
+ |
+ delete[] body_fragments; |
+ |
+ fragment_ = Fragment(head_instructions.entry, current_instructions.current); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitContinueSwitchStatement( |
+ ContinueSwitchStatement* node) { |
+ TryFinallyBlock* outer_finally = NULL; |
+ intptr_t target_context_depth = -1; |
+ JoinEntryInstr* entry = switch_block_->Destination( |
+ node->target(), &outer_finally, &target_context_depth); |
+ |
+ Fragment instructions; |
+ instructions += |
+ TranslateFinallyFinalizers(outer_finally, target_context_depth); |
+ if (instructions.is_open()) { |
+ instructions += Goto(entry); |
+ } |
+ fragment_ = instructions; |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitAssertStatement(AssertStatement* node) { |
+ if (!I->asserts()) { |
+ fragment_ = Fragment(); |
+ return; |
+ } |
+ |
+ TargetEntryInstr* then; |
+ TargetEntryInstr* otherwise; |
+ |
+ bool negate; |
+ Fragment instructions; |
+ instructions += TranslateCondition(node->condition(), &negate); |
+ instructions += BranchIfTrue(&then, &otherwise, negate); |
+ |
+ const dart::Class& klass = dart::Class::ZoneHandle( |
+ Z, dart::Library::LookupCoreClass(Symbols::AssertionError())); |
+ ASSERT(!klass.IsNull()); |
+ const dart::Function& constructor = dart::Function::ZoneHandle( |
+ Z, klass.LookupConstructorAllowPrivate( |
+ H.DartSymbol("_AssertionError._create"))); |
+ ASSERT(!constructor.IsNull()); |
+ |
+ const dart::String& url = H.DartString( |
+ parsed_function_->function().ToLibNamePrefixedQualifiedCString(), |
+ Heap::kOld); |
+ |
+ // Create instance of _AssertionError |
+ Fragment otherwise_fragment(otherwise); |
+ otherwise_fragment += AllocateObject(klass, 0); |
+ LocalVariable* instance = MakeTemporary(); |
+ |
+ // Call _AssertionError._create constructor. |
+ otherwise_fragment += LoadLocal(instance); |
+ otherwise_fragment += PushArgument(); // this |
+ |
+ otherwise_fragment += |
+ node->message() != NULL |
+ ? TranslateExpression(node->message()) |
+ : Constant(H.DartString("<no message>", Heap::kOld)); |
+ otherwise_fragment += PushArgument(); // message |
+ |
+ otherwise_fragment += Constant(url); |
+ otherwise_fragment += PushArgument(); // url |
+ |
+ otherwise_fragment += IntConstant(0); |
+ otherwise_fragment += PushArgument(); // line |
+ |
+ otherwise_fragment += IntConstant(0); |
+ otherwise_fragment += PushArgument(); // column |
+ |
+ otherwise_fragment += StaticCall(constructor, 5); |
+ otherwise_fragment += Drop(); |
+ |
+ // Throw _AssertionError exception. |
+ otherwise_fragment += PushArgument(); |
+ otherwise_fragment += ThrowException(); |
+ otherwise_fragment += Drop(); |
+ |
+ fragment_ = Fragment(instructions.entry, then); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitTryFinally(TryFinally* node) { |
+ InlineBailout("kernel::FlowgraphBuilder::VisitTryFinally"); |
+ |
+ // There are 5 different cases where we need to execute the finally block: |
+ // |
+ // a) 1/2/3th case: Special control flow going out of `node->body()`: |
+ // |
+ // * [BreakStatement] transfers control to a [LabledStatement] |
+ // * [ContinueSwitchStatement] transfers control to a [SwitchCase] |
+ // * [ReturnStatement] returns a value |
+ // |
+ // => All three cases will automatically append all finally blocks |
+ // between the branching point and the destination (so we don't need to |
+ // do anything here). |
+ // |
+ // b) 4th case: Translating the body resulted in an open fragment (i.e. body |
+ // executes without any control flow out of it) |
+ // |
+ // => We are responsible for jumping out of the body to a new block (with |
+ // different try index) and execute the finalizer. |
+ // |
+ // c) 5th case: An exception occured inside the body. |
+ // |
+ // => We are responsible for catching it, executing the finally block and |
+ // rethrowing the exception. |
+ intptr_t try_handler_index = AllocateTryIndex(); |
+ Fragment try_body = TryCatch(try_handler_index); |
+ JoinEntryInstr* after_try = BuildJoinEntry(); |
+ |
+ // Fill in the body of the try. |
+ ++try_depth_; |
+ { |
+ TryCatchBlock tcb(this, try_handler_index); |
+ TryFinallyBlock tfb(this, node->finalizer()); |
+ try_body += TranslateStatement(node->body()); |
+ } |
+ --try_depth_; |
+ |
+ if (try_body.is_open()) { |
+ // Please note: The try index will be on level out of this block, |
+ // thereby ensuring if there's an exception in the finally block we |
+ // won't run it twice. |
+ JoinEntryInstr* finally_entry = BuildJoinEntry(); |
+ |
+ try_body += Goto(finally_entry); |
+ |
+ Fragment finally_body(finally_entry); |
+ finally_body += TranslateStatement(node->finalizer()); |
+ finally_body += Goto(after_try); |
+ } |
+ |
+ // Fill in the body of the catch. |
+ ++catch_depth_; |
+ const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld)); |
+ handler_types.SetAt(0, Object::dynamic_type()); |
+ Fragment finally_body = CatchBlockEntry(handler_types, try_handler_index); |
+ finally_body += TranslateStatement(node->finalizer()); |
+ if (finally_body.is_open()) { |
+ finally_body += LoadLocal(CurrentException()); |
+ finally_body += PushArgument(); |
+ finally_body += LoadLocal(CurrentStackTrace()); |
+ finally_body += PushArgument(); |
+ finally_body += RethrowException(try_handler_index); |
+ Drop(); |
+ } |
+ --catch_depth_; |
+ |
+ fragment_ = Fragment(try_body.entry, after_try); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitTryCatch(class TryCatch* node) { |
+ InlineBailout("kernel::FlowgraphBuilder::VisitTryCatch"); |
+ |
+ intptr_t try_handler_index = AllocateTryIndex(); |
+ Fragment try_body = TryCatch(try_handler_index); |
+ JoinEntryInstr* after_try = BuildJoinEntry(); |
+ |
+ // Fill in the body of the try. |
+ ++try_depth_; |
+ { |
+ TryCatchBlock block(this, try_handler_index); |
+ try_body += TranslateStatement(node->body()); |
+ try_body += Goto(after_try); |
+ } |
+ --try_depth_; |
+ |
+ ++catch_depth_; |
+ const Array& handler_types = |
+ Array::ZoneHandle(Z, Array::New(node->catches().length(), Heap::kOld)); |
+ Fragment catch_body = CatchBlockEntry(handler_types, try_handler_index); |
+ // Fill in the body of the catch. |
+ for (intptr_t i = 0; i < node->catches().length(); i++) { |
+ Catch* catch_clause = node->catches()[i]; |
+ |
+ Fragment catch_handler_body; |
+ |
+ catch_handler_body += EnterScope(catch_clause); |
+ |
+ if (catch_clause->exception() != NULL) { |
+ catch_handler_body += LoadLocal(CurrentException()); |
+ catch_handler_body += |
+ StoreLocal(LookupVariable(catch_clause->exception())); |
+ catch_handler_body += Drop(); |
+ } |
+ if (catch_clause->stack_trace() != NULL) { |
+ catch_handler_body += LoadLocal(CurrentStackTrace()); |
+ catch_handler_body += |
+ StoreLocal(LookupVariable(catch_clause->stack_trace())); |
+ catch_handler_body += Drop(); |
+ } |
+ AbstractType* type_guard = NULL; |
+ if (catch_clause->guard() != NULL && |
+ !catch_clause->guard()->IsDynamicType()) { |
+ type_guard = &T.TranslateType(catch_clause->guard()); |
+ handler_types.SetAt(i, *type_guard); |
+ } else { |
+ handler_types.SetAt(i, Object::dynamic_type()); |
+ } |
+ |
+ { |
+ CatchBlock block(this, CurrentException(), CurrentStackTrace(), |
+ try_handler_index); |
+ |
+ catch_handler_body += TranslateStatement(catch_clause->body()); |
+ |
+ // Note: ExitScope adjusts context_depth_ so even if catch_handler_body |
+ // is closed we still need to execute ExitScope for its side effect. |
+ catch_handler_body += ExitScope(catch_clause); |
+ if (catch_handler_body.is_open()) { |
+ catch_handler_body += Goto(after_try); |
+ } |
+ } |
+ |
+ if (type_guard != NULL) { |
+ if (type_guard->IsMalformed()) { |
+ catch_body += ThrowTypeError(); |
+ catch_body += Drop(); |
+ } else { |
+ catch_body += LoadLocal(CurrentException()); |
+ catch_body += PushArgument(); // exception |
+ catch_body += NullConstant(); |
+ catch_body += PushArgument(); // type arguments |
+ catch_body += Constant(*type_guard); |
+ catch_body += PushArgument(); // guard type |
+ catch_body += Constant(Object::bool_false()); |
+ catch_body += PushArgument(); // negate |
+ catch_body += InstanceCall( |
+ dart::Library::PrivateCoreLibName(Symbols::_instanceOf()), |
+ Token::kIS, 4); |
+ |
+ TargetEntryInstr* catch_entry; |
+ TargetEntryInstr* next_catch_entry; |
+ catch_body += BranchIfTrue(&catch_entry, &next_catch_entry); |
+ |
+ Fragment(catch_entry) + catch_handler_body; |
+ catch_body = Fragment(next_catch_entry); |
+ } |
+ } else { |
+ catch_body += catch_handler_body; |
+ } |
+ } |
+ |
+ // In case the last catch body was not handling the exception and branching to |
+ // after the try block, we will rethrow the exception (i.e. no default catch |
+ // handler). |
+ if (catch_body.is_open()) { |
+ catch_body += LoadLocal(CurrentException()); |
+ catch_body += PushArgument(); |
+ catch_body += LoadLocal(CurrentStackTrace()); |
+ catch_body += PushArgument(); |
+ catch_body += RethrowException(try_handler_index); |
+ Drop(); |
+ } |
+ --catch_depth_; |
+ |
+ fragment_ = Fragment(try_body.entry, after_try); |
+} |
+ |
+ |
+void FlowGraphBuilder::VisitYieldStatement(YieldStatement* node) { |
+ ASSERT(node->is_native()); // Must have been desugared. |
+ // Setup yield/continue point: |
+ // |
+ // ... |
+ // :await_jump_var = index; |
+ // :await_ctx_var = :current_context_var |
+ // return <expr> |
+ // |
+ // Continuation<index>: |
+ // Drop(1) |
+ // ... |
+ // |
+ // BuildGraphOfFunction will create a dispatch that jumps to |
+ // Continuation<:await_jump_var> upon entry to the function. |
+ // |
+ Fragment instructions = IntConstant(yield_continuations_.length() + 1); |
+ instructions += StoreLocal(scopes_->yield_jump_variable); |
+ instructions += Drop(); |
+ instructions += LoadLocal(parsed_function_->current_context_var()); |
+ instructions += StoreLocal(scopes_->yield_context_variable); |
+ instructions += Drop(); |
+ instructions += TranslateExpression(node->expression()); |
+ instructions += Return(); |
+ |
+ // Note: DropTempsInstr serves as an anchor instruction. It will not |
+ // be linked into the resulting graph. |
+ DropTempsInstr* anchor = new (Z) DropTempsInstr(0, NULL); |
+ yield_continuations_.Add(YieldContinuation(anchor, CurrentTryIndex())); |
+ |
+ Fragment continuation(instructions.entry, anchor); |
+ |
+ // TODO(27590): we need a better way to detect if we need to check for an |
+ // exception after yield or not. |
+ if (parsed_function_->function().NumOptionalPositionalParameters() == 3) { |
+ // If function takes three parameters then the second and the third |
+ // are exception and stack_trace. Check if exception is non-null |
+ // and rethrow it. |
+ // |
+ // :async_op([:result, :exception, :stack_trace]) { |
+ // ... |
+ // Continuation<index>: |
+ // if (:exception != null) rethrow(:exception, :stack_trace); |
+ // ... |
+ // } |
+ // |
+ LocalScope* scope = parsed_function_->node_sequence()->scope(); |
+ LocalVariable* exception_var = scope->VariableAt(2); |
+ LocalVariable* stack_trace_var = scope->VariableAt(3); |
+ ASSERT(exception_var->name().raw() == Symbols::ExceptionParameter().raw()); |
+ ASSERT(stack_trace_var->name().raw() == |
+ Symbols::StackTraceParameter().raw()); |
+ |
+ TargetEntryInstr* no_error; |
+ TargetEntryInstr* error; |
+ |
+ continuation += LoadLocal(exception_var); |
+ continuation += BranchIfNull(&no_error, &error); |
+ |
+ Fragment rethrow(error); |
+ rethrow += LoadLocal(exception_var); |
+ rethrow += PushArgument(); |
+ rethrow += LoadLocal(stack_trace_var); |
+ rethrow += PushArgument(); |
+ rethrow += RethrowException(CatchClauseNode::kInvalidTryIndex); |
+ Drop(); |
+ |
+ |
+ continuation = Fragment(continuation.entry, no_error); |
+ } |
+ |
+ fragment_ = continuation; |
+} |
+ |
+ |
+Fragment FlowGraphBuilder::TranslateFunctionNode(FunctionNode* node, |
+ TreeNode* parent) { |
+ // The VM has a per-isolate table of functions indexed by the enclosing |
+ // function and token position. We don't have token positions, so we've |
+ // simply numbered the immediately-nested functions with respect to the |
+ // parent. |
+ Function& function = Function::ZoneHandle(Z); |
+ for (intptr_t i = 0; i < scopes_->function_scopes.length(); ++i) { |
+ if (scopes_->function_scopes[i].function != node) continue; |
+ |
+ function = I->LookupClosureFunction(parsed_function_->function(), |
+ TokenPosition(i)); |
+ if (function.IsNull()) { |
+ const dart::String* name; |
+ if (parent->IsFunctionExpression()) { |
+ name = &Symbols::AnonymousClosure(); |
+ } else { |
+ ASSERT(parent->IsFunctionDeclaration()); |
+ name = &H.DartSymbol( |
+ FunctionDeclaration::Cast(parent)->variable()->name()); |
+ } |
+ function = Function::NewClosureFunction( |
+ *name, parsed_function_->function(), TokenPosition(i)); |
+ function.set_is_debuggable(false); |
+ LocalScope* scope = scopes_->function_scopes[i].scope; |
+ const ContextScope& context_scope = |
+ ContextScope::Handle(Z, scope->PreserveOuterScope(context_depth_)); |
+ function.set_context_scope(context_scope); |
+ function.set_kernel_function(node); |
+ KernelReader::SetupFunctionParameters(H, T, dart::Class::Handle(Z), |
+ function, node, |
+ false, // is_method |
+ true); // is_closure |
+ // Finalize function type. |
+ Type& signature_type = Type::Handle(Z, function.SignatureType()); |
+ signature_type ^= ClassFinalizer::FinalizeType( |
+ *active_class_.klass, signature_type, ClassFinalizer::kCanonicalize); |
+ function.SetSignatureType(signature_type); |
+ |
+ I->AddClosureFunction(function); |
+ } |
+ break; |
+ } |
+ |
+ const dart::Class& closure_class = |
+ dart::Class::ZoneHandle(Z, I->object_store()->closure_class()); |
+ ASSERT(!closure_class.IsNull()); |
+ Fragment instructions = AllocateObject(closure_class, function); |
+ LocalVariable* closure = MakeTemporary(); |
+ |
+ // TODO(27590): Generic closures need type arguments. |
+ |
+ // Store the function and the context in the closure. |
+ instructions += LoadLocal(closure); |
+ instructions += Constant(function); |
+ instructions += StoreInstanceField(Closure::function_offset()); |
+ |
+ instructions += LoadLocal(closure); |
+ instructions += LoadLocal(parsed_function_->current_context_var()); |
+ instructions += StoreInstanceField(Closure::context_offset()); |
+ |
+ return instructions; |
+} |
+ |
+ |
+} // namespace kernel |
+} // namespace dart |