| // 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, TokenPosition start_position) { |
| scope_ = new (Z) LocalScope(scope_, depth_.function_, depth_.loop_); |
| scope_->set_begin_token_pos(start_position); |
| ASSERT(node->kernel_offset() >= 0); |
| result_->scopes.Insert(node->kernel_offset(), scope_); |
| } |
| |
| |
| void ScopeBuilder::ExitScope(TokenPosition end_position) { |
| scope_->set_end_token_pos(end_position); |
| scope_ = scope_->parent(); |
| } |
| |
| |
| LocalVariable* ScopeBuilder::MakeVariable(TokenPosition declaration_pos, |
| TokenPosition token_pos, |
| const dart::String& name, |
| const AbstractType& type) { |
| return new (Z) LocalVariable(declaration_pos, token_pos, 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( |
| declaration->position(), declaration->position(), |
| H.DartSymbol(declaration->name()), T.TranslateVariableType(declaration)); |
| if (declaration->IsFinal()) { |
| variable->set_is_final(); |
| } |
| scope_->InsertParameterAt(pos, variable); |
| result_->locals.Insert(declaration->kernel_offset(), 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(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| 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(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| 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->kernel_offset()); |
| 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->kernel_offset(), 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(declaration->position(), declaration->end_position(), name, |
| T.TranslateVariableType(declaration)); |
| if (declaration->IsFinal()) { |
| variable->set_is_final(); |
| } |
| scope_->AddVariable(variable); |
| result_->locals.Insert(declaration->kernel_offset(), 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_class = NULL; |
| DiscoverEnclosingElements(Z, function, &outermost_function, &outermost_node, |
| &kernel_class); |
| // Use [klass]/[kernel_class] as active class. Type parameters will get |
| // resolved via [kernel_class] unless we are nested inside a static factory |
| // in which case we will use [member]. |
| ActiveClassScope active_class_scope(&active_class_, kernel_class, &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); |
| scope_->set_begin_token_pos(function.token_pos()); |
| scope_->set_end_token_pos(function.end_token_pos()); |
| |
| 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( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| 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(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| 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->position()); |
| field->initializer()->AcceptExpressionVisitor(this); |
| ExitScope(field->end_position()); |
| } |
| } |
| } |
| } else if (function.IsFactory()) { |
| LocalVariable* variable = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| 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()) { |
| // TODO(jensj): HACK: Push the begin token to after any parameters to |
| // avoid crash when breaking on definition line of async method in |
| // debugger. It seems that another scope needs to be added |
| // in which captures are made, but I can't make that work. |
| // This 'solution' doesn't crash, but I cannot see the parameters at |
| // that particular breakpoint either. |
| // Also push the end token to after the "}" to avoid crashing on |
| // stepping past the last line (to the "}" character). |
| if (node->body() != NULL && node->body()->position().IsReal()) { |
| scope_->set_begin_token_pos(node->body()->position()); |
| } |
| if (scope_->end_token_pos().IsReal()) { |
| scope_->set_end_token_pos(scope_->end_token_pos().Next()); |
| } |
| 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(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), klass_type); |
| scope_->InsertParameterAt(pos++, variable); |
| result_->this_variable = variable; |
| } |
| if (is_setter) { |
| result_->setter_value = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| 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(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| 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( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| 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, function->position()); |
| current_function_scope_ = scope_; |
| current_function_node_ = function; |
| if (depth_.function_ == 1) { |
| FunctionScope function_scope = {function->kernel_offset(), scope_}; |
| result_->function_scopes.Add(function_scope); |
| } |
| AddParameters(function); |
| VisitFunctionNode(function); |
| ExitScope(function->end_position()); |
| 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->position()); |
| node->VisitChildren(this); |
| ExitScope(node->end_position()); |
| } |
| |
| |
| void ScopeBuilder::VisitBlock(Block* node) { |
| EnterScope(node, node->position()); |
| node->VisitChildren(this); |
| ExitScope(node->end_position()); |
| } |
| |
| |
| 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, node->position()); |
| 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(node->end_position()); |
| } |
| |
| |
| void ScopeBuilder::VisitForInStatement(ForInStatement* node) { |
| node->iterable()->AcceptExpressionVisitor(this); |
| ++depth_.for_in_; |
| AddIteratorVariable(); |
| ++depth_.loop_; |
| EnterScope(node, node->position()); |
| VisitVariableDeclaration(node->variable()); |
| node->body()->AcceptStatementVisitor(this); |
| ExitScope(node->end_position()); |
| --depth_.loop_; |
| --depth_.for_in_; |
| } |
| |
| |
| void ScopeBuilder::AddSwitchVariable() { |
| if ((depth_.function_ == 0) && (result_->switch_variable == NULL)) { |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| 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(TokenPosition::kNoSource, TokenPosition::kNoSource, 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, ketch->position()); |
| if (ketch->exception() != NULL) { |
| VisitVariableDeclaration(ketch->exception()); |
| } |
| if (ketch->stack_trace() != NULL) { |
| VisitVariableDeclaration(ketch->stack_trace()); |
| } |
| ketch->body()->AcceptStatementVisitor(this); |
| ExitScope(ketch->end_position()); |
| } |
| --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 (FLAG_causal_async_stacks && |
| (node->dart_async_marker() == FunctionNode::kAsync || |
| node->dart_async_marker() == FunctionNode::kAsyncStar)) { |
| LocalVariable* asyncStackTraceVar = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::AsyncStackTraceVar(), AbstractType::dynamic_type()); |
| scope_->AddVariable(asyncStackTraceVar); |
| } |
| |
| if (node->async_marker() == FunctionNode::kSyncYielding) { |
| LocalScope* scope = parsed_function_->node_sequence()->scope(); |
| intptr_t offset = parsed_function_->function().num_fixed_parameters(); |
| for (intptr_t i = 0; |
| i < parsed_function_->function().NumOptionalPositionalParameters(); |
| i++) { |
| scope->VariableAt(offset + 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, :async_op and |
| // :async_stack_trace 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); |
| } |
| } |
| if (FLAG_causal_async_stacks) { |
| LocalVariable* temp = |
| scope_->LookupVariable(Symbols::AsyncStackTraceVar(), 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_), |
| try_index_(builder->CurrentTryIndex()) { |
| 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(try_index_); |
| } |
| return destination_; |
| } |
| |
| FlowGraphBuilder* builder_; |
| LabeledStatement* labeled_statement_; |
| BreakableBlock* outer_; |
| JoinEntryInstr* destination_; |
| TryFinallyBlock* outer_finally_; |
| intptr_t context_depth_; |
| intptr_t try_index_; |
| }; |
| |
| |
| 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_), |
| try_index_(builder->CurrentTryIndex()) { |
| 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(try_index_); |
| 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_; |
| intptr_t try_index_; |
| }; |
| |
| |
| 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( |
| CanonicalName* kernel_class) { |
| ASSERT(kernel_class->IsClass()); |
| dart::String& name = DartString(kernel_class->name()); |
| return ManglePrivateName(kernel_class->parent(), &name); |
| } |
| |
| |
| const dart::String& TranslationHelper::DartConstructorName( |
| CanonicalName* constructor) { |
| ASSERT(constructor->IsConstructor()); |
| return DartFactoryName(constructor); |
| } |
| |
| |
| const dart::String& TranslationHelper::DartProcedureName( |
| CanonicalName* procedure) { |
| ASSERT(procedure->IsProcedure()); |
| if (procedure->IsSetter()) { |
| return DartSetterName(procedure); |
| } else if (procedure->IsGetter()) { |
| return DartGetterName(procedure); |
| } else if (procedure->IsFactory()) { |
| return DartFactoryName(procedure); |
| } else { |
| return DartMethodName(procedure); |
| } |
| } |
| |
| |
| const dart::String& TranslationHelper::DartSetterName(CanonicalName* setter) { |
| return DartSetterName(setter->parent(), setter->name()); |
| } |
| |
| |
| const dart::String& TranslationHelper::DartSetterName(Name* setter_name) { |
| return DartSetterName(setter_name->library(), setter_name->string()); |
| } |
| |
| |
| const dart::String& TranslationHelper::DartSetterName(CanonicalName* parent, |
| String* setter) { |
| // The names flowing into [setter] 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. |
| ASSERT(setter->size() > 0); |
| intptr_t skip = 0; |
| if (setter->buffer()[setter->size() - 1] == '=') { |
| skip = 1; |
| } |
| dart::String& name = dart::String::ZoneHandle( |
| Z, dart::String::FromUTF8(setter->buffer(), setter->size() - skip, |
| allocation_space_)); |
| ManglePrivateName(parent, &name, false); |
| name = dart::Field::SetterSymbol(name); |
| return name; |
| } |
| |
| |
| const dart::String& TranslationHelper::DartGetterName(CanonicalName* getter) { |
| return DartGetterName(getter->parent(), getter->name()); |
| } |
| |
| |
| const dart::String& TranslationHelper::DartGetterName(Name* getter_name) { |
| return DartGetterName(getter_name->library(), getter_name->string()); |
| } |
| |
| |
| const dart::String& TranslationHelper::DartGetterName(CanonicalName* parent, |
| String* getter) { |
| dart::String& name = DartString(getter); |
| ManglePrivateName(parent, &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(CanonicalName* method) { |
| return DartMethodName(method->parent(), method->name()); |
| } |
| |
| |
| const dart::String& TranslationHelper::DartMethodName(Name* method_name) { |
| return DartMethodName(method_name->library(), method_name->string()); |
| } |
| |
| |
| const dart::String& TranslationHelper::DartMethodName(CanonicalName* parent, |
| String* method) { |
| dart::String& name = DartString(method); |
| return ManglePrivateName(parent, &name); |
| } |
| |
| |
| const dart::String& TranslationHelper::DartFactoryName(CanonicalName* factory) { |
| ASSERT(factory->IsConstructor() || factory->IsFactory()); |
| GrowableHandlePtrArray<const dart::String> pieces(Z, 3); |
| pieces.Add(DartClassName(factory->EnclosingName())); |
| pieces.Add(Symbols::Dot()); |
| // [DartMethodName] will mangle the name. |
| pieces.Add(DartMethodName(factory)); |
| return dart::String::ZoneHandle( |
| Z, dart::Symbols::FromConcatAll(thread_, pieces)); |
| } |
| |
| |
| dart::RawLibrary* TranslationHelper::LookupLibraryByKernelLibrary( |
| CanonicalName* kernel_library) { |
| // We only use the name and don't rely on having any particular parent. This |
| // ASSERT is just a sanity check. |
| ASSERT(kernel_library->IsLibrary() || |
| kernel_library->parent()->IsAdministrative()); |
| const dart::String& library_name = DartSymbol(kernel_library->name()); |
| ASSERT(!library_name.IsNull()); |
| dart::RawLibrary* library = |
| dart::Library::LookupLibrary(thread_, library_name); |
| ASSERT(library != Object::null()); |
| return library; |
| } |
| |
| |
| dart::RawClass* TranslationHelper::LookupClassByKernelClass( |
| CanonicalName* kernel_class) { |
| ASSERT(kernel_class->IsClass()); |
| dart::RawClass* klass = NULL; |
| const dart::String& class_name = DartClassName(kernel_class); |
| CanonicalName* kernel_library = kernel_class->parent(); |
| dart::Library& library = |
| dart::Library::Handle(Z, LookupLibraryByKernelLibrary(kernel_library)); |
| klass = library.LookupClassAllowPrivate(class_name); |
| |
| ASSERT(klass != Object::null()); |
| return klass; |
| } |
| |
| |
| dart::RawField* TranslationHelper::LookupFieldByKernelField( |
| CanonicalName* kernel_field) { |
| ASSERT(kernel_field->IsField()); |
| CanonicalName* enclosing = kernel_field->EnclosingName(); |
| |
| dart::Class& klass = dart::Class::Handle(Z); |
| if (enclosing->IsLibrary()) { |
| dart::Library& library = |
| dart::Library::Handle(Z, LookupLibraryByKernelLibrary(enclosing)); |
| klass = library.toplevel_class(); |
| } else { |
| ASSERT(enclosing->IsClass()); |
| klass = LookupClassByKernelClass(enclosing); |
| } |
| dart::RawField* field = |
| klass.LookupFieldAllowPrivate(DartSymbol(kernel_field->name())); |
| ASSERT(field != Object::null()); |
| return field; |
| } |
| |
| |
| dart::RawFunction* TranslationHelper::LookupStaticMethodByKernelProcedure( |
| CanonicalName* procedure) { |
| 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). |
| CanonicalName* enclosing = procedure->EnclosingName(); |
| if (enclosing->IsLibrary()) { |
| dart::Library& library = |
| dart::Library::Handle(Z, LookupLibraryByKernelLibrary(enclosing)); |
| dart::RawFunction* function = |
| library.LookupFunctionAllowPrivate(procedure_name); |
| ASSERT(function != Object::null()); |
| return function; |
| } else { |
| ASSERT(enclosing->IsClass()); |
| dart::Class& klass = |
| dart::Class::Handle(Z, LookupClassByKernelClass(enclosing)); |
| 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(); |
| } |
| } |
| |
| |
| dart::RawFunction* TranslationHelper::LookupConstructorByKernelConstructor( |
| CanonicalName* constructor) { |
| ASSERT(constructor->IsConstructor()); |
| dart::Class& klass = dart::Class::Handle( |
| Z, LookupClassByKernelClass(constructor->EnclosingName())); |
| return LookupConstructorByKernelConstructor(klass, constructor); |
| } |
| |
| |
| dart::RawFunction* TranslationHelper::LookupConstructorByKernelConstructor( |
| const dart::Class& owner, |
| CanonicalName* constructor) { |
| ASSERT(constructor->IsConstructor()); |
| 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); |
| // 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(CanonicalName* parent, |
| 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(parent)); |
| *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_(Script::Handle( |
| zone, |
| builder == NULL ? Script::null() |
| : builder_->parsed_function_->function().script())), |
| result_(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 = H.thread(); |
| 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) { |
| if (builder_ == NULL) return false; |
| |
| 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()); |
| |
| if (builder_ == NULL) return; |
| |
| 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) { |
| CanonicalName* target = node->target(); |
| if (target->IsField()) { |
| const dart::Field& field = |
| dart::Field::Handle(Z, H.LookupFieldByKernelField(target)); |
| 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 (target->IsProcedure()) { |
| const Function& function = |
| Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target)); |
| |
| if (target->IsMethod()) { |
| Function& closure_function = |
| Function::ZoneHandle(Z, function.ImplicitClosureFunction()); |
| closure_function.set_kernel_function(function.kernel_function()); |
| result_ = closure_function.ImplicitStaticClosure(); |
| result_ = H.Canonicalize(result_); |
| } else if (target->IsGetter()) { |
| 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) { |
| if (EvaluateBooleanExpression(node->condition())) { |
| EvaluateExpression(node->then()); |
| } else { |
| EvaluateExpression(node->otherwise()); |
| } |
| } |
| |
| |
| void ConstantEvaluator::VisitLogicalExpression(LogicalExpression* node) { |
| if (node->op() == LogicalExpression::kAnd) { |
| if (EvaluateBooleanExpression(node->left())) { |
| EvaluateBooleanExpression(node->right()); |
| } |
| } else { |
| ASSERT(node->op() == LogicalExpression::kOr); |
| if (!EvaluateBooleanExpression(node->left())) { |
| EvaluateBooleanExpression(node->right()); |
| } |
| } |
| } |
| |
| |
| void ConstantEvaluator::VisitNot(Not* node) { |
| result_ ^= Bool::Get(!EvaluateBooleanExpression(node->expression())).raw(); |
| } |
| |
| |
| void ConstantEvaluator::VisitPropertyGet(PropertyGet* node) { |
| const intptr_t kLengthLen = sizeof("length") - 1; |
| |
| String* string = node->name()->string(); |
| if ((string->size() == kLengthLen) && |
| (memcmp(string->buffer(), "length", kLengthLen) == 0)) { |
| node->receiver()->AcceptExpressionVisitor(this); |
| if (result_.IsString()) { |
| const dart::String& str = |
| dart::String::Handle(Z, dart::String::RawCast(result_.raw())); |
| result_ = Integer::New(str.Length()); |
| } else { |
| H.ReportError( |
| "Constant expressions can only call " |
| "'length' on string constants."); |
| } |
| } else { |
| VisitDefaultExpression(node); |
| } |
| } |
| |
| |
| 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) |
| : translation_helper_(Thread::Current()), |
| zone_(translation_helper_.zone()), |
| 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->kernel_offset())->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->kernel_offset())->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(TokenPosition::kNoSource, |
| 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(TokenPosition::kNoSource, Context::parent_offset()); |
| instructions += StoreLocal(TokenPosition::kNoSource, |
| 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::InstantiateType(const AbstractType& type) { |
| InstantiateTypeInstr* instr = new (Z) |
| InstantiateTypeInstr(TokenPosition::kNoSource, type, Pop(), |
| NULL); // TODO(regis): Pop function type arguments. |
| Push(instr); |
| return Fragment(instr); |
| } |
| |
| |
| Fragment FlowGraphBuilder::InstantiateTypeArguments( |
| const TypeArguments& type_arguments) { |
| InstantiateTypeArgumentsInstr* instr = new (Z) InstantiateTypeArgumentsInstr( |
| TokenPosition::kNoSource, type_arguments, *active_class_.klass, Pop(), |
| NULL); // TODO(regis): Pop function type arguments. |
| 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, |
| bool needs_stacktrace) { |
| ASSERT(CurrentException()->is_captured() == |
| CurrentStackTrace()->is_captured()); |
| const bool should_restore_closure_context = |
| CurrentException()->is_captured() || CurrentCatchContext()->is_captured(); |
| CatchBlockEntryInstr* entry = new (Z) CatchBlockEntryInstr( |
| TokenPosition::kNoSource, // Token position of catch block. |
| false, // Not an artifact of compilation. |
| AllocateBlockId(), CurrentTryIndex(), graph_entry_, handler_types, |
| handler_index, *CurrentException(), *CurrentStackTrace(), |
| needs_stacktrace, H.thread()->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(TokenPosition::kNoSource, |
| 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(TokenPosition::kNoSource, 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(TokenPosition::kNoSource, 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(TokenPosition position, |
| int catch_try_index) { |
| Fragment instructions; |
| instructions += Drop(); |
| instructions += Drop(); |
| instructions += |
| Fragment(new (Z) ReThrowInstr(position, 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, parsed_function_); |
| 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(TokenPosition position) { |
| Fragment instructions; |
| |
| instructions += CheckReturnTypeInCheckedMode(); |
| |
| Value* value = Pop(); |
| ASSERT(stack_ == NULL); |
| |
| const Function& function = parsed_function_->function(); |
| if (NeedsDebugStepCheck(function, position)) { |
| instructions += DebugStepCheck(position); |
| } |
| |
| if (FLAG_causal_async_stacks && |
| (function.IsAsyncClosure() || function.IsAsyncGenClosure())) { |
| // We are returning from an asynchronous closure. Before we do that, be |
| // sure to clear the thread's asynchronous stack trace. |
| const Function& target = Function::ZoneHandle( |
| Z, I->object_store()->async_clear_thread_stack_trace()); |
| ASSERT(!target.IsNull()); |
| instructions += StaticCall(TokenPosition::kNoSource, target, 0); |
| instructions += Drop(); |
| } |
| |
| ReturnInstr* return_instr = new (Z) ReturnInstr(position, value); |
| if (exit_collector_ != NULL) exit_collector_->AddExit(return_instr); |
| |
| instructions <<= return_instr; |
| |
| return instructions.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, |
| bool is_initialization_store, |
| StoreBarrierType emit_store_barrier) { |
| Fragment instructions; |
| |
| const AbstractType& dst_type = AbstractType::ZoneHandle(Z, field.type()); |
| instructions += CheckAssignableInCheckedMode( |
| dst_type, dart::String::ZoneHandle(Z, field.name())); |
| |
| 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); |
| store->set_is_initialization(is_initialization_store); |
| instructions <<= store; |
| |
| return instructions; |
| } |
| |
| |
| Fragment FlowGraphBuilder::StoreInstanceFieldGuarded( |
| const dart::Field& field, |
| bool is_initialization_store) { |
| Fragment instructions; |
| const dart::Field& field_clone = MayCloneField(Z, field); |
| if (I->use_field_guards()) { |
| LocalVariable* store_expression = MakeTemporary(); |
| instructions += LoadLocal(store_expression); |
| instructions += GuardFieldClass(field_clone, H.thread()->GetNextDeoptId()); |
| instructions += LoadLocal(store_expression); |
| instructions += GuardFieldLength(field_clone, H.thread()->GetNextDeoptId()); |
| } |
| instructions += StoreInstanceField(field_clone, is_initialization_store); |
| return instructions; |
| } |
| |
| |
| Fragment FlowGraphBuilder::StoreInstanceField( |
| TokenPosition position, |
| 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, position); |
| return Fragment(store); |
| } |
| |
| |
| Fragment FlowGraphBuilder::StoreLocal(TokenPosition position, |
| LocalVariable* variable) { |
| Fragment instructions; |
| if (variable->is_captured()) { |
| LocalVariable* value = MakeTemporary(); |
| instructions += LoadContextAt(variable->owner()->context_level()); |
| instructions += LoadLocal(value); |
| instructions += StoreInstanceField( |
| position, Context::variable_offset(variable->index())); |
| } else { |
| Value* value = Pop(); |
| StoreLocalInstr* store = |
| new (Z) StoreLocalInstr(*variable, value, position); |
| instructions <<= store; |
| Push(store); |
| } |
| return instructions; |
| } |
| |
| |
| Fragment FlowGraphBuilder::StoreStaticField(TokenPosition position, |
| const dart::Field& field) { |
| return Fragment( |
| new (Z) StoreStaticFieldInstr(MayCloneField(Z, field), Pop(), position)); |
| } |
| |
| |
| Fragment FlowGraphBuilder::StringInterpolate(TokenPosition position) { |
| Value* array = Pop(); |
| StringInterpolateInstr* interpolate = |
| new (Z) StringInterpolateInstr(array, position); |
| Push(interpolate); |
| return Fragment(interpolate); |
| } |
| |
| |
| Fragment FlowGraphBuilder::StringInterpolateSingle(TokenPosition position) { |
| const int kNumberOfArguments = 1; |
| const Array& kNoArgumentNames = Object::null_array(); |
| const dart::Class& cls = dart::Class::Handle( |
| dart::Library::LookupCoreClass(Symbols::StringBase())); |
| ASSERT(!cls.IsNull()); |
| const Function& function = dart::Function::ZoneHandle( |
| Z, dart::Resolver::ResolveStatic(cls, dart::Library::PrivateCoreLibName( |
| Symbols::InterpolateSingle()), |
| kNumberOfArguments, kNoArgumentNames)); |
| Fragment instructions; |
| instructions += PushArgument(); |
| instructions += StaticCall(position, function, 1); |
| return instructions; |
| } |
| |
| |
| Fragment FlowGraphBuilder::ThrowTypeError() { |
| const dart::Class& klass = dart::Class::ZoneHandle( |
| Z, dart::Library::LookupCoreClass(Symbols::TypeError())); |
| ASSERT(!klass.IsNull()); |
| const dart::Function& constructor = dart:: |