| // 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" |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| 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() |
| |
| |
| static void DiscoverEnclosingElements(Zone* zone, |
| const Function& function, |
| Function* outermost_function, |
| TreeNode** outermost_node, |
| Class** klass) { |
| // Find out if there is an enclosing kernel class (which will be used to |
| // resolve type parameters). |
| *outermost_function = function.raw(); |
| while (outermost_function->parent_function() != Object::null()) { |
| *outermost_function = outermost_function->parent_function(); |
| } |
| *outermost_node = |
| static_cast<TreeNode*>(outermost_function->kernel_function()); |
| if (*outermost_node != NULL) { |
| TreeNode* parent = NULL; |
| if ((*outermost_node)->IsProcedure()) { |
| parent = Procedure::Cast(*outermost_node)->parent(); |
| } else if ((*outermost_node)->IsConstructor()) { |
| parent = Constructor::Cast(*outermost_node)->parent(); |
| } else if ((*outermost_node)->IsField()) { |
| parent = Field::Cast(*outermost_node)->parent(); |
| } |
| if (parent != NULL && parent->IsClass()) *klass = Class::Cast(parent); |
| } |
| } |
| |
| |
| 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, |
| const AbstractType& type) { |
| return new (Z) LocalVariable(TokenPosition::kNoSource, |
| 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) { |
| LocalVariable* variable = MakeVariable(H.DartSymbol(declaration->name()), |
| T.TranslateVariableType(declaration)); |
| if (declaration->IsFinal()) { |
| variable->set_is_final(); |
| } |
| 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), |
| AbstractType::dynamic_type()); |
| |
| // 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), |
| AbstractType::dynamic_type()); |
| 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) { |
| // In case `declaration->IsConst()` the flow graph building will take care of |
| // evaluating the constant and setting it via |
| // `declaration->SetConstantValue()`. |
| const dart::String& name = declaration->name()->is_empty() |
| ? GenerateName(":var", name_index_++) |
| : H.DartSymbol(declaration->name()); |
| LocalVariable* variable = |
| MakeVariable(name, T.TranslateVariableType(declaration)); |
| if (declaration->IsFinal()) { |
| variable->set_is_final(); |
| } |
| 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(); |
| |
| // Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used |
| // e.g. for type translation. |
| const dart::Class& klass = |
| dart::Class::Handle(zone_, parsed_function_->function().Owner()); |
| Function& outermost_function = Function::Handle(Z); |
| TreeNode* outermost_node = NULL; |
| Class* kernel_klass = NULL; |
| DiscoverEnclosingElements(Z, function, &outermost_function, &outermost_node, |
| &kernel_klass); |
| // Use [klass]/[kernel_klass] as active class. Type parameters will get |
| // resolved via [kernel_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 = ((outermost_node != NULL) && outermost_node->IsMember()) |
| ? Member::Cast(outermost_node) |
| : NULL; |
| ActiveMemberScope active_member(&active_class_, member); |
| |
| |
| 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); |
| |
| LocalVariable* context_var = parsed_function->current_context_var(); |
| context_var->set_is_forced_stack(); |
| scope_->AddVariable(context_var); |
| scope_->AddVariable(parsed_function->EnsureExpressionTemp()); |
| |
| 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(), |
| AbstractType::dynamic_type()); |
| variable->set_is_forced_stack(); |
| 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(), AbstractType::dynamic_type()); |
| 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)), |
| AbstractType::dynamic_type()); |
| 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(), AbstractType::dynamic_type()); |
| variable->set_is_forced_stack(); |
| 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, AbstractType::dynamic_type()); |
| 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]); |
| } |
| |
| if (node->async_marker() == FunctionNode::kSyncYielding) { |
| LocalScope* scope = parsed_function_->node_sequence()->scope(); |
| for (intptr_t i = 0; |
| i < parsed_function_->function().NumOptionalPositionalParameters(); |
| i++) { |
| scope->VariableAt(i)->set_is_forced_stack(); |
| } |
| } |
| |
| // 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, :await_ctx_var and :async_op 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()); |
| } |
| { |
| LocalVariable* temp = |
| scope_->LookupVariable(Symbols::AsyncOperation(), true); |
| if (temp != NULL) { |
| scope_->CaptureVariable(temp); |
| } |
| } |
| } |
| } |
| |
| |
| 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), |
| try_index_(builder_->CurrentTryIndex()) { |
| 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_; } |
| intptr_t try_index() const { return try_index_; } |
| 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_; |
| const intptr_t try_index_; |
| }; |
| |
| |
| 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 try_index() { return try_index_; } |
| TryCatchBlock* outer() const { return outer_; } |
| |
| 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) { |
| ASSERT(kernel_klass->IsNormalClass()); |
| dart::String& name = DartString(kernel_klass->name()); |
| return ManglePrivateName(kernel_klass->parent(), &name); |
| } |
| |
| |
| 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, |
| allocation_space_)); |
| 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. |
| GrowableHandlePtrArray<const dart::String> pieces(Z, 3); |
| pieces.Add(DartClassName(klass)); |
| pieces.Add(Symbols::Dot()); |
| pieces.Add(DartMethodName(method_name)); |
| return dart::String::ZoneHandle( |
| Z, dart::Symbols::FromConcatAll(thread_, pieces)); |
| } |
| |
| |
| 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::RawUnresolvedClass* TranslationHelper::ToUnresolvedClass( |
| 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)); |
| |
| ASSERT(klass != Object::null()); |
| return dart::UnresolvedClass::New(library, class_name, |
| TokenPosition::kNoSource); |
| } |
| |
| |
| 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(), Heap::kOld)); |
| for (intptr_t i = 0; i < named->length(); ++i) { |
| names.SetAt(i, DartSymbol((*named)[i]->name())); |
| } |
| return names; |
| } |
| |
| |
| ConstantEvaluator::ConstantEvaluator(FlowGraphBuilder* builder, |
| Zone* zone, |
| TranslationHelper* h, |
| DartTypeTranslator* type_translator) |
| : builder_(builder), |
| isolate_(Isolate::Current()), |
| zone_(zone), |
| translation_helper_(*h), |
| type_translator_(*type_translator), |
| script_(dart::Script::Handle( |
| zone, |
| builder_->parsed_function_->function().script())), |
| result_(dart::Instance::Handle(zone)) {} |
| |
| |
| Instance& ConstantEvaluator::EvaluateExpression(Expression* expression) { |
| if (!GetCachedConstant(expression, &result_)) { |
| expression->AcceptExpressionVisitor(this); |
| CacheConstantValue(expression, result_); |
| } |
| // 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) { |
| if (!GetCachedConstant(node, &result_)) { |
| VisitConstructorInvocation(node); |
| CacheConstantValue(node, result_); |
| } |
| // 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) { |
| if (!GetCachedConstant(node, &result_)) { |
| VisitListLiteral(node); |
| CacheConstantValue(node, result_); |
| } |
| // 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) { |
| if (!GetCachedConstant(node, &result_)) { |
| VisitMapLiteral(node); |
| CacheConstantValue(node, result_); |
| } |
| // 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); |
| } |
| |
| |
| bool ConstantEvaluator::GetCachedConstant(TreeNode* node, Instance* value) { |
| const Function& function = builder_->parsed_function_->function(); |
| if (function.kind() == RawFunction::kImplicitStaticFinalGetter) { |
| // Don't cache constants in initializer expressions. They get |
| // evaluated only once. |
| return false; |
| } |
| |
| bool is_present = false; |
| ASSERT(!script_.InVMHeap()); |
| if (script_.compile_time_constants() == Array::null()) { |
| return false; |
| } |
| KernelConstantsMap constants(script_.compile_time_constants()); |
| *value ^= constants.GetOrNull(node, &is_present); |
| // Mutator compiler thread may add constants while background compiler |
| // is running, and thus change the value of 'compile_time_constants'; |
| // do not assert that 'compile_time_constants' has not changed. |
| constants.Release(); |
| if (FLAG_compiler_stats && is_present) { |
| H.thread()->compiler_stats()->num_const_cache_hits++; |
| } |
| return is_present; |
| } |
| |
| |
| void ConstantEvaluator::CacheConstantValue(TreeNode* node, |
| const Instance& value) { |
| ASSERT(Thread::Current()->IsMutatorThread()); |
| |
| const Function& function = builder_->parsed_function_->function(); |
| if (function.kind() == RawFunction::kImplicitStaticFinalGetter) { |
| // Don't cache constants in initializer expressions. They get |
| // evaluated only once. |
| return; |
| } |
| const intptr_t kInitialConstMapSize = 16; |
| ASSERT(!script_.InVMHeap()); |
| if (script_.compile_time_constants() == Array::null()) { |
| const Array& array = Array::Handle( |
| HashTables::New<KernelConstantsMap>(kInitialConstMapSize, Heap::kNew)); |
| script_.set_compile_time_constants(array); |
| } |
| KernelConstantsMap constants(script_.compile_time_constants()); |
| constants.InsertNewOrGetValue(node, value); |
| script_.set_compile_time_constants(constants.Release()); |
| } |
| |
| |
| 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. |
| 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(); |
| } |
| |
| // The frontend should guarantee that [MethodInvocation]s inside constant |
| // expressions are always valid. |
| 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_, |
| /* finalize= */ true), |
| 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_; |
| TryCatchBlock* const saved_try_catch_block = try_catch_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()); |
| |
| // The to-be-translated finalizer has to have the correct try-index (namely |
| // the one outside the try-finally block). |
| bool changed_try_index = false; |
| intptr_t target_try_index = try_finally_block_->try_index(); |
| while (CurrentTryIndex() != target_try_index) { |
| try_catch_block_ = try_catch_block_->outer(); |
| changed_try_index = true; |
| } |
| if (changed_try_index) { |
| JoinEntryInstr* entry = BuildJoinEntry(); |
| instructions += Goto(entry); |
| instructions = Fragment(instructions.entry, entry); |
| } |
| |
| 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 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; |
| try_catch_block_ = saved_try_catch_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(TokenPosition position, |
| const dart::String& name, |
| Token::Kind kind, |
| intptr_t argument_count, |
| intptr_t num_args_checked) { |
| return InstanceCall(position, name, kind, argument_count, Array::null_array(), |
| num_args_checked); |
| } |
| |
| |
| Fragment FlowGraphBuilder::InstanceCall(TokenPosition position, |
| 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(position, 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(TokenPosition position) { |
| Fragment instructions; |
| instructions += Drop(); |
| instructions += Fragment(new (Z) ThrowInstr(position)).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); |
| } |
| |
| |
| const dart::Field& MayCloneField(Zone* zone, const dart::Field& field) { |
| if ((Compiler::IsBackgroundCompilation() || |
| FLAG_force_clone_compiler_objects) && |
| field.IsOriginal()) { |
| return dart::Field::ZoneHandle(zone, field.CloneFromOriginal()); |
| } else { |
| ASSERT(field.IsZoneHandle()); |
| return field; |
| } |
| } |
| |
| |
| Fragment FlowGraphBuilder::LoadField(const dart::Field& field) { |
| LoadFieldInstr* load = new (Z) LoadFieldInstr( |
| Pop(), &MayCloneField(Z, 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(), MayCloneField(Z, 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(TokenPosition position, |
| const Function& target, |
| intptr_t argument_count) { |
| return StaticCall(position, 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(TokenPosition position, |
| const Function& target, |
| intptr_t argument_count, |
| const Array& argument_names) { |
| ArgumentArray arguments = GetArguments(argument_count); |
| StaticCallInstr* call = new (Z) StaticCallInstr( |
| position, 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(); |
| const StoreBarrierType emit_store_barrier = |
| value->BindsToConstant() ? kNoStoreBarrier : kEmitStoreBarrier; |
| StoreIndexedInstr* store = new (Z) StoreIndexedInstr( |
| Pop(), // Array. |
| index, value, emit_store_barrier, Instance::ElementSizeFor(class_id), |
| class_id, kAlignedAccess, Thread::kNoDeoptId, TokenPosition::kNoSource); |
| Push(store); |
| return Fragment(store); |
| } |
| |
| |
| Fragment FlowGraphBuilder::StoreInstanceField( |
| const dart::Field& field, |
| StoreBarrierType emit_store_barrier) { |
| Value* value = Pop(); |
| if (value->BindsToConstant()) { |
| emit_store_barrier = kNoStoreBarrier; |
| } |
| StoreInstanceFieldInstr* store = new (Z) |
| StoreInstanceFieldInstr(MayCloneField(Z, field), Pop(), value, |
| emit_store_barrier, TokenPosition::kNoSource); |
| return Fragment(store); |
| } |
| |
| |
| Fragment FlowGraphBuilder::StoreInstanceFieldGuarded(const dart::Field& field) { |
| Fragment instructions; |
| const dart::Field& field_clone = MayCloneField(Z, field); |
| if (FLAG_use_field_guards) { |
| LocalVariable* store_expression = MakeTemporary(); |
| instructions += LoadLocal(store_expression); |
| instructions += |
| GuardFieldClass(field_clone, Thread::Current()->GetNextDeoptId()); |
| instructions += LoadLocal(store_expression); |
| instructions += |
| GuardFieldLength(field_clone, Thread::Current()->GetNextDeoptId()); |
| } |
| instructions += StoreInstanceField(field_clone); |
| return instructions; |
| } |
| |
| |
| Fragment FlowGraphBuilder::StoreInstanceField( |
| intptr_t offset, |
| StoreBarrierType emit_store_barrier) { |
| Value* value = Pop(); |
| if (value->BindsToConstant()) { |
| emit_store_barrier = kNoStoreBarrier; |
| } |
| StoreInstanceFieldInstr* store = new (Z) StoreInstanceFieldInstr( |
| offset, Pop(), value, emit_store_barrier, 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(MayCloneField(Z, 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(TokenPosition::kNoSource, constructor, 5); |
| instructions += Drop(); |
| |
| // Throw the exception |
| instructions += PushArgument(); |
| instructions += ThrowException(TokenPosition::kNoSource); |
| |
| 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(TokenPosition::kNoSource, 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, 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_->try_index(); |
| } |
| } |
| |
| |
| 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()); |
| |
| Function& outermost_function = Function::Handle(Z); |
| TreeNode* outermost_node = NULL; |
| Class* kernel_klass = NULL; |
| DiscoverEnclosingElements(Z, function, &outermost_function, &outermost_node, |
| &kernel_klass); |
| |
| // 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 = ((outermost_node != NULL) && outermost_node->IsMember()) |
| ? Member::Cast(outermost_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, osr_id_); |
| |
| 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 and use the same type. |
| LocalVariable* parameter = new (Z) |
| LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::TempParam(), variable->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); |
| |
| // When compiling for OSR, use a depth first search to prune instructions |
| // unreachable from the OSR entry. Catch entries are always considered |
| // reachable, even if they become unreachable after OSR. |
| if (osr_id_ != Compiler::kNoOSRDeoptId) { |
| BitVector* block_marks = new (Z) BitVector(Z, next_block_id_); |
| bool found = graph_entry_->PruneUnreachable(graph_entry_, NULL, osr_id_, |
| block_marks); |
| ASSERT(found); |
| } |
| 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])); |
| body += StoreInstanceField(LinkedHashMap::hash_mask_offset(), |
| kNoStoreBarrier); |
| 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])); |
| body += StoreInstanceField(LinkedHashMap::used_data_offset(), |
| kNoStoreBarrier); |
| 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])); |
| body += StoreInstanceField(LinkedHashMap::deleted_keys_offset(), |
| kNoStoreBarrier); |
| 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); |
| |
| Fragment body(normal_entry); |
| if (is_setter) { |
| if (is_method) { |
| body += LoadLocal(scopes_->this_variable); |
| body += LoadLocal(setter_value); |
| body += StoreInstanceFieldGuarded(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; |
| } |
| |
| |
| Fragment FlowGraphBuilder::GuardFieldLength(const dart::Field& field, |
| intptr_t deopt_id) { |
| return Fragment(new (Z) GuardFieldLengthInstr(Pop(), field, deopt_id)); |
| } |
| |
| |
| Fragment FlowGraphBuilder::GuardFieldClass(const dart::Field& field, |
| intptr_t deopt_id) { |
| return Fragment(new (Z) GuardFieldClassInstr(Pop(), field, deopt_id)); |
| } |
| |
| |
| 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(TokenPosition::kNoSource, 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(TokenPosition::kMinSource, 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(TokenPosition::kMinSource, 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(TokenPosition::kMinSource, 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(TokenPosition::kMinSource, 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); |
| instructions += LoadLocal(scopes_->this_variable); |
| instructions += TranslateExpression(init); |
| instructions += StoreInstanceFieldGuarded(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())); |
| |
| instructions += LoadLocal(scopes_->this_variable); |
| instructions += TranslateExpression(init->value()); |
| instructions += StoreInstanceFieldGuarded(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(TokenPosition::kNoSource, 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(TokenPosition::kNoSource, 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) { |
| // The frontend will take care of emitting normal errors (like |
| // [NoSuchMethodError]s) and only emit [InvalidExpression]s in very special |
| // situations (e.g. an invalid annotation). |
| 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; |
| AbstractType& result = TranslateType(node); |
| finalize_ = saved_finalize; |
| return result; |
| } |
| |
| |
| const AbstractType& DartTypeTranslator::TranslateVariableType( |
| VariableDeclaration* variable) { |
| AbstractType& abstract_type = TranslateType(variable->type()); |
| |
| // We return a new `ZoneHandle` here on purpose: The intermediate language |
| // instructions do not make a copy of the handle, so we do it. |
| AbstractType& type = Type::ZoneHandle(Z); |
| |
| if (abstract_type.IsMalformed()) { |
| type = AbstractType::dynamic_type().raw(); |
| } else { |
| type = result_.raw(); |
| } |
| |
| return type; |
| } |
| |
| |
| 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) { |
| // The spec describes in section "19.1 Static Types": |
| // |
| // Any use of a malformed type gives rise to a static warning. A |
| // malformed type is then interpreted as dynamic by the static type |
| // checker and the runtime unless explicitly specified otherwise. |
| // |
| // So we convert malformed return/parameter types to `dynamic`. |
| TypeParameterScope scope(this, &node->type_parameters()); |
| |
| Function& signature_function = Function::ZoneHandle( |
| Z, Function::NewSignatureFunction(*active_class_->klass, |
| TokenPosition::kNoSource)); |
| |
| node->return_type()->AcceptDartTypeVisitor(this); |
| if (result_.IsMalformed()) { |
| result_ = AbstractType::dynamic_type().raw(); |
| } |
| 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()) { |
| result_ = AbstractType::dynamic_type().raw(); |
| } |
| 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()) { |
| result_ = AbstractType::dynamic_type().raw(); |
| } |
| 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); |
| // Do not refer to signature_function anymore, since it may have been |
| // replaced during canonicalization. |
| signature_function = Function::null(); |
| } |
| |
| result_ = signature_type.raw(); |
| } |
| |
| |
| static intptr_t FindTypeParameterIndex(List<TypeParameter>* parameters, |
| TypeParameter* param) { |
| for (intptr_t i = 0; i < parameters->length(); i++) { |
| if (param == (*parameters)[i]) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| |
| void DartTypeTranslator::VisitTypeParameterType(TypeParameterType* node) { |
| for (TypeParameterScope* scope = type_parameter_scope_; scope != NULL; |
| scope = scope->outer()) { |
| const intptr_t index = |
| FindTypeParameterIndex(scope->parameters(), node->parameter()); |
| if (index >= 0) { |
| result_ ^= dart::Type::DynamicType(); |
| return; |
| } |
| } |
| |
| 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'>(); } |
| // } |
| // |
| const intptr_t index = FindTypeParameterIndex( |
| &procedure->function()->type_parameters(), node->parameter()); |
| if (index >= 0) { |
| if (procedure->kind() == Procedure::kFactory) { |
| // 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(index); |
| } else { |
| result_ ^= dart::Type::DynamicType(); |
| } |
| return; |
| } |
| } |
| } |
| |
| ASSERT(active_class_->kernel_class != NULL); |
| List<TypeParameter>* parameters = |
| &active_class_->kernel_class->type_parameters(); |
| const intptr_t index = FindTypeParameterIndex(parameters, node->parameter()); |
| if (index >= 0) { |
| // 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(index); |
| 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()); |
| |
| |
| dart::Object& klass = |
| dart::Object::Handle(Z, H.ToUnresolvedClass(node->klass())); |
| result_ = Type::New(klass, type_arguments, TokenPosition::kNoSource); |
| if (finalize_) { |
| ASSERT(active_class_->klass != NULL); |
| result_ = ClassFinalizer::FinalizeType(*active_class_->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()); |
| if (klass.is_type_finalized()) { |
| type ^= ClassFinalizer::FinalizeType( |
| klass, type, ClassFinalizer::kCanonicalizeWellFormed); |
| klass.SetCanonicalType(type); |
| } |
| 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()); |
| instructions += StoreLocal(LookupVariable(node->variable())); |
| fragment_ = instructions; |
| } |
| |
| |
| 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 { |
| fragment_ = StaticCall(node->position(), getter, 0); |
| } |
| } |
| } else { |
| Procedure* procedure = Procedure::Cast(target); |
| const Function& target = Function::ZoneHandle( |
| Z, H.LookupStaticMethodByKernelProcedure(procedure)); |
| |
| if (procedure->kind() == Procedure::kGetter) { |
| fragment_ = StaticCall(node->position(), 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(node->position(), 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(node->position(), 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(node->position(), 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(node->position(), 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(node->position(), 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); |
| |
| // The frontend ensures we the [StaticInvocation] has matching arguments. |
| ASSERT(target.AreValidArguments(argument_count, argument_names, NULL)); |
| |
| Fragment instructions; |
| 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 { |
| // TODO(28109) Support generic methods in the VM or reify them away. |
| } |
| |
| // 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(node->position(), 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(); |
| |
| // TODO(28109) Support generic methods in the VM or reify them away. |
| 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(node->position(), 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); |
| |
| // TODO(28109) Support generic methods in the VM or reify them away. |
| Fragment instructions = TranslateExpression(node->receiver()); |
| instructions += PushArgument(); |
| instructions += TranslateArguments(node->arguments(), &argument_names); |
| fragment_ = instructions + StaticCall(node->position(), 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(node->position(), 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(TokenPosition::kNoSource, |
| 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( |
| TokenPosition::kNoSource, |
| 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(node->position(), 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(node->position(), 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(node->position()); |
| 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()) && instructions.is_open(); |
| ++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 (instructions.is_open()) { |
| 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(); |
| } |
| } else { |
| Pop(); |
| } |
| 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(TokenPosition::kNoSource, 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(TokenPosition::kNoSource, 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(TokenPosition::kNoSource, 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(TokenPosition::kNoSource, constructor, 3); |
| body_fragment += Drop(); |
| |
| // Throw the exception |
| body_fragment += PushArgument(); |
| body_fragment += ThrowException(TokenPosition::kNoSource); |
| 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( |
| TokenPosition::kNoSource, 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(TokenPosition::kNoSource, constructor, 5); |
| otherwise_fragment += Drop(); |
| |
| // Throw _AssertionError exception. |
| otherwise_fragment += PushArgument(); |
| otherwise_fragment += ThrowException(TokenPosition::kNoSource); |
| 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_; |
| { |
| TryFinallyBlock tfb(this, node->finalizer()); |
| TryCatchBlock tcb(this, try_handler_index); |
| 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( |
| TokenPosition::kNoSource, |
| 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; |
| |
| // NOTE: This is not TokenPosition in the general sense! |
| 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()); |
| } |
| // NOTE: This is not TokenPosition in the general sense! |
| 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 |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |