| // 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_); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| 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() || field.is_generic_covariant_impl()) | |
| ? 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; | |
| } | |
| default: | |
| 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. | |
| if (helper_.ReadTag() == kSomething) { | |
| helper_.SkipVariable(); // read this_variable. | |
| } | |
| Tag tag = helper_.ReadTag(); // read initializer (part 1). | |
| if (tag == kSomething) { | |
| VisitExpression(); // read initializer (part 2). | |
| } | |
| helper_.SkipScope(); | |
| } | |
| 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(); | |
| } | |
| helper_.SkipScope(); | |
| helper_.SkipCapturedContexts(); | |
| } | |
| void ScopeBuilder::VisitInitializer() { | |
| Tag tag = helper_.ReadTag(); | |
| switch (tag) { | |
| case kInvalidInitializer: | |
| helper_.ReadPosition(); // read position. | |
| helper_.ReadStringReference(); // read message | |
| helper_.ReadByte(); // read flags | |
| return; | |
| case kFieldInitializer: | |
| helper_.ReadPosition(); // read position. | |
| helper_.ReadByte(); // read isSynthetic flag. | |
| helper_.SkipCanonicalNameReference(); // read field_reference. | |
| VisitExpression(); // read value. | |
| return; | |
| case kSuperInitializer: | |
| helper_.ReadPosition(); // read position. | |
| helper_.ReadByte(); // read isSynthetic flag. | |
| 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: | |
| helper_.ReadPosition(); // read position. | |
| VisitVariable(); // read variable. | |
| return; | |
| case kAssertInitializer: | |
| helper_.ReadPosition(); // read position. | |
| 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. | |
| LocalVariable* variable = LookupVariable(variable_kernel_offset); | |
| variable->set_is_effectively_final(false); | |
| VisitExpression(); // read expression. | |
| return; | |
| } | |
| case kSpecializedVariableSet: { | |
| helper_.ReadPosition(); // read position. | |
| intptr_t variable_kernel_offset = | |
| helper_.ReadUInt(); // read kernel position. | |
| LocalVariable* variable = LookupVariable(variable_kernel_offset); | |
| variable->set_is_effectively_final(false); | |
| 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: | |
| helper_.ReadPosition(); // read position. | |
| VisitExpression(); // read receiver. | |
| helper_.SkipName(); // read name. | |
| helper_.SkipInterfaceMemberNameReference(); // read target_reference. | |
| return; | |
| case kSuperPropertySet: | |
| helper_.ReadPosition(); // read position. | |
| VisitExpression(); // read receiver. | |
| 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. | |
| helper_.SkipDartType(); // read result_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: | |
| helper_.ReadPosition(); // read position. | |
| VisitExpression(); // read receiver. | |
| 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. | |
| helper_.ReadUInt(); // read id. | |
| 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. | |
| VisitVariable(); // 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. | |
| helper_.ReadUInt(); // read scope size. | |
| 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. | |
| helper_.SkipScope(); | |
| 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: | |
| case kRedirectingFactoryInvocation: | |
| // 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. | |
| helper_.ReadUInt(); // read scope size. | |
| 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()); | |
| helper_.SkipScope(); | |
| 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. | |
| helper_.ReadUInt(); // read scpoe size. | |
| VisitExpression(); // read condition. | |
| VisitStatement(); // read body. | |
| helper_.SkipScope(); | |
| --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. | |
| helper_.ReadUInt(); // read scope size. | |
| 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. | |
| helper_.SkipScope(); | |
| 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. | |
| helper_.ReadUInt(); // read scope size. | |
| VisitDartType(); // Read the guard. | |
| tag = helper_.ReadTag(); // read first part of exception. | |
| if (tag == kSomething) { | |
| LocalVariable* v = VisitVariable(); // read exception. | |
| ASSERT(v->is_final() && !v->is_late()); | |
| v->set_is_effectively_final(); | |
| } | |
| tag = helper_.ReadTag(); // read first part of stack trace. | |
| if (tag == kSomething) { | |
| LocalVariable* v = VisitVariable(); // read stack trace. | |
| ASSERT(v->is_final() && !v->is_late()); | |
| v->set_is_effectively_final(); | |
| } | |
| VisitStatement(); // read body. | |
| helper_.SkipScope(); | |
| 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 kVariableStatement: | |
| helper_.ReadPosition(); // read position. | |
| VisitVariableDeclaration(); // read variable declaration. | |
| return; | |
| case kFunctionDeclaration: { | |
| intptr_t offset = helper_.ReaderOffset() - 1; // -1 to include tag byte. | |
| helper_.ReadPosition(); // read position. | |
| LocalVariable* v = VisitVariable(); // read variable declaration. | |
| ASSERT(v->is_final() && !v->is_late()); | |
| v->set_is_effectively_final(); | |
| helper_.ReadUInt(); // read id. | |
| 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() { | |
| helper_.ReadTag(); // read tag. | |
| helper_.ReadPosition(); // read position. | |
| helper_.SkipCapturedContexts(); | |
| VisitVariable(); // read variable. | |
| } | |
| LocalVariable* ScopeBuilder::VisitVariable() { | |
| 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()); | |
| VariableHelper helper(&helper_); | |
| helper.ReadUntilExcluding(VariableHelper::kAnnotations); | |
| const intptr_t annotations_offset = helper_.ReaderOffset(); | |
| helper.ReadUntilExcluding(VariableHelper::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(); | |
| } | |
| // Non-late variables with an initializer are initialized as effectively | |
| // final. If there is ever a write to the variable, we reset it to false. | |
| if (!helper.IsLate() && tag == kSomething) { | |
| variable->set_is_effectively_final(); | |
| } | |
| if (helper.IsLate()) { | |
| variable->set_is_late(); | |
| variable->set_late_init_offset(initializer_offset); | |
| } | |
| if (helper.IsSynthesized() || helper.IsWildcard()) { | |
| variable->set_invisible(true); | |
| } | |
| scope_->AddVariable(variable); | |
| result_->locals.Insert(kernel_offset, variable); | |
| return 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 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) { | |
| AddParameter(pos++, type_check_mode, attrs); | |
| } | |
| // List of named. | |
| list_length = helper_.ReadListLength(); // read list length. | |
| for (intptr_t i = 0; i < list_length; ++i) { | |
| AddParameter(pos++, type_check_mode, attrs); | |
| } | |
| } | |
| void ScopeBuilder::AddParameter(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()); | |
| VariableHelper helper(&helper_); | |
| helper.ReadUntilExcluding(VariableHelper::kAnnotations); | |
| const intptr_t annotations_offset = helper_.ReaderOffset(); | |
| helper.ReadUntilExcluding(VariableHelper::kType); | |
| String& name = H.DartSymbolObfuscate(helper.name_index_); | |
| ASSERT(name.Length() > 0); | |
| AbstractType& type = BuildAndVisitVariableType(); // read type. | |
| helper.SetJustRead(VariableHelper::kType); | |
| helper.ReadUntilExcluding(VariableHelper::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(); | |
| } | |
| // Parameters are initialized as effectively final. If there is ever a write | |
| // to the parameter, we reset it to false. | |
| if (!helper.IsLate()) { | |
| variable->set_is_effectively_final(); | |
| } | |
| if (helper.IsCovariant()) { | |
| variable->set_is_explicit_covariant_parameter(); | |
| } | |
| if (helper.IsWildcard()) { | |
| 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; | |
| } | |
| } | |
| LocalVariable* variable = new (Z) LocalVariable( | |
| declaration_pos, token_pos, name, static_type, kernel_offset, | |
| inferred_type, inferred_arg_type, inferred_arg_value); | |
| if (name.ptr() == Symbols::This().ptr()) { | |
| variable->set_is_final(); | |
| variable->set_is_effectively_final(); | |
| } | |
| return variable; | |
| } | |
| 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 |