| // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "vm/compiler/frontend/scope_builder.h" |
| |
| #include "vm/compiler/api/print_filter.h" |
| #include "vm/compiler/backend/il.h" // For CompileType. |
| #include "vm/compiler/frontend/kernel_to_il.h" |
| #include "vm/compiler/frontend/kernel_translation_helper.h" |
| #include "vm/flags.h" |
| |
| namespace dart { |
| namespace kernel { |
| |
| #define Z (zone_) |
| #define H (translation_helper_) |
| #define T (type_translator_) |
| #define I Isolate::Current() |
| #define IG IsolateGroup::Current() |
| |
| ScopeBuilder::ScopeBuilder(ParsedFunction* parsed_function) |
| : result_(nullptr), |
| parsed_function_(parsed_function), |
| translation_helper_(Thread::Current()), |
| zone_(translation_helper_.zone()), |
| current_function_scope_(nullptr), |
| scope_(nullptr), |
| depth_(0), |
| needs_expr_temp_(false), |
| helper_( |
| zone_, |
| &translation_helper_, |
| TypedDataView::Handle(Z, parsed_function->function().KernelLibrary()), |
| parsed_function->function().KernelLibraryOffset()), |
| constant_reader_(&helper_, &active_class_), |
| type_translator_(&helper_, |
| &constant_reader_, |
| &active_class_, |
| /*finalize=*/true), |
| inferred_type_metadata_helper_(&helper_, |
| &constant_reader_, |
| &type_translator_), |
| inferred_arg_type_metadata_helper_( |
| &helper_, |
| &constant_reader_, |
| &type_translator_, |
| InferredTypeMetadataHelper::Kind::ArgType), |
| procedure_attributes_metadata_helper_(&helper_) { |
| const auto& kernel_program_info = KernelProgramInfo::Handle( |
| Z, parsed_function->function().KernelProgramInfo()); |
| H.InitFromKernelProgramInfo(kernel_program_info); |
| ASSERT(type_translator_.active_class_ == &active_class_); |
| } |
| |
| ScopeBuildingResult* ScopeBuilder::BuildScopes() { |
| if (result_ != nullptr) return result_; |
| |
| ASSERT(scope_ == nullptr && depth_.loop_ == 0 && depth_.function_ == 0); |
| result_ = new (Z) ScopeBuildingResult(); |
| |
| const Function& function = parsed_function_->function(); |
| |
| // Setup an [ActiveClassScope] and an [ActiveMemberScope] which will be used |
| // e.g. for type translation. |
| const Class& klass = Class::Handle(Z, function.Owner()); |
| |
| Function& outermost_function = |
| Function::Handle(Z, function.GetOutermostFunction()); |
| |
| ActiveClassScope active_class_scope(&active_class_, &klass); |
| ActiveMemberScope active_member(&active_class_, &outermost_function); |
| FunctionType& signature = FunctionType::Handle(Z, function.signature()); |
| ActiveTypeParametersScope active_type_params(&active_class_, function, |
| &signature, Z); |
| |
| LocalScope* enclosing_scope = nullptr; |
| if (function.IsImplicitClosureFunction() && !function.is_static()) { |
| // Create artificial enclosing scope for the tear-off that contains |
| // captured receiver value. This ensure that AssertAssignable will correctly |
| // load instantiator type arguments if they are needed. |
| Class& klass = Class::Handle(Z, function.Owner()); |
| Type& klass_type = H.GetDeclarationType(klass); |
| LocalVariable* receiver_variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), klass_type); |
| parsed_function_->set_receiver_var(receiver_variable); |
| receiver_variable->set_is_captured(); |
| enclosing_scope = new (Z) LocalScope(nullptr, 0, 0); |
| enclosing_scope->set_context_level(0); |
| enclosing_scope->AddVariable(receiver_variable); |
| enclosing_scope->AddContextVariable(receiver_variable); |
| } else if (function.HasParent()) { |
| 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()); |
| |
| if (function.IsSuspendableFunction()) { |
| LocalVariable* suspend_state_var = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::SuspendStateVar(), AbstractType::dynamic_type()); |
| suspend_state_var->set_invisible(true); |
| scope_->AddVariable(suspend_state_var); |
| parsed_function_->set_suspend_state_var(suspend_state_var); |
| } |
| |
| // Add function type arguments variable before current context variable. |
| if (function.IsGeneric() || function.HasGenericParent()) { |
| LocalVariable* type_args_var = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::FunctionTypeArgumentsVar(), AbstractType::dynamic_type()); |
| scope_->AddVariable(type_args_var); |
| parsed_function_->set_function_type_arguments(type_args_var); |
| } |
| |
| if (parsed_function_->has_arg_desc_var()) { |
| scope_->AddVariable(parsed_function_->arg_desc_var()); |
| } |
| |
| LocalVariable* context_var = parsed_function_->current_context_var(); |
| scope_->AddVariable(context_var); |
| |
| parsed_function_->set_scope(scope_); |
| |
| ProcedureAttributesMetadata attrs; |
| |
| if (!function.IsNoSuchMethodDispatcher() && |
| !function.IsInvokeFieldDispatcher() && |
| !function.IsFfiCallbackTrampoline()) { |
| helper_.SetOffset(function.kernel_offset()); |
| attrs = procedure_attributes_metadata_helper_.GetProcedureAttributes( |
| function.kernel_offset()); |
| } |
| |
| FunctionNodeHelper function_node_helper(&helper_); |
| |
| switch (function.kind()) { |
| case UntaggedFunction::kImplicitClosureFunction: { |
| const auto& parent = Function::Handle(Z, function.parent_function()); |
| const auto& target = |
| Function::Handle(Z, function.ImplicitClosureTarget(Z)); |
| |
| // For BuildGraphOfNoSuchMethodForwarder, since closures no longer |
| // require arg_desc_var in all cases. |
| if (target.IsNull() || |
| (parent.num_fixed_parameters() != target.num_fixed_parameters())) { |
| needs_expr_temp_ = true; |
| } |
| } |
| FALL_THROUGH; |
| case UntaggedFunction::kClosureFunction: |
| case UntaggedFunction::kRegularFunction: |
| case UntaggedFunction::kGetterFunction: |
| case UntaggedFunction::kSetterFunction: |
| case UntaggedFunction::kConstructor: { |
| const Tag tag = helper_.PeekTag(); |
| helper_.ReadUntilFunctionNode(); |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| // NOTE: FunctionNode is read further below the if. |
| |
| if (function.is_ffi_native() || function.IsFfiCallClosure()) { |
| needs_expr_temp_ = true; |
| // Calls with handles need try/catch variables. |
| if (function.FfiCSignatureContainsHandles()) { |
| ++depth_.try_; |
| AddTryVariables(); |
| --depth_.try_; |
| ++depth_.catch_; |
| AddCatchVariables(); |
| FinalizeCatchVariables(); |
| --depth_.catch_; |
| } |
| } |
| if (FlowGraphBuilder::IsRecognizedMethodForFlowGraph(function) && |
| FlowGraphBuilder::IsExpressionTempVarUsedInRecognizedMethodFlowGraph( |
| function)) { |
| needs_expr_temp_ = true; |
| } |
| intptr_t pos = 0; |
| if (function.IsClosureFunction()) { |
| LocalVariable* closure_parameter = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::ClosureParameter(), AbstractType::dynamic_type()); |
| scope_->InsertParameterAt(pos++, closure_parameter); |
| } else if (!function.is_static()) { |
| // We use [is_static] instead of [IsStaticFunction] because the latter |
| // returns `false` for constructors. |
| Class& klass = Class::Handle(Z, function.Owner()); |
| Type& klass_type = H.GetDeclarationType(klass); |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), klass_type); |
| scope_->InsertParameterAt(pos++, variable); |
| parsed_function_->set_receiver_var(variable); |
| |
| // We visit instance field initializers because they might contain |
| // [Let] expressions and we need to have a mapping. |
| if (tag == kConstructor) { |
| Class& parent_class = Class::Handle(Z, function.Owner()); |
| Array& class_fields = Array::Handle(Z, parent_class.fields()); |
| Field& class_field = Field::Handle(Z); |
| for (intptr_t i = 0; i < class_fields.Length(); ++i) { |
| class_field ^= class_fields.At(i); |
| if (!class_field.is_static()) { |
| const auto& kernel_data = |
| TypedDataView::Handle(Z, class_field.KernelLibrary()); |
| ASSERT(!kernel_data.IsNull()); |
| intptr_t field_offset = class_field.kernel_offset(); |
| AlternativeReadingScopeWithNewData alt( |
| &helper_.reader_, &kernel_data, field_offset); |
| FieldHelper field_helper(&helper_); |
| field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
| Tag initializer_tag = |
| helper_.ReadTag(); // read first part of initializer. |
| if (initializer_tag == kSomething) { |
| EnterScope(field_offset); |
| VisitExpression(); // read initializer. |
| ExitScope(field_helper.position_, field_helper.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; |
| } |
| |
| ParameterTypeCheckMode type_check_mode = |
| kTypeCheckForNonDynamicallyInvokedMethod; |
| if (function.is_static()) { |
| // In static functions we don't check anything. |
| type_check_mode = kTypeCheckForStaticFunction; |
| } else if (function.IsImplicitClosureFunction()) { |
| // All non-covariant checks are either performed by the type system, |
| // or by a dynamic closure call dispatcher/mirror if dynamically |
| // invoked. For covariant checks, static targets never have covariant |
| // arguments and dynamic targets do their own covariant checking. |
| // Thus, implicit closure functions perform no checking internally. |
| type_check_mode = kTypeCheckForImplicitClosureFunction; |
| } |
| |
| // Continue reading FunctionNode: |
| // read positional_parameters and named_parameters. |
| AddPositionalAndNamedParameters(pos, type_check_mode, attrs); |
| |
| if (function.IsSuspendableFunction()) { |
| // Read return type which is used to create a result of |
| // async/async*/sync* function. It may reference receiver or type |
| // arguments of the enclosing function which need to be captured. |
| VisitDartType(); |
| |
| // Visit optional future value type. |
| if (helper_.ReadTag() == kSomething) { |
| VisitDartType(); |
| } |
| } |
| |
| // We generate a synthetic body for implicit closure functions - which |
| // will forward the call to the real function. |
| // -> see BuildGraphOfImplicitClosureFunction |
| if (!function.IsImplicitClosureFunction()) { |
| helper_.SetOffset(function.kernel_offset()); |
| first_body_token_position_ = TokenPosition::kNoSource; |
| VisitNode(); |
| |
| // 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 (first_body_token_position_.IsReal()) { |
| scope_->set_begin_token_pos(first_body_token_position_); |
| } |
| if (scope_->end_token_pos().IsReal()) { |
| scope_->set_end_token_pos(scope_->end_token_pos().Next()); |
| } |
| } |
| break; |
| } |
| case UntaggedFunction::kImplicitGetter: |
| case UntaggedFunction::kImplicitSetter: { |
| ASSERT(helper_.PeekTag() == kField); |
| const bool is_setter = function.IsImplicitSetterFunction(); |
| const bool is_method = !function.IsStaticFunction(); |
| const auto& field = Field::Handle(Z, function.accessor_field()); |
| intptr_t pos = 0; |
| if (is_method) { |
| Class& klass = Class::Handle(Z, function.Owner()); |
| Type& klass_type = H.GetDeclarationType(klass); |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), klass_type); |
| scope_->InsertParameterAt(pos++, variable); |
| parsed_function_->set_receiver_var(variable); |
| } |
| if (is_setter) { |
| if (CompilerState::Current().is_aot()) { |
| const intptr_t kernel_offset = field.kernel_offset(); |
| const InferredTypeMetadata inferred_field_type = |
| inferred_type_metadata_helper_.GetInferredType(kernel_offset); |
| result_->setter_value = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::Value(), |
| AbstractType::ZoneHandle(Z, function.ParameterTypeAt(pos)), |
| LocalVariable::kNoKernelOffset, /*is_late=*/false, |
| /*inferred_type_md=*/nullptr, |
| /*inferred_arg_type_md=*/field.is_covariant() |
| ? nullptr |
| : &inferred_field_type); |
| } else { |
| result_->setter_value = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::Value(), |
| AbstractType::ZoneHandle(Z, function.ParameterTypeAt(pos))); |
| } |
| scope_->InsertParameterAt(pos++, result_->setter_value); |
| |
| if (is_method) { |
| if (field.is_covariant()) { |
| result_->setter_value->set_is_explicit_covariant_parameter(); |
| } else if (!field.is_generic_covariant_impl() || |
| (!attrs.has_non_this_uses && !attrs.has_tearoff_uses)) { |
| result_->setter_value->set_type_check_mode( |
| LocalVariable::kTypeCheckedByCaller); |
| } |
| } |
| } |
| break; |
| } |
| case UntaggedFunction::kImplicitStaticGetter: { |
| ASSERT(helper_.PeekTag() == kField); |
| ASSERT(function.IsStaticFunction()); |
| // In addition to static field initializers, scopes/local variables |
| // are needed for implicit getters of static const fields, in order to |
| // be able to evaluate their initializers in constant evaluator. |
| if (Field::Handle(Z, function.accessor_field()).is_const()) { |
| VisitNode(); |
| } |
| break; |
| } |
| case UntaggedFunction::kFieldInitializer: { |
| ASSERT(helper_.PeekTag() == kField); |
| if (!function.is_static()) { |
| Class& klass = Class::Handle(Z, function.Owner()); |
| Type& klass_type = H.GetDeclarationType(klass); |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), klass_type); |
| scope_->InsertParameterAt(0, variable); |
| parsed_function_->set_receiver_var(variable); |
| } |
| VisitNode(); |
| break; |
| } |
| case UntaggedFunction::kDynamicInvocationForwarder: { |
| const String& name = String::Handle(Z, function.name()); |
| ASSERT(Function::IsDynamicInvocationForwarderName(name)); |
| |
| const auto& target = Function::ZoneHandle(Z, function.ForwardingTarget()); |
| ASSERT(!target.IsNull()); |
| |
| if (FlowGraphBuilder::IsRecognizedMethodForFlowGraph(function) && |
| FlowGraphBuilder::IsExpressionTempVarUsedInRecognizedMethodFlowGraph( |
| function)) { |
| needs_expr_temp_ = true; |
| } |
| |
| if (helper_.PeekTag() == kField) { |
| // Create [this] variable. |
| const Class& klass = Class::Handle(Z, function.Owner()); |
| parsed_function_->set_receiver_var( |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), H.GetDeclarationType(klass))); |
| scope_->InsertParameterAt(0, parsed_function_->receiver_var()); |
| |
| // Create setter value variable. |
| if (target.IsImplicitSetterFunction()) { |
| result_->setter_value = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::Value(), |
| AbstractType::ZoneHandle(Z, function.ParameterTypeAt(1))); |
| scope_->InsertParameterAt(1, result_->setter_value); |
| } |
| break; |
| } |
| |
| // We do not create dyn:* forwarders for method extractors, since those |
| // can never return unboxed values (they return a closure). |
| ASSERT(!target.IsMethodExtractor()); |
| |
| helper_.ReadUntilFunctionNode(); |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| |
| // Create [this] variable. |
| intptr_t pos = 0; |
| Class& klass = Class::Handle(Z, function.Owner()); |
| parsed_function_->set_receiver_var( |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), H.GetDeclarationType(klass))); |
| scope_->InsertParameterAt(pos++, parsed_function_->receiver_var()); |
| |
| // Create all positional and named parameters. |
| AddPositionalAndNamedParameters( |
| pos, kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod, |
| attrs); |
| break; |
| } |
| case UntaggedFunction::kMethodExtractor: { |
| // Add a receiver parameter. Though it is captured, we emit code to |
| // explicitly copy it to a freshly-allocated closure. |
| // Therefore, it isn't necessary to mark it as captured here. |
| Class& klass = Class::Handle(Z, function.Owner()); |
| Type& klass_type = H.GetDeclarationType(klass); |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), klass_type); |
| scope_->InsertParameterAt(0, variable); |
| parsed_function_->set_receiver_var(variable); |
| break; |
| } |
| case UntaggedFunction::kFfiTrampoline: { |
| needs_expr_temp_ = true; |
| // Callbacks need try/catch variables. |
| ++depth_.try_; |
| AddTryVariables(); |
| --depth_.try_; |
| ++depth_.catch_; |
| AddCatchVariables(); |
| FinalizeCatchVariables(); |
| --depth_.catch_; |
| FALL_THROUGH; |
| } |
| case UntaggedFunction::kInvokeFieldDispatcher: { |
| if (function.IsDynamicClosureCallDispatcher()) { |
| auto const vars = parsed_function_->EnsureDynamicClosureCallVars(); |
| ASSERT(vars != nullptr); |
| #define ADD_VAR(Name, _, __) scope_->AddVariable(vars->Name); |
| FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(ADD_VAR); |
| #undef ADD_VAR |
| for (auto const& v : vars->named_argument_parameter_indices) { |
| scope_->AddVariable(v); |
| } |
| } |
| } |
| FALL_THROUGH; |
| case UntaggedFunction::kNoSuchMethodDispatcher: { |
| for (intptr_t i = 0; i < function.NumParameters(); ++i) { |
| LocalVariable* variable = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| String::ZoneHandle(Z, function.ParameterNameAt(i)), |
| AbstractType::ZoneHandle(Z, function.IsFfiCallbackTrampoline() |
| ? function.ParameterTypeAt(i) |
| : Object::dynamic_type().ptr())); |
| bool added = scope_->InsertParameterAt(i, variable); |
| ASSERT(added); |
| } |
| break; |
| } |
| case UntaggedFunction::kRecordFieldGetter: { |
| needs_expr_temp_ = true; |
| // Add a receiver parameter. |
| Class& klass = Class::Handle(Z, function.Owner()); |
| parsed_function_->set_receiver_var( |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), H.GetDeclarationType(klass))); |
| scope_->InsertParameterAt(0, parsed_function_->receiver_var()); |
| break; |
| } |
| case UntaggedFunction::kIrregexpFunction: |
| UNREACHABLE(); |
| } |
| if (needs_expr_temp_) { |
| parsed_function_->EnsureExpressionTemp(); |
| } |
| if (parsed_function_->has_expression_temp_var()) { |
| scope_->AddVariable(parsed_function_->expression_temp_var()); |
| } |
| if (parsed_function_->function().MayHaveUncheckedEntryPoint()) { |
| scope_->AddVariable(parsed_function_->EnsureEntryPointsTemp()); |
| } |
| |
| parsed_function_->AllocateVariables(); |
| |
| // :suspend_state variable should be allocated to a fixed location in |
| // the stack frame. |
| RELEASE_ASSERT((parsed_function_->suspend_state_var() == nullptr) || |
| (parsed_function_->suspend_state_var()->index().value() == |
| SuspendState::kSuspendStateVarIndex)); |
| |
| #if defined(DEBUG) |
| if (FLAG_print_scopes && compiler::PrintFilter::ShouldPrint(function)) { |
| THR_Print("===== Scopes for %s\n", function.ToFullyQualifiedCString()); |
| THR_Print("%s", result_->ToCString()); |
| THR_Print("=====\n"); |
| } |
| #endif |
| |
| return result_; |
| } |
| |
| void ScopeBuildingResult::PrintTo(BaseTextBuffer* f) const { |
| f->AddString("== function scopes:\n"); |
| for (int i = 0; i < function_scopes.length(); i++) { |
| auto scope = function_scopes[i]; |
| scope.scope->PrintTo(f); |
| } |
| |
| f->AddString("== all scopes, indexed by kernel_offset:\n"); |
| auto it = scopes.GetIterator(); |
| while (auto scope = it.Next()) { |
| f->Printf("%" Pd ": ", scope->key); |
| scope->value->PrintTo(f); |
| } |
| |
| f->AddString("== all variables:\n"); |
| auto it2 = locals.GetIterator(); |
| while (auto local = it2.Next()) { |
| local->value->PrintTo(f); |
| } |
| } |
| |
| const char* ScopeBuildingResult::ToCString() const { |
| char buffer[1024 * 16]; |
| BufferFormatter f(buffer, sizeof(buffer)); |
| PrintTo(&f); |
| return Thread::Current()->zone()->MakeCopyOfString(buffer); |
| } |
| |
| void ScopeBuilder::ReportUnexpectedTag(const char* variant, Tag tag) { |
| const auto& script = Script::Handle(Z, Script()); |
| H.ReportError(script, TokenPosition::kNoSource, |
| "Unexpected tag %d (%s) in %s, expected %s", tag, |
| Reader::TagName(tag), |
| parsed_function_->function().ToQualifiedCString(), variant); |
| } |
| |
| void ScopeBuilder::VisitNode() { |
| Tag tag = helper_.PeekTag(); |
| switch (tag) { |
| case kConstructor: |
| VisitConstructor(); |
| return; |
| case kProcedure: |
| VisitProcedure(); |
| return; |
| case kField: |
| VisitField(); |
| return; |
| case kFunctionNode: |
| VisitFunctionNode(); |
| return; |
| default: |
| UNIMPLEMENTED(); |
| return; |
| } |
| } |
| |
| void ScopeBuilder::VisitConstructor() { |
| // 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. |
| ConstructorHelper constructor_helper(&helper_); |
| constructor_helper.ReadUntilExcluding(ConstructorHelper::kFunction); |
| { |
| const Function& function = parsed_function_->function(); |
| Class& parent_class = Class::Handle(Z, function.Owner()); |
| Array& class_fields = Array::Handle(Z, parent_class.fields()); |
| Field& class_field = Field::Handle(Z); |
| for (intptr_t i = 0; i < class_fields.Length(); ++i) { |
| class_field ^= class_fields.At(i); |
| if (!class_field.is_static()) { |
| const auto& kernel_data = |
| TypedDataView::Handle(Z, class_field.KernelLibrary()); |
| ASSERT(!kernel_data.IsNull()); |
| intptr_t field_offset = class_field.kernel_offset(); |
| AlternativeReadingScopeWithNewData alt(&helper_.reader_, &kernel_data, |
| field_offset); |
| FieldHelper field_helper(&helper_); |
| field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
| Tag initializer_tag = helper_.ReadTag(); |
| if (initializer_tag == kSomething) { |
| VisitExpression(); // read initializer. |
| } |
| } |
| } |
| } |
| |
| // Visit children (note that there's no reason to visit the name). |
| VisitFunctionNode(); |
| intptr_t list_length = |
| helper_.ReadListLength(); // read initializers list length. |
| for (intptr_t i = 0; i < list_length; i++) { |
| VisitInitializer(); |
| } |
| } |
| |
| void ScopeBuilder::VisitProcedure() { |
| ProcedureHelper procedure_helper(&helper_); |
| procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction); |
| VisitFunctionNode(); |
| } |
| |
| void ScopeBuilder::VisitField() { |
| FieldHelper field_helper(&helper_); |
| field_helper.ReadUntilExcluding(FieldHelper::kType); |
| VisitDartType(); // read type. |
| Tag tag = helper_.ReadTag(); // read initializer (part 1). |
| if (tag == kSomething) { |
| VisitExpression(); // read initializer (part 2). |
| } |
| } |
| |
| void ScopeBuilder::VisitFunctionNode() { |
| FunctionNodeHelper function_node_helper(&helper_); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
| |
| intptr_t list_length = |
| helper_.ReadListLength(); // read type_parameters list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| TypeParameterHelper helper(&helper_); |
| helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound); |
| VisitDartType(); // read ith bound. |
| helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kDefaultType); |
| VisitDartType(); // read ith default type. |
| helper.Finish(); |
| } |
| function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters); |
| |
| // Read (but don't visit) the positional and named parameters, because they've |
| // already been added to the scope. |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody); |
| |
| if (helper_.ReadTag() == kSomething) { |
| PositionScope scope(&helper_.reader_); |
| VisitStatement(); // Read body |
| first_body_token_position_ = helper_.reader_.min_position(); |
| } |
| } |
| |
| void ScopeBuilder::VisitInitializer() { |
| Tag tag = helper_.ReadTag(); |
| helper_.ReadByte(); // read isSynthetic flag. |
| switch (tag) { |
| case kInvalidInitializer: |
| return; |
| case kFieldInitializer: |
| helper_.ReadPosition(); // read position. |
| helper_.SkipCanonicalNameReference(); // read field_reference. |
| VisitExpression(); // read value. |
| return; |
| case kSuperInitializer: |
| helper_.ReadPosition(); // read position. |
| helper_.SkipCanonicalNameReference(); // read target_reference. |
| VisitArguments(); // read arguments. |
| return; |
| case kRedirectingInitializer: |
| helper_.ReadPosition(); // read position. |
| helper_.SkipCanonicalNameReference(); // read target_reference. |
| VisitArguments(); // read arguments. |
| return; |
| case kLocalInitializer: |
| VisitVariableDeclaration(); // read variable. |
| return; |
| case kAssertInitializer: |
| VisitStatement(); |
| return; |
| default: |
| ReportUnexpectedTag("initializer", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void ScopeBuilder::VisitExpression() { |
| uint8_t payload = 0; |
| Tag tag = helper_.ReadTag(&payload); |
| switch (tag) { |
| case kInvalidExpression: |
| helper_.ReadPosition(); |
| helper_.SkipStringReference(); |
| if (helper_.ReadTag() == kSomething) { |
| VisitExpression(); // read expression. |
| } |
| return; |
| case kVariableGet: { |
| helper_.ReadPosition(); // read position. |
| intptr_t variable_kernel_offset = |
| helper_.ReadUInt(); // read kernel position. |
| helper_.ReadUInt(); // read relative variable index. |
| helper_.SkipOptionalDartType(); // read promoted type. |
| VisitVariableGet(variable_kernel_offset); |
| return; |
| } |
| case kSpecializedVariableGet: { |
| helper_.ReadPosition(); // read position. |
| intptr_t variable_kernel_offset = |
| helper_.ReadUInt(); // read kernel position. |
| VisitVariableGet(variable_kernel_offset); |
| return; |
| } |
| case kVariableSet: { |
| helper_.ReadPosition(); // read position. |
| intptr_t variable_kernel_offset = |
| helper_.ReadUInt(); // read kernel position. |
| helper_.ReadUInt(); // read relative variable index. |
| LookupVariable(variable_kernel_offset); |
| VisitExpression(); // read expression. |
| return; |
| } |
| case kSpecializedVariableSet: { |
| helper_.ReadPosition(); // read position. |
| intptr_t variable_kernel_offset = |
| helper_.ReadUInt(); // read kernel position. |
| LookupVariable(variable_kernel_offset); |
| VisitExpression(); // read expression. |
| return; |
| } |
| case kInstanceGet: |
| helper_.ReadByte(); // read kind. |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| helper_.SkipName(); // read name. |
| helper_.SkipDartType(); // read result_type. |
| // read interface_target_reference. |
| helper_.SkipInterfaceMemberNameReference(); |
| return; |
| case kDynamicGet: |
| helper_.ReadByte(); // read kind. |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| helper_.SkipName(); // read name. |
| return; |
| case kInstanceTearOff: |
| helper_.ReadByte(); // read kind. |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| helper_.SkipName(); // read name. |
| helper_.SkipDartType(); // read result_type. |
| // read interface_target_reference. |
| helper_.SkipInterfaceMemberNameReference(); |
| return; |
| case kFunctionTearOff: |
| // Removed by lowering kernel transformation. |
| UNREACHABLE(); |
| break; |
| case kInstanceSet: |
| helper_.ReadByte(); // read kind. |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| helper_.SkipName(); // read name. |
| VisitExpression(); // read value. |
| // read interface_target_reference. |
| helper_.SkipInterfaceMemberNameReference(); |
| return; |
| case kDynamicSet: |
| helper_.ReadByte(); // read kind. |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| helper_.SkipName(); // read name. |
| VisitExpression(); // read value. |
| return; |
| case kAbstractSuperPropertyGet: |
| // Abstract super property getters must be converted into super property |
| // getters during mixin transformation. |
| UNREACHABLE(); |
| break; |
| case kAbstractSuperPropertySet: |
| // Abstract super property setters must be converted into super property |
| // setters during mixin transformation. |
| UNREACHABLE(); |
| break; |
| case kSuperPropertyGet: |
| HandleLoadReceiver(); |
| helper_.ReadPosition(); // read position. |
| helper_.SkipName(); // read name. |
| helper_.SkipInterfaceMemberNameReference(); // read target_reference. |
| return; |
| case kSuperPropertySet: |
| HandleLoadReceiver(); |
| helper_.ReadPosition(); // read position. |
| helper_.SkipName(); // read name. |
| VisitExpression(); // read value. |
| helper_.SkipInterfaceMemberNameReference(); // read target_reference. |
| return; |
| case kStaticGet: |
| helper_.ReadPosition(); // read position. |
| helper_.SkipCanonicalNameReference(); // read target_reference. |
| return; |
| case kStaticSet: |
| helper_.ReadPosition(); // read position. |
| helper_.SkipCanonicalNameReference(); // read target_reference. |
| VisitExpression(); // read expression. |
| return; |
| case kInstanceInvocation: |
| helper_.ReadByte(); // read kind. |
| helper_.ReadFlags(); // read flags. |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| helper_.SkipName(); // read name. |
| VisitArguments(); // read arguments. |
| helper_.SkipDartType(); // read function_type. |
| // read interface_target_reference. |
| helper_.SkipInterfaceMemberNameReference(); |
| return; |
| case kDynamicInvocation: |
| helper_.ReadByte(); // read kind. |
| helper_.ReadByte(); // read flags. |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| helper_.SkipName(); // read name. |
| VisitArguments(); // read arguments. |
| return; |
| case kLocalFunctionInvocation: { |
| helper_.ReadPosition(); // read position. |
| intptr_t variable_kernel_offset = |
| helper_.ReadUInt(); // read variable kernel position. |
| helper_.ReadUInt(); // read relative variable index. |
| VisitArguments(); // read arguments. |
| helper_.SkipDartType(); // read function_type. |
| VisitVariableGet(variable_kernel_offset); |
| return; |
| } |
| case kFunctionInvocation: |
| helper_.ReadByte(); // read kind. |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| VisitArguments(); // read arguments. |
| helper_.SkipDartType(); // read function_type. |
| return; |
| case kEqualsCall: |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read left. |
| VisitExpression(); // read right. |
| helper_.SkipDartType(); // read function_type. |
| // read interface_target_reference. |
| helper_.SkipInterfaceMemberNameReference(); |
| return; |
| case kEqualsNull: |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read expression. |
| return; |
| case kAbstractSuperMethodInvocation: |
| // Abstract super method invocations must be converted into super |
| // method invocations during mixin transformation. |
| UNREACHABLE(); |
| break; |
| case kSuperMethodInvocation: |
| HandleLoadReceiver(); |
| helper_.ReadPosition(); // read position. |
| helper_.SkipName(); // read name. |
| VisitArguments(); // read arguments. |
| // read interface_target_reference. |
| helper_.SkipInterfaceMemberNameReference(); |
| return; |
| case kStaticInvocation: |
| helper_.ReadPosition(); // read position. |
| helper_.SkipCanonicalNameReference(); // read procedure_reference. |
| VisitArguments(); // read arguments. |
| return; |
| case kConstructorInvocation: |
| helper_.ReadPosition(); // read position. |
| helper_.SkipCanonicalNameReference(); // read target_reference. |
| VisitArguments(); // read arguments. |
| return; |
| case kNot: |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read expression. |
| return; |
| case kNullCheck: |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read expression. |
| return; |
| case kLogicalExpression: |
| needs_expr_temp_ = true; |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read left. |
| helper_.SkipBytes(1); // read operator. |
| VisitExpression(); // read right. |
| return; |
| case kConditionalExpression: { |
| needs_expr_temp_ = true; |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read condition. |
| VisitExpression(); // read then. |
| VisitExpression(); // read otherwise. |
| helper_.SkipOptionalDartType(); // read unused static type. |
| return; |
| } |
| case kStringConcatenation: { |
| helper_.ReadPosition(); // read position. |
| VisitListOfExpressions(); |
| return; |
| } |
| case kIsExpression: |
| needs_expr_temp_ = true; |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read operand. |
| VisitDartType(); // read type. |
| return; |
| case kAsExpression: |
| helper_.ReadPosition(); // read position. |
| helper_.ReadFlags(); // read flags. |
| VisitExpression(); // read operand. |
| VisitDartType(); // read type. |
| return; |
| case kTypeLiteral: |
| helper_.ReadPosition(); // read file offset. |
| VisitDartType(); // read type. |
| return; |
| case kThisExpression: |
| HandleLoadReceiver(); |
| helper_.ReadPosition(); // read file offset. |
| return; |
| case kRethrow: |
| helper_.ReadPosition(); // read position. |
| return; |
| case kThrow: |
| helper_.ReadPosition(); // read position. |
| helper_.ReadFlags(); // read flags. |
| VisitExpression(); // read expression. |
| return; |
| case kListLiteral: { |
| helper_.ReadPosition(); // read position. |
| VisitDartType(); // read type. |
| VisitListOfExpressions(); |
| return; |
| } |
| case kSetLiteral: { |
| // Set literals are currently desugared in the frontend and will not |
| // reach the VM. See http://dartbug.com/35124 for discussion. |
| UNREACHABLE(); |
| return; |
| } |
| case kMapLiteral: { |
| helper_.ReadPosition(); // read position. |
| VisitDartType(); // read key type. |
| VisitDartType(); // read value type. |
| intptr_t list_length = helper_.ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitExpression(); // read ith key. |
| VisitExpression(); // read ith value. |
| } |
| return; |
| } |
| case kRecordLiteral: |
| helper_.ReadPosition(); // read position. |
| VisitListOfExpressions(); // read positionals. |
| VisitListOfNamedExpressions(); // read named. |
| VisitDartType(); // read recordType. |
| return; |
| case kRecordIndexGet: |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| helper_.SkipDartType(); // read recordType. |
| helper_.ReadUInt(); // read index. |
| return; |
| case kRecordNameGet: |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| helper_.SkipDartType(); // read recordType. |
| helper_.SkipStringReference(); // read name. |
| return; |
| case kFunctionExpression: { |
| intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte. |
| helper_.ReadPosition(); // read position. |
| HandleLocalFunction(offset); // read function node. |
| return; |
| } |
| case kLet: { |
| PositionScope scope(&helper_.reader_); |
| intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte. |
| |
| EnterScope(offset); |
| |
| helper_.ReadPosition(); // read position. |
| VisitVariableDeclaration(); // read variable declaration. |
| VisitExpression(); // read expression. |
| |
| ExitScope(helper_.reader_.min_position(), helper_.reader_.max_position()); |
| return; |
| } |
| case kBlockExpression: { |
| PositionScope scope(&helper_.reader_); |
| intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte. |
| |
| EnterScope(offset); |
| |
| helper_.ReadPosition(); // read position. |
| intptr_t list_length = |
| helper_.ReadListLength(); // read number of statements. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitStatement(); // read ith statement. |
| } |
| VisitExpression(); // read expression. |
| |
| ExitScope(helper_.reader_.min_position(), helper_.reader_.max_position()); |
| return; |
| } |
| case kBigIntLiteral: |
| helper_.ReadPosition(); // read position. |
| helper_.SkipStringReference(); // read string reference. |
| return; |
| case kStringLiteral: |
| helper_.ReadPosition(); // read position. |
| helper_.SkipStringReference(); // read string reference. |
| return; |
| case kSpecializedIntLiteral: |
| helper_.ReadPosition(); // read position. |
| return; |
| case kNegativeIntLiteral: |
| helper_.ReadPosition(); // read position. |
| helper_.ReadUInt(); // read value. |
| return; |
| case kPositiveIntLiteral: |
| helper_.ReadPosition(); // read position. |
| helper_.ReadUInt(); // read value. |
| return; |
| case kDoubleLiteral: |
| helper_.ReadPosition(); // read position. |
| helper_.ReadDouble(); // read value. |
| return; |
| case kTrueLiteral: |
| helper_.ReadPosition(); // read position. |
| return; |
| case kFalseLiteral: |
| helper_.ReadPosition(); // read position. |
| return; |
| case kNullLiteral: |
| helper_.ReadPosition(); // read position. |
| return; |
| case kConstantExpression: |
| helper_.ReadPosition(); |
| helper_.SkipDartType(); |
| helper_.SkipConstantReference(); |
| return; |
| case kFileUriConstantExpression: |
| helper_.ReadPosition(); |
| helper_.ReadUInt(); |
| helper_.SkipDartType(); |
| helper_.SkipConstantReference(); |
| return; |
| case kInstantiation: { |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); |
| const intptr_t list_length = |
| helper_.ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitDartType(); // read ith type. |
| } |
| return; |
| } |
| case kLoadLibrary: |
| case kCheckLibraryIsLoaded: |
| helper_.ReadPosition(); // read file offset. |
| helper_.ReadUInt(); // library index |
| break; |
| case kAwaitExpression: |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read operand. |
| if (helper_.ReadTag() == kSomething) { |
| VisitDartType(); // read runtime check type. |
| } |
| return; |
| case kFileUriExpression: |
| helper_.ReadUInt(); // read uri. |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read expression. |
| return; |
| case kConstStaticInvocation: |
| case kConstConstructorInvocation: |
| case kConstListLiteral: |
| case kConstSetLiteral: |
| case kConstMapLiteral: |
| case kSymbolLiteral: |
| case kListConcatenation: |
| case kSetConcatenation: |
| case kMapConcatenation: |
| case kInstanceCreation: |
| case kStaticTearOff: |
| case kSwitchExpression: |
| case kPatternAssignment: |
| // These nodes are internal to the front end and |
| // removed by the constant evaluator. |
| default: |
| ReportUnexpectedTag("expression", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void ScopeBuilder::VisitStatement() { |
| Tag tag = helper_.ReadTag(); // read tag. |
| switch (tag) { |
| case kExpressionStatement: |
| VisitExpression(); // read expression. |
| return; |
| case kBlock: { |
| PositionScope scope(&helper_.reader_); |
| intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte. |
| |
| EnterScope(offset); |
| helper_.ReadPosition(); // read block start offset. |
| helper_.ReadPosition(); // read block end offset. |
| intptr_t list_length = |
| helper_.ReadListLength(); // read number of statements. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitStatement(); // read ith statement. |
| } |
| |
| ExitScope(helper_.reader_.min_position(), helper_.reader_.max_position()); |
| return; |
| } |
| case kEmptyStatement: |
| return; |
| case kAssertBlock: |
| if (IG->asserts()) { |
| PositionScope scope(&helper_.reader_); |
| intptr_t offset = |
| helper_.ReaderOffset() - 1; // -1 to include tag byte. |
| |
| EnterScope(offset); |
| |
| intptr_t list_length = |
| helper_.ReadListLength(); // read number of statements. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitStatement(); // read ith statement. |
| } |
| |
| ExitScope(helper_.reader_.min_position(), |
| helper_.reader_.max_position()); |
| } else { |
| helper_.SkipStatementList(); |
| } |
| return; |
| case kAssertStatement: |
| if (IG->asserts()) { |
| VisitExpression(); // Read condition. |
| helper_.ReadPosition(); // read condition start offset. |
| helper_.ReadPosition(); // read condition end offset. |
| Tag tag = helper_.ReadTag(); // read (first part of) message. |
| if (tag == kSomething) { |
| VisitExpression(); // read (rest of) message. |
| } |
| } else { |
| helper_.SkipExpression(); // Read condition. |
| helper_.ReadPosition(); // read condition start offset. |
| helper_.ReadPosition(); // read condition end offset. |
| Tag tag = helper_.ReadTag(); // read (first part of) message. |
| if (tag == kSomething) { |
| helper_.SkipExpression(); // read (rest of) message. |
| } |
| } |
| return; |
| case kLabeledStatement: |
| helper_.ReadPosition(); // read position. |
| VisitStatement(); // read body. |
| return; |
| case kBreakStatement: |
| helper_.ReadPosition(); // read position. |
| helper_.ReadUInt(); // read target_index. |
| return; |
| case kWhileStatement: |
| ++depth_.loop_; |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read condition. |
| VisitStatement(); // read body. |
| --depth_.loop_; |
| return; |
| case kDoStatement: |
| ++depth_.loop_; |
| helper_.ReadPosition(); // read position. |
| VisitStatement(); // read body. |
| VisitExpression(); // read condition. |
| --depth_.loop_; |
| return; |
| case kForStatement: { |
| PositionScope scope(&helper_.reader_); |
| |
| intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte. |
| |
| ++depth_.loop_; |
| EnterScope(offset); |
| |
| TokenPosition position = helper_.ReadPosition(); // read position. |
| intptr_t list_length = |
| helper_.ReadListLength(); // read number of variables. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitVariableDeclaration(); // read ith variable. |
| } |
| |
| Tag tag = helper_.ReadTag(); // Read first part of condition. |
| if (tag == kSomething) { |
| VisitExpression(); // read rest of condition. |
| } |
| VisitListOfExpressions(); // read updates. |
| VisitStatement(); // read body. |
| |
| ExitScope(position, helper_.reader_.max_position()); |
| --depth_.loop_; |
| return; |
| } |
| case kSwitchStatement: { |
| AddSwitchVariable(); |
| helper_.ReadPosition(); // read position. |
| helper_.ReadBool(); // read exhaustive flag. |
| VisitExpression(); // read condition. |
| helper_.SkipOptionalDartType(); // read expression type. |
| int case_count = helper_.ReadListLength(); // read number of cases. |
| for (intptr_t i = 0; i < case_count; ++i) { |
| helper_.ReadPosition(); // read file offset. |
| int expression_count = |
| helper_.ReadListLength(); // read number of expressions. |
| for (intptr_t j = 0; j < expression_count; ++j) { |
| helper_.ReadPosition(); // read jth position. |
| VisitExpression(); // read jth expression. |
| } |
| helper_.ReadBool(); // read is_default. |
| VisitStatement(); // read body. |
| } |
| return; |
| } |
| case kContinueSwitchStatement: |
| helper_.ReadPosition(); // read position. |
| helper_.ReadUInt(); // read target_index. |
| return; |
| case kIfStatement: |
| helper_.ReadPosition(); // read position. |
| VisitExpression(); // read condition. |
| VisitStatement(); // read then. |
| VisitStatement(); // read otherwise. |
| return; |
| case kReturnStatement: { |
| if ((depth_.function_ == 0) && (depth_.finally_ > 0) && |
| (result_->finally_return_variable == nullptr)) { |
| const String& name = Symbols::TryFinallyReturnValue(); |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| name, AbstractType::dynamic_type()); |
| current_function_scope_->AddVariable(variable); |
| result_->finally_return_variable = variable; |
| } |
| |
| helper_.ReadPosition(); // read position |
| Tag tag = helper_.ReadTag(); // read (first part of) expression. |
| if (tag == kSomething) { |
| VisitExpression(); // read (rest of) expression. |
| } |
| return; |
| } |
| case kTryCatch: { |
| ++depth_.try_; |
| AddTryVariables(); |
| helper_.ReadPosition(); // read position. |
| VisitStatement(); // read body. |
| --depth_.try_; |
| |
| ++depth_.catch_; |
| AddCatchVariables(); |
| |
| helper_.ReadByte(); // read flags |
| intptr_t catch_count = |
| helper_.ReadListLength(); // read number of catches. |
| for (intptr_t i = 0; i < catch_count; ++i) { |
| PositionScope scope(&helper_.reader_); |
| intptr_t offset = helper_.ReaderOffset(); // Catch has no tag. |
| |
| EnterScope(offset); |
| |
| helper_.ReadPosition(); // read position. |
| VisitDartType(); // Read the guard. |
| tag = helper_.ReadTag(); // read first part of exception. |
| if (tag == kSomething) { |
| VisitVariableDeclaration(); // read exception. |
| } |
| tag = helper_.ReadTag(); // read first part of stack trace. |
| if (tag == kSomething) { |
| VisitVariableDeclaration(); // read stack trace. |
| } |
| VisitStatement(); // read body. |
| |
| ExitScope(helper_.reader_.min_position(), |
| helper_.reader_.max_position()); |
| } |
| |
| FinalizeCatchVariables(); |
| |
| --depth_.catch_; |
| return; |
| } |
| case kTryFinally: { |
| ++depth_.try_; |
| ++depth_.finally_; |
| AddTryVariables(); |
| |
| helper_.ReadPosition(); // read position. |
| VisitStatement(); // read body. |
| |
| --depth_.finally_; |
| --depth_.try_; |
| ++depth_.catch_; |
| AddCatchVariables(); |
| |
| VisitStatement(); // read finalizer. |
| |
| FinalizeCatchVariables(); |
| |
| --depth_.catch_; |
| return; |
| } |
| case kYieldStatement: { |
| helper_.ReadPosition(); // read position. |
| helper_.ReadByte(); // read flags. |
| VisitExpression(); // read expression. |
| return; |
| } |
| case kVariableDeclaration: |
| VisitVariableDeclaration(); // read variable declaration. |
| return; |
| case kFunctionDeclaration: { |
| intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte. |
| helper_.ReadPosition(); // read position. |
| VisitVariableDeclaration(); // read variable declaration. |
| HandleLocalFunction(offset); // read function node. |
| return; |
| } |
| case kForInStatement: |
| case kAsyncForInStatement: |
| case kIfCaseStatement: |
| case kPatternSwitchStatement: |
| case kPatternVariableDeclaration: |
| // These nodes are internal to the front end and |
| // removed by the constant evaluator. |
| default: |
| ReportUnexpectedTag("declaration", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void ScopeBuilder::VisitListOfExpressions() { |
| const intptr_t list_length = helper_.ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitExpression(); |
| } |
| } |
| |
| void ScopeBuilder::VisitListOfNamedExpressions() { |
| const intptr_t list_length = helper_.ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| helper_.SkipStringReference(); // read ith name index. |
| VisitExpression(); // read ith expression. |
| } |
| } |
| |
| void ScopeBuilder::VisitArguments() { |
| helper_.ReadUInt(); // read argument_count. |
| |
| // Types |
| intptr_t list_length = helper_.ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitDartType(); // read ith type. |
| } |
| |
| VisitListOfExpressions(); // Positional. |
| VisitListOfNamedExpressions(); // Named. |
| } |
| |
| void ScopeBuilder::VisitVariableDeclaration() { |
| PositionScope scope(&helper_.reader_); |
| |
| const intptr_t kernel_offset = |
| helper_.data_program_offset_ + helper_.ReaderOffset(); |
| // MetadataHelper expects relative offsets and adjusts them internally |
| const InferredTypeMetadata inferred_type = |
| inferred_type_metadata_helper_.GetInferredType(helper_.ReaderOffset()); |
| VariableDeclarationHelper helper(&helper_); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kAnnotations); |
| const intptr_t annotations_offset = helper_.ReaderOffset(); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
| AbstractType& type = BuildAndVisitVariableType(); |
| |
| const String& name = H.DartSymbolObfuscate(helper.name_index_); |
| |
| intptr_t initializer_offset = helper_.ReaderOffset(); |
| Tag tag = helper_.ReadTag(); // read (first part of) initializer. |
| if (tag == kSomething) { |
| VisitExpression(); // read (actual) initializer. |
| } |
| |
| // Go to next token position so it ends *after* the last potentially |
| // debuggable position in the initializer. |
| TokenPosition end_position = helper_.reader_.max_position(); |
| if (end_position.IsReal()) { |
| end_position = end_position.Next(); |
| } |
| LocalVariable* variable = |
| MakeVariable(helper.position_, end_position, name, type, kernel_offset, |
| helper.IsLate(), &inferred_type); |
| if (helper.annotation_count_ > 0) { |
| variable->set_annotations_offset(annotations_offset); |
| } |
| if (helper.IsFinal()) { |
| variable->set_is_final(); |
| } |
| if (helper.IsLate()) { |
| variable->set_is_late(); |
| variable->set_late_init_offset(initializer_offset); |
| } |
| if (helper.IsSynthesized() || helper.IsWildcard() || |
| helper.IsInitializingFormal() || helper.IsSuperInitializingFormal()) { |
| variable->set_invisible(true); |
| } |
| |
| scope_->AddVariable(variable); |
| result_->locals.Insert(kernel_offset, variable); |
| } |
| |
| AbstractType& ScopeBuilder::BuildAndVisitVariableType() { |
| const intptr_t offset = helper_.ReaderOffset(); |
| AbstractType& type = T.BuildType(); |
| helper_.SetOffset(offset); // rewind |
| VisitDartType(); |
| return type; |
| } |
| |
| void ScopeBuilder::VisitDartType() { |
| Tag tag = helper_.ReadTag(); |
| switch (tag) { |
| case kInvalidType: |
| case kDynamicType: |
| case kVoidType: |
| case kNullType: |
| // those contain nothing. |
| return; |
| case kNeverType: |
| helper_.ReadNullability(); |
| return; |
| case kInterfaceType: |
| VisitInterfaceType(false); |
| return; |
| case kSimpleInterfaceType: |
| VisitInterfaceType(true); |
| return; |
| case kFunctionType: |
| VisitFunctionType(false); |
| return; |
| case kSimpleFunctionType: |
| VisitFunctionType(true); |
| return; |
| case kRecordType: |
| VisitRecordType(); |
| return; |
| case kTypeParameterType: |
| VisitTypeParameterType(); |
| return; |
| case kIntersectionType: |
| VisitIntersectionType(); |
| return; |
| case kExtensionType: |
| VisitExtensionType(); |
| return; |
| case kFutureOrType: |
| VisitFutureOrType(); |
| return; |
| default: |
| ReportUnexpectedTag("type", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void ScopeBuilder::VisitInterfaceType(bool simple) { |
| helper_.ReadNullability(); // read nullability. |
| helper_.ReadUInt(); // read klass_name. |
| if (!simple) { |
| intptr_t length = helper_.ReadListLength(); // read number of types. |
| for (intptr_t i = 0; i < length; ++i) { |
| VisitDartType(); // read the ith type. |
| } |
| } |
| } |
| |
| void ScopeBuilder::VisitFunctionType(bool simple) { |
| helper_.ReadNullability(); // read nullability. |
| |
| if (!simple) { |
| intptr_t list_length = |
| helper_.ReadListLength(); // read type_parameters list length. |
| for (int i = 0; i < list_length; ++i) { |
| TypeParameterHelper helper(&helper_); |
| helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound); |
| VisitDartType(); // read bound. |
| helper.ReadUntilExcludingAndSetJustRead( |
| TypeParameterHelper::kDefaultType); |
| VisitDartType(); // read default type. |
| helper.Finish(); |
| } |
| helper_.ReadUInt(); // read required parameter count. |
| helper_.ReadUInt(); // read total parameter count. |
| } |
| |
| const intptr_t positional_count = |
| helper_.ReadListLength(); // read positional_parameters list length. |
| for (intptr_t i = 0; i < positional_count; ++i) { |
| VisitDartType(); // read ith positional parameter. |
| } |
| |
| if (!simple) { |
| const intptr_t named_count = |
| helper_.ReadListLength(); // read named_parameters list length. |
| for (intptr_t i = 0; i < named_count; ++i) { |
| // read string reference (i.e. named_parameters[i].name). |
| helper_.SkipStringReference(); |
| VisitDartType(); // read named_parameters[i].type. |
| helper_.ReadByte(); // read flags |
| } |
| } |
| |
| VisitDartType(); // read return type. |
| } |
| |
| void ScopeBuilder::VisitRecordType() { |
| helper_.ReadNullability(); // read nullability. |
| const intptr_t positional_count = |
| helper_.ReadListLength(); // read positional list length. |
| for (intptr_t i = 0; i < positional_count; ++i) { |
| VisitDartType(); // read positional[i]. |
| } |
| const intptr_t named_count = |
| helper_.ReadListLength(); // read named list length. |
| for (intptr_t i = 0; i < named_count; ++i) { |
| helper_.SkipStringReference(); // read named[i].name. |
| VisitDartType(); // read named[i].type. |
| helper_.ReadFlags(); // read named[i].flags |
| } |
| } |
| |
| void ScopeBuilder::VisitTypeParameterType() { |
| Function& function = Function::Handle(Z, parsed_function_->function().ptr()); |
| |
| helper_.ReadNullability(); // read nullability. |
| |
| // The index here is the index identifying the type parameter binding site |
| // inside the DILL file, which uses a different indexing system than the VM |
| // uses for its 'TypeParameter's internally. This index includes both class |
| // and function type parameters. |
| |
| intptr_t index = helper_.ReadUInt(); // read index for parameter. |
| |
| if (!function.IsImplicitStaticClosureFunction()) { |
| 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(), |
| LocalVariable::kNoKernelOffset); |
| } else { |
| // If the type parameter is a parameter to this or an enclosing function, |
| // we can read it directly from the function type arguments vector later. |
| // Otherwise, the type arguments vector we need is stored on the instance |
| // object, so we need to capture 'this'. |
| Class& parent_class = Class::Handle(Z, function.Owner()); |
| if (index < parent_class.NumTypeParameters()) { |
| HandleLoadReceiver(); |
| } |
| } |
| } |
| } |
| |
| void ScopeBuilder::VisitIntersectionType() { |
| VisitDartType(); // read left. |
| helper_.SkipDartType(); // read right. |
| } |
| |
| void ScopeBuilder::VisitExtensionType() { |
| // We skip the extension type and only use the type erasure. |
| helper_.ReadNullability(); |
| helper_.SkipCanonicalNameReference(); // read index for canonical name. |
| helper_.SkipListOfDartTypes(); // read type arguments |
| VisitDartType(); // read type erasure. |
| } |
| |
| void ScopeBuilder::VisitFutureOrType() { |
| helper_.ReadNullability(); |
| VisitDartType(); // read type argument. |
| } |
| |
| void ScopeBuilder::HandleLocalFunction(intptr_t parent_kernel_offset) { |
| // "Peek" ahead into the function node |
| const intptr_t offset = helper_.ReaderOffset(); |
| |
| FunctionNodeHelper function_node_helper(&helper_); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
| |
| LocalScope* saved_function_scope = current_function_scope_; |
| DepthState saved_depth_state = depth_; |
| depth_ = DepthState(depth_.function_ + 1); |
| EnterScope(parent_kernel_offset); |
| current_function_scope_ = scope_; |
| if (depth_.function_ == 1) { |
| FunctionScope function_scope = {offset, scope_}; |
| result_->function_scopes.Add(function_scope); |
| } |
| |
| int num_type_params = 0; |
| { |
| AlternativeReadingScope _(&helper_.reader_); |
| num_type_params = helper_.ReadListLength(); |
| } |
| // Adding this scope here informs the type translator the type parameters of |
| // this function are now in scope, although they are not defined and will be |
| // filled in with dynamic. This is OK, since their definitions are not needed |
| // for scope building of the enclosing function. |
| TypeTranslator::TypeParameterScope scope(&type_translator_, num_type_params); |
| |
| // read positional_parameters and named_parameters. |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| |
| ProcedureAttributesMetadata default_attrs; |
| AddPositionalAndNamedParameters(0, kTypeCheckForNonDynamicallyInvokedMethod, |
| default_attrs); |
| |
| if (function_node_helper.async_marker_ != FunctionNodeHelper::kSync) { |
| // Read return type which is used to create a result of async/async*/sync* |
| // function. It may reference receiver or type arguments of the enclosing |
| // function which need to be captured. |
| VisitDartType(); |
| } |
| |
| // "Peek" is now done. |
| helper_.SetOffset(offset); |
| |
| VisitFunctionNode(); // read function node. |
| |
| // Remember if this closure and all closures nested within it don't |
| // capture any variables from outer scopes. |
| if (scope_->function_level() == 1) { |
| if (scope_->NumCapturedVariables() == 0) { |
| result_->closure_offsets_without_captures.Add(offset); |
| } |
| } |
| |
| ExitScope(function_node_helper.position_, function_node_helper.end_position_); |
| depth_ = saved_depth_state; |
| current_function_scope_ = saved_function_scope; |
| } |
| |
| void ScopeBuilder::EnterScope(intptr_t kernel_offset) { |
| scope_ = new (Z) LocalScope(scope_, depth_.function_, depth_.loop_); |
| ASSERT(kernel_offset >= 0); |
| result_->scopes.Insert(kernel_offset, scope_); |
| } |
| |
| void ScopeBuilder::ExitScope(TokenPosition start_position, |
| TokenPosition end_position) { |
| scope_->set_begin_token_pos(start_position); |
| scope_->set_end_token_pos(end_position); |
| scope_ = scope_->parent(); |
| } |
| |
| void ScopeBuilder::AddPositionalAndNamedParameters( |
| intptr_t pos, |
| ParameterTypeCheckMode type_check_mode /* = kTypeCheckAllParameters*/, |
| const ProcedureAttributesMetadata& attrs) { |
| // List of positional. |
| intptr_t list_length = helper_.ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| AddVariableDeclarationParameter(pos++, type_check_mode, attrs); |
| } |
| |
| // List of named. |
| list_length = helper_.ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| AddVariableDeclarationParameter(pos++, type_check_mode, attrs); |
| } |
| } |
| |
| void ScopeBuilder::AddVariableDeclarationParameter( |
| intptr_t pos, |
| ParameterTypeCheckMode type_check_mode, |
| const ProcedureAttributesMetadata& attrs) { |
| // Convert kernel offset of variable declaration to absolute. |
| const intptr_t kernel_offset = |
| helper_.data_program_offset_ + helper_.ReaderOffset(); |
| // MetadataHelper expects relative offsets and adjusts them internally |
| const InferredTypeMetadata inferred_type = |
| inferred_type_metadata_helper_.GetInferredType(helper_.ReaderOffset()); |
| const InferredTypeMetadata inferred_arg_type = |
| inferred_arg_type_metadata_helper_.GetInferredType( |
| helper_.ReaderOffset()); |
| VariableDeclarationHelper helper(&helper_); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kAnnotations); |
| const intptr_t annotations_offset = helper_.ReaderOffset(); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
| String& name = H.DartSymbolObfuscate(helper.name_index_); |
| ASSERT(name.Length() > 0); |
| AbstractType& type = BuildAndVisitVariableType(); // read type. |
| helper.SetJustRead(VariableDeclarationHelper::kType); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
| |
| LocalVariable* variable = MakeVariable( |
| helper.position_, helper.position_, name, type, kernel_offset, |
| /*is_late=*/false, &inferred_type, &inferred_arg_type); |
| if (helper.annotation_count_ > 0) { |
| variable->set_annotations_offset(annotations_offset); |
| } |
| if (helper.IsFinal()) { |
| variable->set_is_final(); |
| } |
| if (helper.IsCovariant()) { |
| variable->set_is_explicit_covariant_parameter(); |
| } |
| if (helper.IsWildcard() || helper.IsInitializingFormal() || |
| helper.IsSuperInitializingFormal()) { |
| variable->set_invisible(true); |
| } |
| |
| const bool needs_covariant_check_in_method = |
| helper.IsCovariant() || |
| (helper.IsGenericCovariantImpl() && |
| (attrs.has_non_this_uses || attrs.has_tearoff_uses)); |
| if (needs_covariant_check_in_method) { |
| variable->set_needs_covariant_check_in_method(); |
| } |
| |
| switch (type_check_mode) { |
| case kTypeCheckAllParameters: |
| variable->set_type_check_mode(LocalVariable::kDoTypeCheck); |
| break; |
| case kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod: |
| if (needs_covariant_check_in_method) { |
| // Don't type check covariant parameters - they will be checked by |
| // a function we forward to. Their types however are not known. |
| variable->set_type_check_mode(LocalVariable::kSkipTypeCheck); |
| } else { |
| variable->set_type_check_mode(LocalVariable::kDoTypeCheck); |
| } |
| break; |
| case kTypeCheckForNonDynamicallyInvokedMethod: |
| if (needs_covariant_check_in_method) { |
| variable->set_type_check_mode(LocalVariable::kDoTypeCheck); |
| } else { |
| // Types of non-covariant parameters are guaranteed to match by |
| // front-end enforcing strong mode types at call site. |
| variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller); |
| } |
| break; |
| case kTypeCheckForImplicitClosureFunction: |
| if (needs_covariant_check_in_method) { |
| // Don't type check covariant parameters - they will be checked by |
| // a function we forward to. Their types however are not known. |
| variable->set_type_check_mode(LocalVariable::kSkipTypeCheck); |
| } else { |
| // All non-covariant checks are either checked by the type system or |
| // by a dynamic closure call dispatcher. |
| variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller); |
| } |
| break; |
| case kTypeCheckForStaticFunction: |
| variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller); |
| break; |
| } |
| |
| // TODO(sjindel): We can also skip these checks on dynamic invocations as |
| // well. |
| if (inferred_arg_type.IsSkipCheck()) { |
| variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller); |
| } |
| |
| scope_->InsertParameterAt(pos, variable); |
| result_->locals.Insert(kernel_offset, variable); |
| |
| // The default value may contain 'let' bindings for which the constant |
| // evaluator needs scope bindings. |
| Tag tag = helper_.ReadTag(); |
| if (tag == kSomething) { |
| VisitExpression(); // read initializer. |
| } |
| } |
| |
| LocalVariable* ScopeBuilder::MakeVariable( |
| TokenPosition declaration_pos, |
| TokenPosition token_pos, |
| const String& name, |
| const AbstractType& static_type, |
| intptr_t kernel_offset /* = LocalVariable::kNoKernelOffset */, |
| bool is_late /* = false */, |
| const InferredTypeMetadata* inferred_type_md /* = nullptr */, |
| const InferredTypeMetadata* inferred_arg_type_md /* = nullptr */) { |
| CompileType* inferred_type = nullptr; |
| if (inferred_type_md != nullptr && !inferred_type_md->IsTrivial()) { |
| inferred_type = new (Z) |
| CompileType(inferred_type_md->ToCompileType(Z, &static_type, is_late)); |
| } else { |
| inferred_type = new (Z) CompileType(CompileType::FromAbstractType( |
| static_type, CompileType::kCanBeNull, is_late)); |
| } |
| CompileType* inferred_arg_type = nullptr; |
| const Object* inferred_arg_value = nullptr; |
| if (inferred_arg_type_md != nullptr && !inferred_arg_type_md->IsTrivial()) { |
| inferred_arg_type = |
| new (Z) CompileType(inferred_arg_type_md->ToCompileType(Z)); |
| if (inferred_arg_type_md->IsConstant()) { |
| inferred_arg_value = &inferred_arg_type_md->constant_value; |
| } |
| } |
| return new (Z) LocalVariable(declaration_pos, token_pos, name, static_type, |
| kernel_offset, inferred_type, inferred_arg_type, |
| inferred_arg_value); |
| } |
| |
| void ScopeBuilder::AddExceptionVariable( |
| GrowableArray<LocalVariable*>* variables, |
| const char* prefix, |
| intptr_t nesting_depth) { |
| LocalVariable* v = nullptr; |
| |
| // 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 == nullptr) { |
| 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 encounter a YieldStatement. |
| current_function_scope_->AddVariable(v); |
| } |
| |
| variables->Add(v); |
| } |
| |
| void ScopeBuilder::FinalizeExceptionVariable( |
| GrowableArray<LocalVariable*>* variables, |
| GrowableArray<LocalVariable*>* raw_variables, |
| const String& symbol, |
| intptr_t nesting_depth) { |
| // No need to create variables for try/catch-statements inside |
| // nested functions. |
| if (depth_.function_ > 0) return; |
| |
| LocalVariable* variable = (*variables)[nesting_depth - 1]; |
| LocalVariable* raw_variable; |
| if (variable->is_captured()) { |
| raw_variable = |
| new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| symbol, AbstractType::dynamic_type()); |
| const bool ok = scope_->AddVariable(raw_variable); |
| ASSERT(ok); |
| } else { |
| raw_variable = variable; |
| } |
| raw_variables->EnsureLength(nesting_depth, nullptr); |
| (*raw_variables)[nesting_depth - 1] = raw_variable; |
| } |
| |
| 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::FinalizeCatchVariables() { |
| const intptr_t unique_id = result_->raw_variable_counter_++; |
| FinalizeExceptionVariable( |
| &result_->exception_variables, &result_->raw_exception_variables, |
| GenerateName(":raw_exception", unique_id), depth_.catch_); |
| FinalizeExceptionVariable( |
| &result_->stack_trace_variables, &result_->raw_stack_trace_variables, |
| GenerateName(":raw_stacktrace", unique_id), depth_.catch_); |
| } |
| |
| void ScopeBuilder::AddSwitchVariable() { |
| if ((depth_.function_ == 0) && (result_->switch_variable == nullptr)) { |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::SwitchExpr(), AbstractType::dynamic_type()); |
| current_function_scope_->AddVariable(variable); |
| result_->switch_variable = variable; |
| } |
| } |
| |
| void ScopeBuilder::VisitVariableGet(intptr_t declaration_binary_offset) { |
| LocalVariable* variable = LookupVariable(declaration_binary_offset); |
| if (variable->is_late()) { |
| // Late variable initializer expressions may also contain local variables |
| // that need to be captured. |
| AlternativeReadingScope alt(&helper_.reader_, variable->late_init_offset()); |
| if (helper_.ReadTag() != kNothing) { |
| VisitExpression(); |
| } |
| } |
| } |
| |
| LocalVariable* ScopeBuilder::LookupVariable( |
| intptr_t declaration_binary_offset) { |
| LocalVariable* variable = result_->locals.Lookup(declaration_binary_offset); |
| if (variable == nullptr) { |
| // 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() != nullptr); |
| |
| const auto& name = Object::null_string(); // only use kernel offset. |
| variable = current_function_scope_->parent()->LookupVariable( |
| name, declaration_binary_offset, true); |
| ASSERT(variable != nullptr); |
| result_->locals.Insert(declaration_binary_offset, variable); |
| } |
| |
| ASSERT(variable->owner() != nullptr); |
| 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()); |
| } |
| return variable; |
| } |
| |
| const String& ScopeBuilder::GenerateName(const char* prefix, intptr_t suffix) { |
| char name[64]; |
| Utils::SNPrint(name, 64, "%s%" Pd "", prefix, suffix); |
| return H.DartSymbolObfuscate(name); |
| } |
| |
| void ScopeBuilder::HandleLoadReceiver() { |
| if (!parsed_function_->has_receiver_var() && |
| current_function_scope_->parent() != nullptr) { |
| // Lazily populate receiver variable using the parent function scope. |
| parsed_function_->set_receiver_var( |
| current_function_scope_->parent()->LookupVariable( |
| Symbols::This(), LocalVariable::kNoKernelOffset, true)); |
| } |
| |
| if ((current_function_scope_->parent() != nullptr) || |
| (scope_->function_level() > 0)) { |
| // Every scope we use the [receiver] 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 [receiver]. |
| scope_->CaptureVariable(parsed_function_->receiver_var()); |
| } |
| |
| parsed_function_->set_receiver_used(); |
| } |
| |
| void ScopeBuilder::HandleSpecialLoad(LocalVariable** variable, |
| const String& symbol, |
| intptr_t kernel_offset) { |
| if (current_function_scope_->parent() != nullptr) { |
| // 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 == nullptr) { |
| *variable = current_function_scope_->parent()->LookupVariable( |
| symbol, kernel_offset, true); |
| ASSERT(*variable != nullptr); |
| } |
| } |
| |
| if ((current_function_scope_->parent() != nullptr) || |
| (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); |
| } |
| } |
| |
| } // namespace kernel |
| } // namespace dart |