| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "vm/compiler/frontend/kernel_binary_flowgraph.h" |
| |
| #include "vm/bootstrap.h" |
| #include "vm/code_descriptors.h" |
| #include "vm/compiler/aot/precompiler.h" |
| #include "vm/compiler/assembler/disassembler_kbc.h" |
| #include "vm/compiler/frontend/prologue_builder.h" |
| #include "vm/compiler/jit/compiler.h" |
| #include "vm/dart_entry.h" |
| #include "vm/longjump.h" |
| #include "vm/object_store.h" |
| #include "vm/resolver.h" |
| #include "vm/stack_frame.h" |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| |
| namespace dart { |
| |
| #if defined(DART_USE_INTERPRETER) |
| DEFINE_FLAG(bool, dump_kernel_bytecode, false, "Dump kernel bytecode"); |
| #endif // defined(DART_USE_INTERPRETER) |
| |
| namespace kernel { |
| |
| #define Z (zone_) |
| #define H (translation_helper_) |
| #define T (type_translator_) |
| #define I Isolate::Current() |
| #define B (flow_graph_builder_) |
| |
| static bool IsFieldInitializer(const Function& function, Zone* zone) { |
| return (function.kind() == RawFunction::kImplicitStaticFinalGetter) && |
| String::Handle(zone, function.name()) |
| .StartsWith(Symbols::InitPrefix()); |
| } |
| |
| void FunctionNodeHelper::ReadUntilExcluding(Field field) { |
| if (field <= next_read_) return; |
| |
| // Ordered with fall-through. |
| switch (next_read_) { |
| case kStart: { |
| Tag tag = helper_->ReadTag(); // read tag. |
| ASSERT(tag == kFunctionNode); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kPosition: |
| position_ = helper_->ReadPosition(); // read position. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEndPosition: |
| end_position_ = helper_->ReadPosition(); // read end position. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kAsyncMarker: |
| async_marker_ = static_cast<AsyncMarker>(helper_->ReadByte()); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kDartAsyncMarker: |
| dart_async_marker_ = static_cast<AsyncMarker>( |
| helper_->ReadByte()); // read dart async marker. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kTypeParameters: |
| helper_->SkipTypeParametersList(); // read type parameters. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kTotalParameterCount: |
| total_parameter_count_ = |
| helper_->ReadUInt(); // read total parameter count. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kRequiredParameterCount: |
| required_parameter_count_ = |
| helper_->ReadUInt(); // read required parameter count. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kPositionalParameters: |
| helper_->SkipListOfVariableDeclarations(); // read positionals. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kNamedParameters: |
| helper_->SkipListOfVariableDeclarations(); // read named. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kReturnType: |
| helper_->SkipDartType(); // read return type. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kBody: |
| if (helper_->ReadTag() == kSomething) |
| helper_->SkipStatement(); // read body. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEnd: |
| return; |
| } |
| } |
| |
| void TypeParameterHelper::ReadUntilExcluding(Field field) { |
| for (; next_read_ < field; ++next_read_) { |
| switch (next_read_) { |
| case kFlags: |
| flags_ = helper_->ReadFlags(); |
| break; |
| case kAnnotations: |
| helper_->SkipListOfExpressions(); // read annotations. |
| break; |
| case kName: |
| name_index_ = helper_->ReadStringReference(); // read name index. |
| break; |
| case kBound: |
| helper_->SkipDartType(); |
| break; |
| case kDefaultType: |
| if (helper_->ReadTag() == kSomething) { |
| helper_->SkipDartType(); |
| } |
| break; |
| case kEnd: |
| return; |
| } |
| } |
| } |
| |
| void VariableDeclarationHelper::ReadUntilExcluding(Field field) { |
| if (field <= next_read_) return; |
| |
| // Ordered with fall-through. |
| switch (next_read_) { |
| case kPosition: |
| position_ = helper_->ReadPosition(); // read position. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEqualPosition: |
| equals_position_ = helper_->ReadPosition(); // read equals position. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kAnnotations: |
| helper_->SkipListOfExpressions(); // read annotations. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kFlags: |
| flags_ = helper_->ReadFlags(); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kNameIndex: |
| name_index_ = helper_->ReadStringReference(); // read name index. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kType: |
| helper_->SkipDartType(); // read type. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kInitializer: |
| if (helper_->ReadTag() == kSomething) |
| helper_->SkipExpression(); // read initializer. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEnd: |
| return; |
| } |
| } |
| |
| FieldHelper::FieldHelper(KernelReaderHelper* helper, intptr_t offset) |
| : helper_(helper), |
| next_read_(kStart), |
| has_function_literal_initializer_(false) { |
| helper_->SetOffset(offset); |
| } |
| |
| void FieldHelper::ReadUntilExcluding(Field field, |
| bool detect_function_literal_initializer) { |
| if (field <= next_read_) return; |
| |
| // Ordered with fall-through. |
| switch (next_read_) { |
| case kStart: { |
| Tag tag = helper_->ReadTag(); // read tag. |
| ASSERT(tag == kField); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kCanonicalName: |
| canonical_name_ = |
| helper_->ReadCanonicalNameReference(); // read canonical_name. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kSourceUriIndex: |
| source_uri_index_ = helper_->ReadUInt(); // read source_uri_index. |
| helper_->set_current_script_id(source_uri_index_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kPosition: |
| position_ = helper_->ReadPosition(false); // read position. |
| helper_->RecordTokenPosition(position_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEndPosition: |
| end_position_ = helper_->ReadPosition(false); // read end position. |
| helper_->RecordTokenPosition(end_position_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kFlags: |
| flags_ = helper_->ReadFlags(); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kName: |
| helper_->SkipName(); // read name. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kAnnotations: { |
| annotation_count_ = helper_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < annotation_count_; ++i) { |
| helper_->SkipExpression(); // read ith expression. |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kType: |
| helper_->SkipDartType(); // read type. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kInitializer: |
| if (helper_->ReadTag() == kSomething) { |
| if (detect_function_literal_initializer && |
| helper_->PeekTag() == kFunctionExpression) { |
| AlternativeReadingScope alt(&helper_->reader_); |
| Tag tag = helper_->ReadTag(); |
| ASSERT(tag == kFunctionExpression); |
| helper_->ReadPosition(); // read position. |
| |
| FunctionNodeHelper helper(helper_); |
| helper.ReadUntilIncluding(FunctionNodeHelper::kEndPosition); |
| |
| has_function_literal_initializer_ = true; |
| function_literal_start_ = helper.position_; |
| function_literal_end_ = helper.end_position_; |
| } |
| helper_->SkipExpression(); // read initializer. |
| } |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEnd: |
| return; |
| } |
| } |
| |
| void ProcedureHelper::ReadUntilExcluding(Field field) { |
| if (field <= next_read_) return; |
| |
| // Ordered with fall-through. |
| switch (next_read_) { |
| case kStart: { |
| Tag tag = helper_->ReadTag(); // read tag. |
| ASSERT(tag == kProcedure); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kCanonicalName: |
| canonical_name_ = |
| helper_->ReadCanonicalNameReference(); // read canonical_name. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kSourceUriIndex: |
| source_uri_index_ = helper_->ReadUInt(); // read source_uri_index. |
| helper_->set_current_script_id(source_uri_index_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kPosition: |
| position_ = helper_->ReadPosition(false); // read position. |
| helper_->RecordTokenPosition(position_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEndPosition: |
| end_position_ = helper_->ReadPosition(false); // read end position. |
| helper_->RecordTokenPosition(end_position_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kKind: |
| kind_ = static_cast<Kind>(helper_->ReadByte()); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kFlags: |
| flags_ = helper_->ReadFlags(); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kName: |
| helper_->SkipName(); // read name. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kAnnotations: { |
| annotation_count_ = helper_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < annotation_count_; ++i) { |
| helper_->SkipExpression(); // read ith expression. |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kForwardingStubSuperTarget: |
| if (helper_->ReadTag() == kSomething) { |
| forwarding_stub_super_target_ = helper_->ReadCanonicalNameReference(); |
| } |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kForwardingStubInterfaceTarget: |
| if (helper_->ReadTag() == kSomething) { |
| helper_->ReadCanonicalNameReference(); |
| } |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kFunction: |
| if (helper_->ReadTag() == kSomething) |
| helper_->SkipFunctionNode(); // read function node. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEnd: |
| return; |
| } |
| } |
| |
| void ConstructorHelper::ReadUntilExcluding(Field field) { |
| if (field <= next_read_) return; |
| |
| // Ordered with fall-through. |
| switch (next_read_) { |
| case kStart: { |
| Tag tag = helper_->ReadTag(); // read tag. |
| ASSERT(tag == kConstructor); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kCanonicalName: |
| canonical_name_ = |
| helper_->ReadCanonicalNameReference(); // read canonical_name. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kSourceUriIndex: |
| source_uri_index_ = helper_->ReadUInt(); // read source_uri_index. |
| helper_->set_current_script_id(source_uri_index_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kPosition: |
| position_ = helper_->ReadPosition(); // read position. |
| helper_->RecordTokenPosition(position_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEndPosition: |
| end_position_ = helper_->ReadPosition(); // read end position. |
| helper_->RecordTokenPosition(end_position_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kFlags: |
| flags_ = helper_->ReadFlags(); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kName: |
| helper_->SkipName(); // read name. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kAnnotations: { |
| annotation_count_ = helper_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < annotation_count_; ++i) { |
| helper_->SkipExpression(); // read ith expression. |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kFunction: |
| helper_->SkipFunctionNode(); // read function. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kInitializers: { |
| intptr_t list_length = |
| helper_->ReadListLength(); // read initializers list length. |
| for (intptr_t i = 0; i < list_length; i++) { |
| helper_->SkipInitializer(); |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kEnd: |
| return; |
| } |
| } |
| |
| void ClassHelper::ReadUntilExcluding(Field field) { |
| if (field <= next_read_) return; |
| |
| // Ordered with fall-through. |
| switch (next_read_) { |
| case kStart: { |
| Tag tag = helper_->ReadTag(); // read tag. |
| ASSERT(tag == kClass); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kCanonicalName: |
| canonical_name_ = |
| helper_->ReadCanonicalNameReference(); // read canonical_name. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kSourceUriIndex: |
| source_uri_index_ = helper_->ReadUInt(); // read source_uri_index. |
| helper_->set_current_script_id(source_uri_index_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kPosition: |
| position_ = helper_->ReadPosition(false); // read position. |
| helper_->RecordTokenPosition(position_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEndPosition: |
| end_position_ = helper_->ReadPosition(); // read end position. |
| helper_->RecordTokenPosition(end_position_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kFlags: |
| flags_ = helper_->ReadFlags(); // read flags. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kNameIndex: |
| name_index_ = helper_->ReadStringReference(); // read name index. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kAnnotations: { |
| annotation_count_ = helper_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < annotation_count_; ++i) { |
| helper_->SkipExpression(); // read ith expression. |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kTypeParameters: |
| helper_->SkipTypeParametersList(); // read type parameters. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kSuperClass: { |
| Tag type_tag = helper_->ReadTag(); // read super class type (part 1). |
| if (type_tag == kSomething) { |
| helper_->SkipDartType(); // read super class type (part 2). |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kMixinType: { |
| Tag type_tag = helper_->ReadTag(); // read mixin type (part 1). |
| if (type_tag == kSomething) { |
| helper_->SkipDartType(); // read mixin type (part 2). |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kImplementedClasses: |
| helper_->SkipListOfDartTypes(); // read implemented_classes. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kFields: { |
| intptr_t list_length = |
| helper_->ReadListLength(); // read fields list length. |
| for (intptr_t i = 0; i < list_length; i++) { |
| FieldHelper field_helper(helper_); |
| field_helper.ReadUntilExcluding(FieldHelper::kEnd); // read field. |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kConstructors: { |
| intptr_t list_length = |
| helper_->ReadListLength(); // read constructors list length. |
| for (intptr_t i = 0; i < list_length; i++) { |
| ConstructorHelper constructor_helper(helper_); |
| constructor_helper.ReadUntilExcluding( |
| ConstructorHelper::kEnd); // read constructor. |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kProcedures: { |
| procedure_count_ = helper_->ReadListLength(); // read procedures #. |
| for (intptr_t i = 0; i < procedure_count_; i++) { |
| ProcedureHelper procedure_helper(helper_); |
| procedure_helper.ReadUntilExcluding( |
| ProcedureHelper::kEnd); // read procedure. |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kClassIndex: |
| // Read class index. |
| for (intptr_t i = 0; i < procedure_count_; ++i) { |
| helper_->reader_.ReadUInt32(); |
| } |
| helper_->reader_.ReadUInt32(); |
| helper_->reader_.ReadUInt32(); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEnd: |
| return; |
| } |
| } |
| |
| void LibraryHelper::ReadUntilExcluding(Field field) { |
| if (field <= next_read_) return; |
| |
| // Ordered with fall-through. |
| switch (next_read_) { |
| case kFlags: { |
| flags_ = helper_->ReadFlags(); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kCanonicalName: |
| canonical_name_ = |
| helper_->ReadCanonicalNameReference(); // read canonical_name. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kName: |
| name_index_ = helper_->ReadStringReference(); // read name index. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kSourceUriIndex: |
| source_uri_index_ = helper_->ReadUInt(); // read source_uri_index. |
| helper_->set_current_script_id(source_uri_index_); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kAnnotations: |
| helper_->SkipListOfExpressions(); // read annotations. |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kDependencies: { |
| intptr_t dependency_count = helper_->ReadUInt(); // read list length. |
| for (intptr_t i = 0; i < dependency_count; ++i) { |
| helper_->SkipLibraryDependency(); |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kAdditionalExports: { |
| intptr_t name_count = helper_->ReadUInt(); |
| for (intptr_t i = 0; i < name_count; ++i) { |
| helper_->SkipCanonicalNameReference(); |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kParts: { |
| intptr_t part_count = helper_->ReadUInt(); // read list length. |
| for (intptr_t i = 0; i < part_count; ++i) { |
| helper_->SkipLibraryPart(); |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kTypedefs: { |
| intptr_t typedef_count = helper_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < typedef_count; i++) { |
| helper_->SkipLibraryTypedef(); |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kClasses: { |
| class_count_ = helper_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < class_count_; ++i) { |
| ClassHelper class_helper(helper_); |
| class_helper.ReadUntilExcluding(ClassHelper::kEnd); |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kToplevelField: { |
| intptr_t field_count = helper_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < field_count; ++i) { |
| FieldHelper field_helper(helper_); |
| field_helper.ReadUntilExcluding(FieldHelper::kEnd); |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kToplevelProcedures: { |
| procedure_count_ = helper_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < procedure_count_; ++i) { |
| ProcedureHelper procedure_helper(helper_); |
| procedure_helper.ReadUntilExcluding(ProcedureHelper::kEnd); |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kLibraryIndex: |
| // Read library index. |
| for (intptr_t i = 0; i < class_count_; ++i) { |
| helper_->reader_.ReadUInt32(); |
| } |
| helper_->reader_.ReadUInt32(); |
| helper_->reader_.ReadUInt32(); |
| for (intptr_t i = 0; i < procedure_count_; ++i) { |
| helper_->reader_.ReadUInt32(); |
| } |
| helper_->reader_.ReadUInt32(); |
| helper_->reader_.ReadUInt32(); |
| if (++next_read_ == field) return; |
| /* Falls through */ |
| case kEnd: |
| return; |
| } |
| } |
| |
| void LibraryDependencyHelper::ReadUntilExcluding(Field field) { |
| if (field <= next_read_) return; |
| |
| // Ordered with fall-through. |
| switch (next_read_) { |
| case kFileOffset: { |
| helper_->ReadPosition(); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kFlags: { |
| flags_ = helper_->ReadFlags(); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kAnnotations: { |
| helper_->SkipListOfExpressions(); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kTargetLibrary: { |
| target_library_canonical_name_ = helper_->ReadCanonicalNameReference(); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kName: { |
| name_index_ = helper_->ReadStringReference(); |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kCombinators: { |
| intptr_t count = helper_->ReadListLength(); |
| for (intptr_t i = 0; i < count; ++i) { |
| // Skip flags |
| helper_->SkipBytes(1); |
| // Skip list of names. |
| helper_->SkipListOfStrings(); |
| } |
| if (++next_read_ == field) return; |
| } |
| /* Falls through */ |
| case kEnd: |
| return; |
| } |
| } |
| |
| MetadataHelper::MetadataHelper(StreamingFlowGraphBuilder* builder) |
| : builder_(builder), |
| translation_helper_(builder->translation_helper_), |
| mappings_offset_(0), |
| mappings_num_(0), |
| last_node_offset_(0), |
| last_mapping_index_(0) {} |
| |
| void MetadataHelper::SetMetadataMappings(intptr_t mappings_offset, |
| intptr_t mappings_num) { |
| ASSERT((mappings_offset_ == 0) && (mappings_num_ == 0)); |
| ASSERT((mappings_offset != 0) && (mappings_num != 0)); |
| mappings_offset_ = mappings_offset; |
| mappings_num_ = mappings_num; |
| |
| #ifdef DEBUG |
| // Verify that node offsets are sorted. |
| { |
| Reader reader(H.metadata_mappings()); |
| reader.set_offset(mappings_offset); |
| |
| intptr_t prev_node_offset = 0; |
| for (intptr_t i = 0; i < mappings_num; ++i) { |
| intptr_t node_offset = reader.ReadUInt32(); |
| intptr_t md_offset = reader.ReadUInt32(); |
| |
| ASSERT((node_offset > 0) && (md_offset >= 0)); |
| ASSERT(node_offset > prev_node_offset); |
| prev_node_offset = node_offset; |
| } |
| } |
| #endif // DEBUG |
| |
| last_node_offset_ = kIntptrMax; |
| last_mapping_index_ = 0; |
| } |
| |
| intptr_t MetadataHelper::FindMetadataMapping(intptr_t node_offset) { |
| const intptr_t kUInt32Size = 4; |
| ASSERT(mappings_num_ > 0); |
| |
| Reader reader(H.metadata_mappings()); |
| |
| intptr_t left = 0; |
| intptr_t right = mappings_num_ - 1; |
| while (left < right) { |
| intptr_t mid = ((right - left) / 2) + left; |
| intptr_t mid_node_offset = |
| reader.ReadUInt32At(mappings_offset_ + mid * 2 * kUInt32Size); |
| |
| if (node_offset < mid_node_offset) { |
| right = mid - 1; |
| } else if (node_offset > mid_node_offset) { |
| left = mid + 1; |
| } else { |
| return mid; // Exact match found. |
| } |
| } |
| ASSERT((0 <= left) && (left <= mappings_num_)); |
| |
| // Approximate match is found. Make sure it has an offset greater or equal |
| // to the given node offset. |
| if (left < mappings_num_) { |
| intptr_t found_node_offset = |
| reader.ReadUInt32At(mappings_offset_ + left * 2 * kUInt32Size); |
| |
| if (found_node_offset < node_offset) { |
| ++left; |
| } |
| } |
| ASSERT((left == mappings_num_) || |
| static_cast<intptr_t>(reader.ReadUInt32At( |
| mappings_offset_ + left * 2 * kUInt32Size)) >= node_offset); |
| |
| return left; |
| } |
| |
| intptr_t MetadataHelper::GetNextMetadataPayloadOffset(intptr_t node_offset) { |
| builder_->EnsureMetadataIsScanned(); |
| |
| if (mappings_num_ == 0) { |
| return -1; // No metadata. |
| } |
| |
| node_offset += builder_->data_program_offset_; |
| |
| // Nodes are parsed in linear order most of the time, so do the search |
| // only if looking back. |
| if (node_offset < last_node_offset_) { |
| last_mapping_index_ = FindMetadataMapping(node_offset); |
| } |
| |
| intptr_t index = last_mapping_index_; |
| intptr_t mapping_node_offset = 0; |
| intptr_t mapping_md_offset = -1; |
| |
| Reader reader(H.metadata_mappings()); |
| const intptr_t kUInt32Size = 4; |
| reader.set_offset(mappings_offset_ + index * 2 * kUInt32Size); |
| |
| for (; index < mappings_num_; ++index) { |
| mapping_node_offset = reader.ReadUInt32(); |
| mapping_md_offset = reader.ReadUInt32(); |
| |
| if (mapping_node_offset >= node_offset) { |
| break; |
| } |
| } |
| |
| last_mapping_index_ = index; |
| last_node_offset_ = node_offset; |
| |
| if ((index < mappings_num_) && (mapping_node_offset == node_offset)) { |
| ASSERT(mapping_md_offset >= 0); |
| return mapping_md_offset; |
| } else { |
| return -1; |
| } |
| } |
| |
| bool DirectCallMetadataHelper::ReadMetadata(intptr_t node_offset, |
| NameIndex* target_name, |
| bool* check_receiver_for_null) { |
| intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset); |
| if (md_offset < 0) { |
| return false; |
| } |
| |
| AlternativeReadingScope alt(&builder_->reader_, &H.metadata_payloads(), |
| md_offset); |
| |
| *target_name = builder_->ReadCanonicalNameReference(); |
| *check_receiver_for_null = builder_->ReadBool(); |
| return true; |
| } |
| |
| DirectCallMetadata DirectCallMetadataHelper::GetDirectTargetForPropertyGet( |
| intptr_t node_offset) { |
| NameIndex kernel_name; |
| bool check_receiver_for_null = false; |
| if (!ReadMetadata(node_offset, &kernel_name, &check_receiver_for_null)) { |
| return DirectCallMetadata(Function::null_function(), false); |
| } |
| |
| if (H.IsProcedure(kernel_name) && !H.IsGetter(kernel_name)) { |
| // Tear-off. Use method extractor as direct call target. |
| const String& method_name = H.DartMethodName(kernel_name); |
| const Function& target_method = Function::ZoneHandle( |
| builder_->zone_, |
| builder_->LookupMethodByMember(kernel_name, method_name)); |
| const String& getter_name = H.DartGetterName(kernel_name); |
| return DirectCallMetadata( |
| Function::ZoneHandle(builder_->zone_, |
| target_method.GetMethodExtractor(getter_name)), |
| check_receiver_for_null); |
| } else { |
| const String& getter_name = H.DartGetterName(kernel_name); |
| const Function& target = Function::ZoneHandle( |
| builder_->zone_, |
| builder_->LookupMethodByMember(kernel_name, getter_name)); |
| ASSERT(target.IsGetterFunction() || target.IsImplicitGetterFunction()); |
| return DirectCallMetadata(target, check_receiver_for_null); |
| } |
| } |
| |
| DirectCallMetadata DirectCallMetadataHelper::GetDirectTargetForPropertySet( |
| intptr_t node_offset) { |
| NameIndex kernel_name; |
| bool check_receiver_for_null = false; |
| if (!ReadMetadata(node_offset, &kernel_name, &check_receiver_for_null)) { |
| return DirectCallMetadata(Function::null_function(), false); |
| } |
| |
| const String& method_name = H.DartSetterName(kernel_name); |
| const Function& target = Function::ZoneHandle( |
| builder_->zone_, |
| builder_->LookupMethodByMember(kernel_name, method_name)); |
| ASSERT(target.IsSetterFunction() || target.IsImplicitSetterFunction()); |
| |
| return DirectCallMetadata(target, check_receiver_for_null); |
| } |
| |
| DirectCallMetadata DirectCallMetadataHelper::GetDirectTargetForMethodInvocation( |
| intptr_t node_offset) { |
| NameIndex kernel_name; |
| bool check_receiver_for_null = false; |
| if (!ReadMetadata(node_offset, &kernel_name, &check_receiver_for_null)) { |
| return DirectCallMetadata(Function::null_function(), false); |
| } |
| |
| const String& method_name = H.DartProcedureName(kernel_name); |
| const Function& target = Function::ZoneHandle( |
| builder_->zone_, |
| builder_->LookupMethodByMember(kernel_name, method_name)); |
| |
| return DirectCallMetadata(target, check_receiver_for_null); |
| } |
| |
| bool ProcedureAttributesMetadataHelper::ReadMetadata( |
| intptr_t node_offset, |
| ProcedureAttributesMetadata* metadata) { |
| intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset); |
| if (md_offset < 0) { |
| return false; |
| } |
| |
| AlternativeReadingScope alt(&builder_->reader_, &H.metadata_payloads(), |
| md_offset); |
| |
| const int kDynamicUsesBit = 1 << 0; |
| const int kNonThisUsesBit = 1 << 1; |
| const int kTearOffUsesBit = 1 << 2; |
| |
| const uint8_t flags = builder_->ReadByte(); |
| metadata->has_dynamic_invocations = |
| (flags & kDynamicUsesBit) == kDynamicUsesBit; |
| metadata->has_non_this_uses = (flags & kNonThisUsesBit) == kNonThisUsesBit; |
| metadata->has_tearoff_uses = (flags & kTearOffUsesBit) == kTearOffUsesBit; |
| return true; |
| } |
| |
| ProcedureAttributesMetadata |
| ProcedureAttributesMetadataHelper::GetProcedureAttributes( |
| intptr_t node_offset) { |
| ProcedureAttributesMetadata metadata; |
| ReadMetadata(node_offset, &metadata); |
| return metadata; |
| } |
| |
| InferredTypeMetadata InferredTypeMetadataHelper::GetInferredType( |
| intptr_t node_offset) { |
| const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset); |
| if (md_offset < 0) { |
| return InferredTypeMetadata(kDynamicCid, true); |
| } |
| |
| AlternativeReadingScope alt(&builder_->reader_, &H.metadata_payloads(), |
| md_offset); |
| |
| const NameIndex kernel_name = builder_->ReadCanonicalNameReference(); |
| const bool nullable = builder_->ReadBool(); |
| |
| if (H.IsRoot(kernel_name)) { |
| return InferredTypeMetadata(kDynamicCid, nullable); |
| } |
| |
| const Class& klass = |
| Class::Handle(builder_->zone_, H.LookupClassByKernelClass(kernel_name)); |
| ASSERT(!klass.IsNull()); |
| |
| intptr_t cid = klass.id(); |
| if (cid == kClosureCid) { |
| // VM uses more specific function types and doesn't expect instances of |
| // _Closure class, so inferred _Closure class doesn't make sense for the VM. |
| cid = kDynamicCid; |
| } |
| |
| return InferredTypeMetadata(cid, nullable); |
| } |
| |
| #if defined(DART_USE_INTERPRETER) |
| void BytecodeMetadataHelper::ReadMetadata(const Function& function) { |
| const intptr_t node_offset = function.kernel_offset(); |
| const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset); |
| if (md_offset < 0) { |
| return; |
| } |
| |
| AlternativeReadingScope alt(&builder_->reader_, &H.metadata_payloads(), |
| md_offset); |
| |
| // Create object pool and read pool entries. |
| const intptr_t obj_count = builder_->reader_.ReadListLength(); |
| const ObjectPool& pool = |
| ObjectPool::Handle(builder_->zone_, ObjectPool::New(obj_count)); |
| ReadPoolEntries(function, function, pool, 0); |
| |
| // Read bytecode and attach to function. |
| const Code& bytecode = Code::Handle(builder_->zone_, ReadBytecode(pool)); |
| function.AttachBytecode(bytecode); |
| |
| // Read exceptions table. |
| ReadExceptionsTable(bytecode); |
| |
| if (FLAG_dump_kernel_bytecode) { |
| KernelBytecodeDisassembler::Disassemble(function); |
| } |
| |
| // Read closures. |
| Function& closure = Function::Handle(builder_->zone_); |
| Code& closure_bytecode = Code::Handle(builder_->zone_); |
| intptr_t num_closures = builder_->ReadListLength(); |
| for (intptr_t i = 0; i < num_closures; i++) { |
| intptr_t closure_index = builder_->ReadUInt(); |
| ASSERT(closure_index < obj_count); |
| closure ^= pool.ObjectAt(closure_index); |
| |
| // Read closure bytecode and attach to closure function. |
| closure_bytecode = ReadBytecode(pool); |
| closure.AttachBytecode(closure_bytecode); |
| |
| // Read closure exceptions table. |
| ReadExceptionsTable(closure_bytecode); |
| |
| if (FLAG_dump_kernel_bytecode) { |
| KernelBytecodeDisassembler::Disassemble(closure); |
| } |
| } |
| } |
| |
| intptr_t BytecodeMetadataHelper::ReadPoolEntries(const Function& function, |
| const Function& inner_function, |
| const ObjectPool& pool, |
| intptr_t from_index) { |
| // These enums and the code below reading the constant pool from kernel must |
| // be kept in sync with pkg/vm/lib/bytecode/constant_pool.dart. |
| enum ConstantPoolTag { |
| kInvalid, |
| kNull, |
| kString, |
| kInt, |
| kDouble, |
| kBool, |
| kArgDesc, |
| kICData, |
| kStaticICData, |
| kField, |
| kFieldOffset, |
| kClass, |
| kTypeArgumentsFieldOffset, |
| kTearOff, |
| kType, |
| kTypeArguments, |
| kList, |
| kInstance, |
| kSymbol, |
| kTypeArgumentsForInstanceAllocation, |
| kContextOffset, |
| kClosureFunction, |
| kEndClosureFunctionScope, |
| kNativeEntry, |
| kSubtypeTestCache, |
| }; |
| |
| enum InvocationKind { |
| method, // x.foo(...) or foo(...) |
| getter, // x.foo |
| setter // x.foo = ... |
| }; |
| |
| Object& obj = Object::Handle(builder_->zone_); |
| Object& elem = Object::Handle(builder_->zone_); |
| Array& array = Array::Handle(builder_->zone_); |
| Field& field = Field::Handle(builder_->zone_); |
| Class& cls = Class::Handle(builder_->zone_); |
| String& name = String::Handle(builder_->zone_); |
| TypeArguments& type_args = TypeArguments::Handle(builder_->zone_); |
| const intptr_t obj_count = pool.Length(); |
| for (intptr_t i = from_index; i < obj_count; ++i) { |
| const intptr_t tag = builder_->ReadTag(); |
| switch (tag) { |
| case ConstantPoolTag::kInvalid: |
| UNREACHABLE(); |
| case ConstantPoolTag::kNull: |
| obj = Object::null(); |
| break; |
| case ConstantPoolTag::kString: |
| obj = H.DartString(builder_->ReadStringReference()).raw(); |
| ASSERT(obj.IsString()); |
| obj = H.Canonicalize(String::Cast(obj)); |
| break; |
| case ConstantPoolTag::kInt: { |
| uint32_t low_bits = builder_->ReadUInt32(); |
| int64_t value = builder_->ReadUInt32(); |
| value = (value << 32) | low_bits; |
| obj = Integer::New(value); |
| } break; |
| case ConstantPoolTag::kDouble: { |
| uint32_t low_bits = builder_->ReadUInt32(); |
| uint64_t bits = builder_->ReadUInt32(); |
| bits = (bits << 32) | low_bits; |
| double value = bit_cast<double, uint64_t>(bits); |
| obj = Double::New(value); |
| } break; |
| case ConstantPoolTag::kBool: |
| if (builder_->ReadUInt() == 1) { |
| obj = Bool::True().raw(); |
| } else { |
| obj = Bool::False().raw(); |
| } |
| break; |
| case ConstantPoolTag::kArgDesc: { |
| intptr_t num_arguments = builder_->ReadUInt(); |
| intptr_t num_type_args = builder_->ReadUInt(); |
| intptr_t num_arg_names = builder_->ReadListLength(); |
| if (num_arg_names == 0) { |
| obj = ArgumentsDescriptor::New(num_type_args, num_arguments); |
| } else { |
| array = Array::New(num_arg_names); |
| for (intptr_t j = 0; j < num_arg_names; j++) { |
| array.SetAt(j, H.DartSymbolPlain(builder_->ReadStringReference())); |
| } |
| obj = ArgumentsDescriptor::New(num_type_args, num_arguments, array); |
| } |
| } break; |
| case ConstantPoolTag::kICData: { |
| InvocationKind kind = static_cast<InvocationKind>(builder_->ReadByte()); |
| if (kind == InvocationKind::getter) { |
| name = builder_->ReadNameAsGetterName().raw(); |
| } else if (kind == InvocationKind::setter) { |
| name = builder_->ReadNameAsSetterName().raw(); |
| } else { |
| ASSERT(kind == InvocationKind::method); |
| name = builder_->ReadNameAsMethodName().raw(); |
| } |
| intptr_t arg_desc_index = builder_->ReadUInt(); |
| ASSERT(arg_desc_index < i); |
| array ^= pool.ObjectAt(arg_desc_index); |
| // TODO(regis): Should num_args_tested be explicitly provided? |
| obj = ICData::New(function, name, |
| array, // Arguments descriptor. |
| Thread::kNoDeoptId, 1 /* num_args_tested */, |
| ICData::RebindRule::kInstance); |
| #if defined(TAG_IC_DATA) |
| ICData::Cast(obj).set_tag(Instruction::kInstanceCall); |
| #endif |
| } break; |
| case ConstantPoolTag::kStaticICData: { |
| InvocationKind kind = static_cast<InvocationKind>(builder_->ReadByte()); |
| NameIndex target = builder_->ReadCanonicalNameReference(); |
| if (H.IsConstructor(target)) { |
| name = H.DartConstructorName(target).raw(); |
| elem = H.LookupConstructorByKernelConstructor(target); |
| } else if (H.IsField(target)) { |
| if (kind == InvocationKind::getter) { |
| name = H.DartGetterName(target).raw(); |
| } else if (kind == InvocationKind::setter) { |
| name = H.DartSetterName(target).raw(); |
| } else { |
| ASSERT(kind == InvocationKind::method); |
| UNIMPLEMENTED(); // TODO(regis): Revisit. |
| } |
| field = H.LookupFieldByKernelField(target); |
| cls = field.Owner(); |
| elem = cls.LookupFunctionAllowPrivate(name); |
| } else { |
| if ((kind == InvocationKind::method) && H.IsGetter(target)) { |
| UNIMPLEMENTED(); // TODO(regis): Revisit. |
| } |
| name = H.DartProcedureName(target).raw(); |
| elem = H.LookupStaticMethodByKernelProcedure(target); |
| } |
| ASSERT(elem.IsFunction()); |
| intptr_t arg_desc_index = builder_->ReadUInt(); |
| ASSERT(arg_desc_index < i); |
| array ^= pool.ObjectAt(arg_desc_index); |
| obj = ICData::New(function, name, |
| array, // Arguments descriptor. |
| Thread::kNoDeoptId, 0 /* num_args_tested */, |
| ICData::RebindRule::kStatic); |
| ICData::Cast(obj).AddTarget(Function::Cast(elem)); |
| #if defined(TAG_IC_DATA) |
| ICData::Cast(obj).set_tag(Instruction::kStaticCall); |
| #endif |
| } break; |
| case ConstantPoolTag::kField: |
| obj = |
| H.LookupFieldByKernelField(builder_->ReadCanonicalNameReference()); |
| ASSERT(obj.IsField()); |
| break; |
| case ConstantPoolTag::kFieldOffset: |
| obj = |
| H.LookupFieldByKernelField(builder_->ReadCanonicalNameReference()); |
| ASSERT(obj.IsField()); |
| obj = Smi::New(Field::Cast(obj).Offset() / kWordSize); |
| break; |
| case ConstantPoolTag::kClass: |
| obj = |
| H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference()); |
| ASSERT(obj.IsClass()); |
| break; |
| case ConstantPoolTag::kTypeArgumentsFieldOffset: |
| cls = |
| H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference()); |
| obj = Smi::New(cls.type_arguments_field_offset() / kWordSize); |
| break; |
| case ConstantPoolTag::kTearOff: |
| obj = H.LookupStaticMethodByKernelProcedure( |
| builder_->ReadCanonicalNameReference()); |
| ASSERT(obj.IsFunction()); |
| obj = Function::Cast(obj).ImplicitClosureFunction(); |
| ASSERT(obj.IsFunction()); |
| obj = Function::Cast(obj).ImplicitStaticClosure(); |
| ASSERT(obj.IsInstance()); |
| obj = H.Canonicalize(Instance::Cast(obj)); |
| break; |
| case ConstantPoolTag::kType: |
| obj = builder_->type_translator_.BuildType().raw(); |
| ASSERT(obj.IsAbstractType()); |
| break; |
| case ConstantPoolTag::kTypeArguments: |
| obj = builder_->type_translator_ |
| .BuildTypeArguments(builder_->ReadListLength()) |
| .raw(); |
| ASSERT(obj.IsNull() || obj.IsTypeArguments()); |
| break; |
| case ConstantPoolTag::kList: { |
| obj = builder_->type_translator_.BuildType().raw(); |
| ASSERT(obj.IsAbstractType()); |
| const intptr_t length = builder_->ReadListLength(); |
| array = Array::New(length, AbstractType::Cast(obj)); |
| for (intptr_t j = 0; j < length; j++) { |
| intptr_t elem_index = builder_->ReadUInt(); |
| ASSERT(elem_index < i); |
| elem = pool.ObjectAt(elem_index); |
| array.SetAt(j, elem); |
| } |
| obj = H.Canonicalize(Array::Cast(array)); |
| ASSERT(!obj.IsNull()); |
| } break; |
| case ConstantPoolTag::kInstance: { |
| cls = |
| H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference()); |
| obj = Instance::New(cls, Heap::kOld); |
| intptr_t type_args_index = builder_->ReadUInt(); |
| ASSERT(type_args_index < i); |
| type_args ^= pool.ObjectAt(type_args_index); |
| if (!type_args.IsNull()) { |
| Instance::Cast(obj).SetTypeArguments(type_args); |
| } |
| intptr_t num_fields = builder_->ReadUInt(); |
| for (intptr_t j = 0; j < num_fields; j++) { |
| NameIndex field_name = builder_->ReadCanonicalNameReference(); |
| ASSERT(H.IsField(field_name)); |
| field = H.LookupFieldByKernelField(field_name); |
| intptr_t elem_index = builder_->ReadUInt(); |
| ASSERT(elem_index < i); |
| elem = pool.ObjectAt(elem_index); |
| Instance::Cast(obj).SetField(field, elem); |
| } |
| obj = H.Canonicalize(Instance::Cast(obj)); |
| } break; |
| case ConstantPoolTag::kSymbol: |
| obj = H.DartSymbolPlain(builder_->ReadStringReference()).raw(); |
| ASSERT(String::Cast(obj).IsSymbol()); |
| break; |
| case ConstantPoolTag::kTypeArgumentsForInstanceAllocation: { |
| cls = |
| H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference()); |
| obj = |
| builder_->type_translator_ |
| .BuildInstantiatedTypeArguments(cls, builder_->ReadListLength()) |
| .raw(); |
| ASSERT(obj.IsNull() || obj.IsTypeArguments()); |
| } break; |
| case ConstantPoolTag::kContextOffset: { |
| intptr_t index = builder_->ReadUInt(); |
| if (i == 0) { |
| obj = Smi::New(Context::parent_offset() / kWordSize); |
| } else { |
| obj = Smi::New(Context::variable_offset(index - 1) / kWordSize); |
| } |
| } break; |
| case ConstantPoolTag::kClosureFunction: { |
| name = H.DartSymbolPlain(builder_->ReadStringReference()).raw(); |
| const Function& closure = Function::Handle( |
| builder_->zone_, |
| Function::NewClosureFunction(name, inner_function, |
| TokenPosition::kNoSource)); |
| |
| FunctionNodeHelper function_node_helper(builder_); |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kTypeParameters); |
| builder_->LoadAndSetupTypeParameters(builder_->active_class(), closure, |
| builder_->ReadListLength(), |
| closure); |
| function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters); |
| |
| // Scope remains opened until ConstantPoolTag::kEndClosureFunctionScope. |
| ActiveTypeParametersScope scope( |
| builder_->active_class(), &closure, |
| TypeArguments::Handle(builder_->zone_, closure.type_parameters()), |
| builder_->zone_); |
| |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| |
| intptr_t required_parameter_count = |
| function_node_helper.required_parameter_count_; |
| intptr_t total_parameter_count = |
| function_node_helper.total_parameter_count_; |
| |
| intptr_t positional_parameter_count = builder_->ReadListLength(); |
| |
| intptr_t named_parameter_count = |
| total_parameter_count - positional_parameter_count; |
| |
| const intptr_t extra_parameters = 1; |
| closure.set_num_fixed_parameters(extra_parameters + |
| required_parameter_count); |
| if (named_parameter_count > 0) { |
| closure.SetNumOptionalParameters(named_parameter_count, false); |
| } else { |
| closure.SetNumOptionalParameters( |
| positional_parameter_count - required_parameter_count, true); |
| } |
| intptr_t parameter_count = extra_parameters + total_parameter_count; |
| closure.set_parameter_types(Array::Handle( |
| builder_->zone_, Array::New(parameter_count, Heap::kOld))); |
| closure.set_parameter_names(Array::Handle( |
| builder_->zone_, Array::New(parameter_count, Heap::kOld))); |
| |
| intptr_t pos = 0; |
| closure.SetParameterTypeAt(pos, AbstractType::dynamic_type()); |
| closure.SetParameterNameAt(pos, Symbols::ClosureParameter()); |
| pos++; |
| |
| const Library& lib = Library::Handle( |
| builder_->zone_, builder_->active_class()->klass->library()); |
| for (intptr_t j = 0; j < positional_parameter_count; ++j, ++pos) { |
| VariableDeclarationHelper helper(builder_); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
| const AbstractType& type = |
| builder_->type_translator_.BuildVariableType(); |
| Tag tag = builder_->ReadTag(); // read (first part of) initializer. |
| if (tag == kSomething) { |
| builder_->SkipExpression(); // read (actual) initializer. |
| } |
| |
| closure.SetParameterTypeAt(pos, type); |
| closure.SetParameterNameAt(pos, |
| H.DartIdentifier(lib, helper.name_index_)); |
| } |
| |
| intptr_t named_parameter_count_check = builder_->ReadListLength(); |
| ASSERT(named_parameter_count_check == named_parameter_count); |
| for (intptr_t j = 0; j < named_parameter_count; ++j, ++pos) { |
| VariableDeclarationHelper helper(builder_); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
| const AbstractType& type = |
| builder_->type_translator_.BuildVariableType(); |
| Tag tag = builder_->ReadTag(); // read (first part of) initializer. |
| if (tag == kSomething) { |
| builder_->SkipExpression(); // read (actual) initializer. |
| } |
| |
| closure.SetParameterTypeAt(pos, type); |
| closure.SetParameterNameAt(pos, |
| H.DartIdentifier(lib, helper.name_index_)); |
| } |
| |
| function_node_helper.SetJustRead(FunctionNodeHelper::kNamedParameters); |
| |
| const AbstractType& return_type = |
| builder_->type_translator_.BuildVariableType(); |
| closure.set_result_type(return_type); |
| function_node_helper.SetJustRead(FunctionNodeHelper::kReturnType); |
| // The closure has no body. |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd); |
| |
| pool.SetTypeAt(i, ObjectPool::kTaggedObject); |
| pool.SetObjectAt(i, closure); |
| |
| // Continue reading the constant pool entries inside the opened |
| // ActiveTypeParametersScope until the scope gets closed by a |
| // kEndClosureFunctionScope tag, in which case control returns here. |
| i = ReadPoolEntries(function, closure, pool, i + 1); |
| // Pool entry at index i has been set to null, because it was a |
| // kEndClosureFunctionScope. |
| ASSERT(pool.ObjectAt(i) == Object::null()); |
| continue; |
| } |
| case ConstantPoolTag::kEndClosureFunctionScope: { |
| // Entry is not used and set to null. |
| obj = Object::null(); |
| pool.SetTypeAt(i, ObjectPool::kTaggedObject); |
| pool.SetObjectAt(i, obj); |
| return i; // The caller will close the scope. |
| } break; |
| case ConstantPoolTag::kNativeEntry: { |
| name = H.DartString(builder_->ReadStringReference()).raw(); |
| obj = NativeEntry(function, name); |
| } break; |
| case ConstantPoolTag::kSubtypeTestCache: { |
| obj = SubtypeTestCache::New(); |
| } break; |
| default: |
| UNREACHABLE(); |
| } |
| pool.SetTypeAt(i, ObjectPool::kTaggedObject); |
| pool.SetObjectAt(i, obj); |
| } |
| // Return the index of the last read pool entry. |
| return obj_count - 1; |
| } |
| |
| RawCode* BytecodeMetadataHelper::ReadBytecode(const ObjectPool& pool) { |
| // TODO(regis): Avoid copying bytecode from mapped kernel binary. |
| intptr_t size = builder_->reader_.ReadUInt(); |
| intptr_t offset = builder_->reader_.offset(); |
| uint8_t* data = |
| builder_->reader_.CopyDataIntoZone(builder_->zone_, offset, size); |
| builder_->reader_.set_offset(offset + size); |
| |
| // Create and return code object. |
| return Code::FinalizeBytecode(reinterpret_cast<void*>(data), size, pool); |
| } |
| |
| void BytecodeMetadataHelper::ReadExceptionsTable(const Code& bytecode) { |
| const intptr_t try_block_count = builder_->reader_.ReadListLength(); |
| if (try_block_count > 0) { |
| const ObjectPool& pool = |
| ObjectPool::Handle(builder_->zone_, bytecode.object_pool()); |
| AbstractType& handler_type = AbstractType::Handle(builder_->zone_); |
| Array& handler_types = Array::Handle(builder_->zone_); |
| DescriptorList* pc_descriptors_list = |
| new (builder_->zone_) DescriptorList(64); |
| ExceptionHandlerList* exception_handlers_list = |
| new (builder_->zone_) ExceptionHandlerList(); |
| |
| // Encoding of ExceptionsTable is described in |
| // pkg/vm/lib/bytecode/exceptions.dart. |
| for (intptr_t try_index = 0; try_index < try_block_count; try_index++) { |
| intptr_t outer_try_index_plus1 = builder_->reader_.ReadUInt(); |
| intptr_t outer_try_index = outer_try_index_plus1 - 1; |
| intptr_t start_pc = builder_->reader_.ReadUInt(); |
| intptr_t end_pc = builder_->reader_.ReadUInt(); |
| intptr_t handler_pc = builder_->reader_.ReadUInt(); |
| uint8_t flags = builder_->reader_.ReadByte(); |
| const uint8_t kFlagNeedsStackTrace = 1 << 0; |
| const uint8_t kFlagIsSynthetic = 1 << 1; |
| const bool needs_stacktrace = (flags & kFlagNeedsStackTrace) != 0; |
| const bool is_generated = (flags & kFlagIsSynthetic) != 0; |
| intptr_t type_count = builder_->reader_.ReadListLength(); |
| ASSERT(type_count > 0); |
| handler_types = Array::New(type_count, Heap::kOld); |
| for (intptr_t i = 0; i < type_count; i++) { |
| intptr_t type_index = builder_->reader_.ReadUInt(); |
| ASSERT(type_index < pool.Length()); |
| handler_type ^= pool.ObjectAt(type_index); |
| handler_types.SetAt(i, handler_type); |
| } |
| pc_descriptors_list->AddDescriptor(RawPcDescriptors::kOther, start_pc, |
| Thread::kNoDeoptId, |
| TokenPosition::kNoSource, try_index); |
| pc_descriptors_list->AddDescriptor(RawPcDescriptors::kOther, end_pc, |
| Thread::kNoDeoptId, |
| TokenPosition::kNoSource, -1); |
| |
| exception_handlers_list->AddHandler( |
| try_index, outer_try_index, handler_pc, TokenPosition::kNoSource, |
| is_generated, handler_types, needs_stacktrace); |
| } |
| const PcDescriptors& descriptors = PcDescriptors::Handle( |
| builder_->zone_, |
| pc_descriptors_list->FinalizePcDescriptors(bytecode.PayloadStart())); |
| bytecode.set_pc_descriptors(descriptors); |
| const ExceptionHandlers& handlers = ExceptionHandlers::Handle( |
| builder_->zone_, exception_handlers_list->FinalizeExceptionHandlers( |
| bytecode.PayloadStart())); |
| bytecode.set_exception_handlers(handlers); |
| } else { |
| bytecode.set_pc_descriptors(Object::empty_descriptors()); |
| bytecode.set_exception_handlers(Object::empty_exception_handlers()); |
| } |
| } |
| |
| RawTypedData* BytecodeMetadataHelper::NativeEntry(const Function& function, |
| const String& external_name) { |
| Zone* zone = builder_->zone_; |
| MethodRecognizer::Kind kind = MethodRecognizer::RecognizeKind(function); |
| // This list of recognized methods must be kept in sync with the list of |
| // methods handled specially by the NativeCall bytecode in the interpreter. |
| switch (kind) { |
| case MethodRecognizer::kObjectEquals: |
| case MethodRecognizer::kStringBaseLength: |
| case MethodRecognizer::kStringBaseIsEmpty: |
| case MethodRecognizer::kGrowableArrayLength: |
| case MethodRecognizer::kObjectArrayLength: |
| case MethodRecognizer::kImmutableArrayLength: |
| case MethodRecognizer::kTypedDataLength: |
| case MethodRecognizer::kClassIDgetID: |
| case MethodRecognizer::kGrowableArrayCapacity: |
| case MethodRecognizer::kListFactory: |
| case MethodRecognizer::kObjectArrayAllocate: |
| case MethodRecognizer::kLinkedHashMap_getIndex: |
| case MethodRecognizer::kLinkedHashMap_setIndex: |
| case MethodRecognizer::kLinkedHashMap_getData: |
| case MethodRecognizer::kLinkedHashMap_setData: |
| case MethodRecognizer::kLinkedHashMap_getHashMask: |
| case MethodRecognizer::kLinkedHashMap_setHashMask: |
| case MethodRecognizer::kLinkedHashMap_getUsedData: |
| case MethodRecognizer::kLinkedHashMap_setUsedData: |
| case MethodRecognizer::kLinkedHashMap_getDeletedKeys: |
| case MethodRecognizer::kLinkedHashMap_setDeletedKeys: |
| break; |
| default: |
| kind = MethodRecognizer::kUnknown; |
| } |
| NativeFunctionWrapper trampoline = NULL; |
| NativeFunction native_function = NULL; |
| intptr_t argc_tag = 0; |
| if (kind == MethodRecognizer::kUnknown) { |
| if (FLAG_link_natives_lazily) { |
| trampoline = &NativeEntry::BootstrapNativeCallWrapper; |
| native_function = |
| reinterpret_cast<NativeFunction>(&NativeEntry::LinkNativeCall); |
| } else { |
| const Class& cls = Class::Handle(zone, function.Owner()); |
| const Library& library = Library::Handle(zone, cls.library()); |
| Dart_NativeEntryResolver resolver = library.native_entry_resolver(); |
| const bool is_bootstrap_native = Bootstrap::IsBootstrapResolver(resolver); |
| const int num_params = |
| NativeArguments::ParameterCountForResolution(function); |
| bool is_auto_scope = true; |
| native_function = NativeEntry::ResolveNative(library, external_name, |
| num_params, &is_auto_scope); |
| ASSERT(native_function != NULL); // TODO(regis): Should we throw instead? |
| if (is_bootstrap_native) { |
| trampoline = &NativeEntry::BootstrapNativeCallWrapper; |
| } else if (is_auto_scope) { |
| trampoline = &NativeEntry::AutoScopeNativeCallWrapper; |
| } else { |
| trampoline = &NativeEntry::NoScopeNativeCallWrapper; |
| } |
| } |
| argc_tag = NativeArguments::ComputeArgcTag(function); |
| } |
| // TODO(regis): Introduce a new VM class subclassing Object and containing |
| // these four untagged values. |
| #ifdef ARCH_IS_32_BIT |
| const TypedData& native_entry = TypedData::Handle( |
| zone, TypedData::New(kTypedDataUint32ArrayCid, 4, Heap::kOld)); |
| native_entry.SetUint32(0 << 2, static_cast<uint32_t>(kind)); |
| native_entry.SetUint32(1 << 2, reinterpret_cast<uint32_t>(trampoline)); |
| native_entry.SetUint32(2 << 2, reinterpret_cast<uint32_t>(native_function)); |
| native_entry.SetUint32(3 << 2, static_cast<uint32_t>(argc_tag)); |
| #else |
| const TypedData& native_entry = TypedData::Handle( |
| zone, TypedData::New(kTypedDataUint64ArrayCid, 4, Heap::kOld)); |
| native_entry.SetUint64(0 << 3, static_cast<uint64_t>(kind)); |
| native_entry.SetUint64(1 << 3, reinterpret_cast<uint64_t>(trampoline)); |
| native_entry.SetUint64(2 << 3, reinterpret_cast<uint64_t>(native_function)); |
| native_entry.SetUint64(3 << 3, static_cast<uint64_t>(argc_tag)); |
| #endif |
| return native_entry.raw(); |
| } |
| #endif // defined(DART_USE_INTERPRETER) |
| |
| StreamingScopeBuilder::StreamingScopeBuilder(ParsedFunction* parsed_function) |
| : result_(NULL), |
| parsed_function_(parsed_function), |
| translation_helper_(Thread::Current()), |
| zone_(translation_helper_.zone()), |
| current_function_scope_(NULL), |
| scope_(NULL), |
| depth_(0), |
| name_index_(0), |
| needs_expr_temp_(false), |
| builder_(new StreamingFlowGraphBuilder( |
| &translation_helper_, |
| Script::Handle(Z, parsed_function->function().script()), |
| zone_, |
| TypedData::Handle(Z, parsed_function->function().KernelData()), |
| parsed_function->function().KernelDataProgramOffset(), |
| &active_class_)), |
| type_translator_(builder_, /*finalize=*/true) { |
| H.InitFromScript(builder_->script()); |
| ASSERT(type_translator_.active_class_ == &active_class_); |
| ASSERT(builder_->type_translator_.active_class_ == &active_class_); |
| } |
| |
| StreamingScopeBuilder::~StreamingScopeBuilder() { |
| delete builder_; |
| } |
| |
| ScopeBuildingResult* StreamingScopeBuilder::BuildScopes() { |
| if (result_ != NULL) return result_; |
| |
| ASSERT(scope_ == NULL && depth_.loop_ == 0 && depth_.function_ == 0); |
| result_ = new (Z) ScopeBuildingResult(); |
| |
| const Function& function = parsed_function_->function(); |
| |
| // Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used |
| // e.g. for type translation. |
| const Class& klass = Class::Handle(zone_, function.Owner()); |
| |
| Function& outermost_function = Function::Handle(Z); |
| builder_->DiscoverEnclosingElements(Z, function, &outermost_function); |
| |
| ActiveClassScope active_class_scope(&active_class_, &klass); |
| ActiveMemberScope active_member(&active_class_, &outermost_function); |
| ActiveTypeParametersScope active_type_params(&active_class_, function, Z); |
| |
| LocalScope* enclosing_scope = NULL; |
| 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.GetCanonicalType(klass); |
| result_->this_variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), klass_type); |
| result_->this_variable->set_index(VariableIndex(0)); |
| result_->this_variable->set_is_captured(); |
| enclosing_scope = new (Z) LocalScope(NULL, 0, 0); |
| enclosing_scope->set_context_level(0); |
| enclosing_scope->AddVariable(result_->this_variable); |
| } else if (function.IsLocalFunction()) { |
| enclosing_scope = LocalScope::RestoreOuterScope( |
| ContextScope::Handle(Z, function.context_scope())); |
| } |
| current_function_scope_ = scope_ = new (Z) LocalScope(enclosing_scope, 0, 0); |
| scope_->set_begin_token_pos(function.token_pos()); |
| scope_->set_end_token_pos(function.end_token_pos()); |
| |
| // Add function type arguments variable before current context variable. |
| if (I->reify_generic_functions() && |
| (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()) { |
| needs_expr_temp_ = true; |
| scope_->AddVariable(parsed_function_->arg_desc_var()); |
| } |
| |
| LocalVariable* context_var = parsed_function_->current_context_var(); |
| context_var->set_is_forced_stack(); |
| scope_->AddVariable(context_var); |
| |
| parsed_function_->SetNodeSequence( |
| new SequenceNode(TokenPosition::kNoSource, scope_)); |
| |
| builder_->SetOffset(function.kernel_offset()); |
| |
| FunctionNodeHelper function_node_helper(builder_); |
| const ProcedureAttributesMetadata attrs = |
| builder_->procedure_attributes_metadata_helper_.GetProcedureAttributes( |
| function.kernel_offset()); |
| |
| switch (function.kind()) { |
| case RawFunction::kClosureFunction: |
| case RawFunction::kImplicitClosureFunction: |
| case RawFunction::kRegularFunction: |
| case RawFunction::kGetterFunction: |
| case RawFunction::kSetterFunction: |
| case RawFunction::kConstructor: { |
| const Tag tag = builder_->PeekTag(); |
| builder_->ReadUntilFunctionNode(); |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| current_function_async_marker_ = function_node_helper.async_marker_; |
| // NOTE: FunctionNode is read further below the if. |
| |
| intptr_t pos = 0; |
| if (function.IsClosureFunction()) { |
| LocalVariable* closure_parameter = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::ClosureParameter(), AbstractType::dynamic_type()); |
| closure_parameter->set_is_forced_stack(); |
| 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.GetCanonicalType(klass); |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), klass_type); |
| scope_->InsertParameterAt(pos++, variable); |
| result_->this_variable = variable; |
| |
| // We visit instance field initializers because they might contain |
| // [Let] expressions and we need to have a mapping. |
| if (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()) { |
| TypedData& kernel_data = |
| TypedData::Handle(Z, class_field.KernelData()); |
| ASSERT(!kernel_data.IsNull()); |
| intptr_t field_offset = class_field.kernel_offset(); |
| AlternativeReadingScope alt(&builder_->reader_, &kernel_data, |
| field_offset); |
| FieldHelper field_helper(builder_); |
| field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
| Tag initializer_tag = |
| builder_->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 = kTypeCheckAllParameters; |
| if (function.IsNonImplicitClosureFunction()) { |
| type_check_mode = kTypeCheckAllParameters; |
| } else if (function.IsImplicitClosureFunction()) { |
| if (!attrs.has_dynamic_invocations) { |
| // This is a tear-off of an instance method that can not be reached |
| // from any dynamic invocation. The method would not check any |
| // parameters except covariant ones and those annotated with |
| // generic-covariant-impl. Which means that we have to check |
| // the rest in the tear-off itself.. |
| type_check_mode = kTypeCheckForTearOffOfNonDynamicallyInvokedMethod; |
| } |
| } else { |
| if (function.is_static()) { |
| // In static functions we don't check anything. |
| type_check_mode = kTypeCheckForStaticFunction; |
| } else if (!attrs.has_dynamic_invocations) { |
| // If the current function is never a target of a dynamic invocation |
| // and this parameter is not marked with generic-covariant-impl |
| // (which means that among all super-interfaces no type parameters |
| // ever occur at the position of this parameter) then we don't need |
| // to check this parameter on the callee side, because strong mode |
| // guarantees that it was checked at the caller side. |
| type_check_mode = kTypeCheckForNonDynamicallyInvokedMethod; |
| } |
| } |
| |
| // Continue reading FunctionNode: |
| // read positional_parameters and named_parameters. |
| AddPositionalAndNamedParameters(pos, type_check_mode, attrs); |
| |
| // We generate a synthetic body for implicit closure functions - which |
| // will forward the call to the real function. |
| // -> see BuildGraphOfImplicitClosureFunction |
| if (!function.IsImplicitClosureFunction()) { |
| builder_->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 RawFunction::kImplicitGetter: |
| case RawFunction::kImplicitStaticFinalGetter: |
| case RawFunction::kImplicitSetter: { |
| ASSERT(builder_->PeekTag() == kField); |
| if (IsFieldInitializer(function, Z)) { |
| VisitNode(); |
| break; |
| } |
| const bool is_setter = function.IsImplicitSetterFunction(); |
| const bool is_method = !function.IsStaticFunction(); |
| intptr_t pos = 0; |
| if (is_method) { |
| Class& klass = Class::Handle(Z, function.Owner()); |
| Type& klass_type = H.GetCanonicalType(klass); |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), klass_type); |
| scope_->InsertParameterAt(pos++, variable); |
| result_->this_variable = variable; |
| } |
| if (is_setter) { |
| result_->setter_value = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::Value(), |
| AbstractType::ZoneHandle(Z, function.ParameterTypeAt(pos))); |
| scope_->InsertParameterAt(pos++, result_->setter_value); |
| |
| if (is_method && !attrs.has_dynamic_invocations) { |
| FieldHelper field_helper(builder_); |
| field_helper.ReadUntilIncluding(FieldHelper::kFlags); |
| |
| if (!field_helper.IsCovariant() && |
| (!field_helper.IsGenericCovariantImpl() || |
| (!attrs.has_non_this_uses && !attrs.has_tearoff_uses))) { |
| result_->setter_value->set_type_check_mode( |
| LocalVariable::kTypeCheckedByCaller); |
| } |
| } |
| } |
| break; |
| } |
| case RawFunction::kMethodExtractor: { |
| // Add a receiver parameter. Though it is captured, we emit code to |
| // explicitly copy it to a fixed offset in a freshly-allocated context |
| // instead of using the generic code for regular functions. |
| // Therefore, it isn't necessary to mark it as captured here. |
| Class& klass = Class::Handle(Z, function.Owner()); |
| Type& klass_type = H.GetCanonicalType(klass); |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::This(), klass_type); |
| scope_->InsertParameterAt(0, variable); |
| result_->this_variable = variable; |
| break; |
| } |
| case RawFunction::kNoSuchMethodDispatcher: |
| case RawFunction::kInvokeFieldDispatcher: |
| for (intptr_t i = 0; i < function.NumParameters(); ++i) { |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| String::ZoneHandle(Z, function.ParameterNameAt(i)), |
| AbstractType::dynamic_type()); |
| scope_->InsertParameterAt(i, variable); |
| } |
| break; |
| case RawFunction::kSignatureFunction: |
| case RawFunction::kIrregexpFunction: |
| UNREACHABLE(); |
| } |
| if (needs_expr_temp_ || function.is_no_such_method_forwarder()) { |
| scope_->AddVariable(parsed_function_->EnsureExpressionTemp()); |
| } |
| parsed_function_->AllocateVariables(); |
| |
| return result_; |
| } |
| |
| void StreamingScopeBuilder::ReportUnexpectedTag(const char* variant, Tag tag) { |
| H.ReportError(builder_->script(), TokenPosition::kNoSource, |
| "Unexpected tag %d (%s) in %s, expected %s", tag, |
| Reader::TagName(tag), |
| parsed_function_->function().ToQualifiedCString(), variant); |
| } |
| |
| void StreamingScopeBuilder::VisitNode() { |
| Tag tag = builder_->PeekTag(); |
| switch (tag) { |
| case kConstructor: |
| VisitConstructor(); |
| return; |
| case kProcedure: |
| VisitProcedure(); |
| return; |
| case kField: |
| VisitField(); |
| return; |
| case kFunctionNode: |
| VisitFunctionNode(); |
| return; |
| default: |
| UNIMPLEMENTED(); |
| return; |
| } |
| } |
| |
| void StreamingScopeBuilder::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(builder_); |
| 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()) { |
| TypedData& kernel_data = TypedData::Handle(Z, class_field.KernelData()); |
| ASSERT(!kernel_data.IsNull()); |
| intptr_t field_offset = class_field.kernel_offset(); |
| AlternativeReadingScope alt(&builder_->reader_, &kernel_data, |
| field_offset); |
| FieldHelper field_helper(builder_); |
| field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
| Tag initializer_tag = builder_->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 = |
| builder_->ReadListLength(); // read initializers list length. |
| for (intptr_t i = 0; i < list_length; i++) { |
| VisitInitializer(); |
| } |
| } |
| |
| void StreamingScopeBuilder::VisitProcedure() { |
| ProcedureHelper procedure_helper(builder_); |
| procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction); |
| if (builder_->ReadTag() == kSomething) { |
| VisitFunctionNode(); |
| } |
| } |
| |
| void StreamingScopeBuilder::VisitField() { |
| FieldHelper field_helper(builder_); |
| field_helper.ReadUntilExcluding(FieldHelper::kType); |
| VisitDartType(); // read type. |
| Tag tag = builder_->ReadTag(); // read initializer (part 1). |
| if (tag == kSomething) { |
| VisitExpression(); // read initializer (part 2). |
| } |
| } |
| |
| void StreamingScopeBuilder::VisitFunctionNode() { |
| FunctionNodeHelper function_node_helper(builder_); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
| |
| intptr_t list_length = |
| builder_->ReadListLength(); // read type_parameters list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| TypeParameterHelper helper(builder_); |
| helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound); |
| VisitDartType(); // read ith bound. |
| helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kDefaultType); |
| if (builder_->ReadTag() == kSomething) { |
| VisitDartType(); // read ith default type. |
| } |
| helper.Finish(); |
| } |
| function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters); |
| |
| if (FLAG_causal_async_stacks && |
| (function_node_helper.dart_async_marker_ == FunctionNodeHelper::kAsync || |
| function_node_helper.dart_async_marker_ == |
| FunctionNodeHelper::kAsyncStar)) { |
| LocalVariable* asyncStackTraceVar = MakeVariable( |
| TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::AsyncStackTraceVar(), AbstractType::dynamic_type()); |
| scope_->AddVariable(asyncStackTraceVar); |
| } |
| |
| if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncYielding) { |
| LocalScope* scope = parsed_function_->node_sequence()->scope(); |
| intptr_t offset = parsed_function_->function().num_fixed_parameters(); |
| for (intptr_t i = 0; |
| i < parsed_function_->function().NumOptionalPositionalParameters(); |
| i++) { |
| scope->VariableAt(offset + i)->set_is_forced_stack(); |
| } |
| } |
| |
| // 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 (builder_->ReadTag() == kSomething) { |
| PositionScope scope(&builder_->reader_); |
| VisitStatement(); // Read body |
| first_body_token_position_ = builder_->reader_.min_position(); |
| } |
| |
| // Ensure that :await_jump_var, :await_ctx_var, :async_op, |
| // :async_completer and :async_stack_trace are captured. |
| if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncYielding) { |
| { |
| LocalVariable* temp = NULL; |
| LookupCapturedVariableByName( |
| (depth_.function_ == 0) ? &result_->yield_jump_variable : &temp, |
| Symbols::AwaitJumpVar()); |
| } |
| { |
| LocalVariable* temp = NULL; |
| LookupCapturedVariableByName( |
| (depth_.function_ == 0) ? &result_->yield_context_variable : &temp, |
| Symbols::AwaitContextVar()); |
| } |
| { |
| LocalVariable* temp = |
| scope_->LookupVariable(Symbols::AsyncOperation(), true); |
| if (temp != NULL) { |
| scope_->CaptureVariable(temp); |
| } |
| } |
| { |
| LocalVariable* temp = |
| scope_->LookupVariable(Symbols::AsyncCompleter(), true); |
| if (temp != NULL) { |
| scope_->CaptureVariable(temp); |
| } |
| } |
| if (FLAG_causal_async_stacks) { |
| LocalVariable* temp = |
| scope_->LookupVariable(Symbols::AsyncStackTraceVar(), true); |
| if (temp != NULL) { |
| scope_->CaptureVariable(temp); |
| } |
| } |
| } |
| } |
| |
| void StreamingScopeBuilder::VisitInitializer() { |
| Tag tag = builder_->ReadTag(); |
| builder_->ReadByte(); // read isSynthetic flag. |
| switch (tag) { |
| case kInvalidInitializer: |
| return; |
| case kFieldInitializer: |
| builder_->SkipCanonicalNameReference(); // read field_reference. |
| VisitExpression(); // read value. |
| return; |
| case kSuperInitializer: |
| builder_->SkipCanonicalNameReference(); // read target_reference. |
| VisitArguments(); // read arguments. |
| return; |
| case kRedirectingInitializer: |
| builder_->SkipCanonicalNameReference(); // read target_reference. |
| VisitArguments(); // read arguments. |
| return; |
| case kLocalInitializer: |
| VisitVariableDeclaration(); // read variable. |
| return; |
| case kAssertInitializer: |
| VisitStatement(); |
| return; |
| default: |
| ReportUnexpectedTag("initializer", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void StreamingScopeBuilder::VisitExpression() { |
| uint8_t payload = 0; |
| Tag tag = builder_->ReadTag(&payload); |
| switch (tag) { |
| case kInvalidExpression: |
| builder_->ReadPosition(); |
| builder_->SkipStringReference(); |
| return; |
| case kVariableGet: { |
| builder_->ReadPosition(); // read position. |
| intptr_t variable_kernel_offset = |
| builder_->ReadUInt(); // read kernel position. |
| builder_->ReadUInt(); // read relative variable index. |
| builder_->SkipOptionalDartType(); // read promoted type. |
| LookupVariable(variable_kernel_offset); |
| return; |
| } |
| case kSpecializedVariableGet: { |
| builder_->ReadPosition(); // read position. |
| intptr_t variable_kernel_offset = |
| builder_->ReadUInt(); // read kernel position. |
| LookupVariable(variable_kernel_offset); |
| return; |
| } |
| case kVariableSet: { |
| builder_->ReadPosition(); // read position. |
| intptr_t variable_kernel_offset = |
| builder_->ReadUInt(); // read kernel position. |
| builder_->ReadUInt(); // read relative variable index. |
| LookupVariable(variable_kernel_offset); |
| VisitExpression(); // read expression. |
| return; |
| } |
| case kSpecializedVariableSet: { |
| builder_->ReadPosition(); // read position. |
| intptr_t variable_kernel_offset = |
| builder_->ReadUInt(); // read kernel position. |
| LookupVariable(variable_kernel_offset); |
| VisitExpression(); // read expression. |
| return; |
| } |
| case kPropertyGet: |
| builder_->ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| builder_->SkipName(); // read name. |
| // read interface_target_reference. |
| builder_->SkipCanonicalNameReference(); |
| return; |
| case kPropertySet: |
| builder_->ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| builder_->SkipName(); // read name. |
| VisitExpression(); // read value. |
| // read interface_target_reference. |
| builder_->SkipCanonicalNameReference(); |
| return; |
| case kDirectPropertyGet: |
| builder_->ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| builder_->SkipCanonicalNameReference(); // read target_reference. |
| return; |
| case kDirectPropertySet: |
| builder_->ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| builder_->SkipCanonicalNameReference(); // read target_reference. |
| VisitExpression(); // read value· |
| return; |
| case kSuperPropertyGet: |
| HandleSpecialLoad(&result_->this_variable, Symbols::This()); |
| builder_->ReadPosition(); // read position. |
| builder_->SkipName(); // read name. |
| builder_->SkipCanonicalNameReference(); // read target_reference. |
| return; |
| case kSuperPropertySet: |
| HandleSpecialLoad(&result_->this_variable, Symbols::This()); |
| builder_->ReadPosition(); // read position. |
| builder_->SkipName(); // read name. |
| VisitExpression(); // read value. |
| builder_->SkipCanonicalNameReference(); // read target_reference. |
| return; |
| case kStaticGet: |
| builder_->ReadPosition(); // read position. |
| builder_->SkipCanonicalNameReference(); // read target_reference. |
| return; |
| case kStaticSet: |
| builder_->ReadPosition(); // read position. |
| builder_->SkipCanonicalNameReference(); // read target_reference. |
| VisitExpression(); // read expression. |
| return; |
| case kMethodInvocation: |
| builder_->ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| builder_->SkipName(); // read name. |
| VisitArguments(); // read arguments. |
| // read interface_target_reference. |
| builder_->SkipCanonicalNameReference(); |
| return; |
| case kDirectMethodInvocation: |
| builder_->ReadPosition(); // read position. |
| VisitExpression(); // read receiver. |
| builder_->SkipCanonicalNameReference(); // read target_reference. |
| VisitArguments(); // read arguments. |
| return; |
| case kSuperMethodInvocation: |
| HandleSpecialLoad(&result_->this_variable, Symbols::This()); |
| builder_->ReadPosition(); // read position. |
| builder_->SkipName(); // read name. |
| VisitArguments(); // read arguments. |
| // read interface_target_reference. |
| builder_->SkipCanonicalNameReference(); |
| return; |
| case kStaticInvocation: |
| case kConstStaticInvocation: |
| builder_->ReadPosition(); // read position. |
| builder_->SkipCanonicalNameReference(); // read procedure_reference. |
| VisitArguments(); // read arguments. |
| return; |
| case kConstructorInvocation: |
| case kConstConstructorInvocation: |
| builder_->ReadPosition(); // read position. |
| builder_->SkipCanonicalNameReference(); // read target_reference. |
| VisitArguments(); // read arguments. |
| return; |
| case kNot: |
| VisitExpression(); // read expression. |
| return; |
| case kLogicalExpression: |
| needs_expr_temp_ = true; |
| VisitExpression(); // read left. |
| builder_->SkipBytes(1); // read operator. |
| VisitExpression(); // read right. |
| return; |
| case kConditionalExpression: { |
| needs_expr_temp_ = true; |
| VisitExpression(); // read condition. |
| VisitExpression(); // read then. |
| VisitExpression(); // read otherwise. |
| builder_->SkipOptionalDartType(); // read unused static type. |
| return; |
| } |
| case kStringConcatenation: { |
| builder_->ReadPosition(); // read position. |
| intptr_t list_length = builder_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitExpression(); // read ith expression. |
| } |
| return; |
| } |
| case kIsExpression: |
| builder_->ReadPosition(); // read position. |
| VisitExpression(); // read operand. |
| VisitDartType(); // read type. |
| return; |
| case kAsExpression: |
| builder_->ReadPosition(); // read position. |
| builder_->ReadFlags(); // read flags. |
| VisitExpression(); // read operand. |
| VisitDartType(); // read type. |
| return; |
| case kSymbolLiteral: |
| builder_->SkipStringReference(); // read index into string table. |
| return; |
| case kTypeLiteral: |
| VisitDartType(); // read type. |
| return; |
| case kThisExpression: |
| HandleSpecialLoad(&result_->this_variable, Symbols::This()); |
| return; |
| case kRethrow: |
| builder_->ReadPosition(); // read position. |
| return; |
| case kThrow: |
| builder_->ReadPosition(); // read position. |
| VisitExpression(); // read expression. |
| return; |
| case kListLiteral: |
| case kConstListLiteral: { |
| builder_->ReadPosition(); // read position. |
| VisitDartType(); // read type. |
| intptr_t list_length = builder_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitExpression(); // read ith expression. |
| } |
| return; |
| } |
| case kMapLiteral: |
| case kConstMapLiteral: { |
| builder_->ReadPosition(); // read position. |
| VisitDartType(); // read key type. |
| VisitDartType(); // read value type. |
| intptr_t list_length = builder_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitExpression(); // read ith key. |
| VisitExpression(); // read ith value. |
| } |
| return; |
| } |
| case kFunctionExpression: { |
| intptr_t offset = |
| builder_->ReaderOffset() - 1; // -1 to include tag byte. |
| builder_->ReadPosition(); // read position. |
| HandleLocalFunction(offset); // read function node. |
| return; |
| } |
| case kLet: { |
| PositionScope scope(&builder_->reader_); |
| intptr_t offset = |
| builder_->ReaderOffset() - 1; // -1 to include tag byte. |
| |
| EnterScope(offset); |
| |
| VisitVariableDeclaration(); // read variable declaration. |
| VisitExpression(); // read expression. |
| |
| ExitScope(builder_->reader_.min_position(), |
| builder_->reader_.max_position()); |
| return; |
| } |
| case kBigIntLiteral: |
| builder_->SkipStringReference(); // read string reference. |
| return; |
| case kStringLiteral: |
| builder_->SkipStringReference(); // read string reference. |
| return; |
| case kSpecializedIntLiteral: |
| return; |
| case kNegativeIntLiteral: |
| builder_->ReadUInt(); // read value. |
| return; |
| case kPositiveIntLiteral: |
| builder_->ReadUInt(); // read value. |
| return; |
| case kDoubleLiteral: |
| builder_->ReadDouble(); // read value. |
| return; |
| case kTrueLiteral: |
| return; |
| case kFalseLiteral: |
| return; |
| case kNullLiteral: |
| return; |
| case kConstantExpression: { |
| builder_->SkipConstantReference(); |
| return; |
| } |
| case kInstantiation: { |
| VisitExpression(); |
| const intptr_t list_length = |
| builder_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitDartType(); // read ith type. |
| } |
| return; |
| } |
| case kLoadLibrary: |
| case kCheckLibraryIsLoaded: |
| builder_->ReadUInt(); // library index |
| break; |
| default: |
| ReportUnexpectedTag("expression", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void StreamingScopeBuilder::VisitStatement() { |
| Tag tag = builder_->ReadTag(); // read tag. |
| switch (tag) { |
| case kExpressionStatement: |
| VisitExpression(); // read expression. |
| return; |
| case kBlock: { |
| PositionScope scope(&builder_->reader_); |
| intptr_t offset = |
| builder_->ReaderOffset() - 1; // -1 to include tag byte. |
| |
| EnterScope(offset); |
| |
| intptr_t list_length = |
| builder_->ReadListLength(); // read number of statements. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitStatement(); // read ith statement. |
| } |
| |
| ExitScope(builder_->reader_.min_position(), |
| builder_->reader_.max_position()); |
| return; |
| } |
| case kEmptyStatement: |
| return; |
| case kAssertBlock: |
| if (I->asserts()) { |
| PositionScope scope(&builder_->reader_); |
| intptr_t offset = |
| builder_->ReaderOffset() - 1; // -1 to include tag byte. |
| |
| EnterScope(offset); |
| |
| intptr_t list_length = |
| builder_->ReadListLength(); // read number of statements. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitStatement(); // read ith statement. |
| } |
| |
| ExitScope(builder_->reader_.min_position(), |
| builder_->reader_.max_position()); |
| } else { |
| builder_->SkipStatementList(); |
| } |
| return; |
| case kAssertStatement: |
| if (I->asserts()) { |
| VisitExpression(); // Read condition. |
| builder_->ReadPosition(); // read condition start offset. |
| builder_->ReadPosition(); // read condition end offset. |
| Tag tag = builder_->ReadTag(); // read (first part of) message. |
| if (tag == kSomething) { |
| VisitExpression(); // read (rest of) message. |
| } |
| } else { |
| builder_->SkipExpression(); // Read condition. |
| builder_->ReadPosition(); // read condition start offset. |
| builder_->ReadPosition(); // read condition end offset. |
| Tag tag = builder_->ReadTag(); // read (first part of) message. |
| if (tag == kSomething) { |
| builder_->SkipExpression(); // read (rest of) message. |
| } |
| } |
| return; |
| case kLabeledStatement: |
| VisitStatement(); // read body. |
| return; |
| case kBreakStatement: |
| builder_->ReadPosition(); // read position. |
| builder_->ReadUInt(); // read target_index. |
| return; |
| case kWhileStatement: |
| ++depth_.loop_; |
| builder_->ReadPosition(); // read position. |
| VisitExpression(); // read condition. |
| VisitStatement(); // read body. |
| --depth_.loop_; |
| return; |
| case kDoStatement: |
| ++depth_.loop_; |
| builder_->ReadPosition(); // read position. |
| VisitStatement(); // read body. |
| VisitExpression(); // read condition. |
| --depth_.loop_; |
| return; |
| case kForStatement: { |
| PositionScope scope(&builder_->reader_); |
| |
| intptr_t offset = |
| builder_->ReaderOffset() - 1; // -1 to include tag byte. |
| |
| ++depth_.loop_; |
| EnterScope(offset); |
| |
| TokenPosition position = builder_->ReadPosition(); // read position. |
| intptr_t list_length = |
| builder_->ReadListLength(); // read number of variables. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitVariableDeclaration(); // read ith variable. |
| } |
| |
| Tag tag = builder_->ReadTag(); // Read first part of condition. |
| if (tag == kSomething) { |
| VisitExpression(); // read rest of condition. |
| } |
| list_length = builder_->ReadListLength(); // read number of updates. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitExpression(); // read ith update. |
| } |
| VisitStatement(); // read body. |
| |
| ExitScope(position, builder_->reader_.max_position()); |
| --depth_.loop_; |
| return; |
| } |
| case kForInStatement: |
| case kAsyncForInStatement: { |
| PositionScope scope(&builder_->reader_); |
| |
| intptr_t start_offset = |
| builder_->ReaderOffset() - 1; // -1 to include tag byte. |
| |
| builder_->ReadPosition(); // read position. |
| TokenPosition body_position = |
| builder_->ReadPosition(); // read body position. |
| |
| // Notice the ordering: We skip the variable, read the iterable, go back, |
| // re-read the variable, go forward to after having read the iterable. |
| intptr_t offset = builder_->ReaderOffset(); |
| builder_->SkipVariableDeclaration(); // read variable. |
| VisitExpression(); // read iterable. |
| |
| ++depth_.for_in_; |
| AddIteratorVariable(); |
| ++depth_.loop_; |
| EnterScope(start_offset); |
| |
| { |
| AlternativeReadingScope alt(&builder_->reader_, offset); |
| VisitVariableDeclaration(); // read variable. |
| } |
| VisitStatement(); // read body. |
| |
| if (!body_position.IsReal()) { |
| body_position = builder_->reader_.min_position(); |
| } |
| // TODO(jensj): From kernel_binary.cc |
| // forinstmt->variable_->set_end_position(forinstmt->position_); |
| ExitScope(body_position, builder_->reader_.max_position()); |
| --depth_.loop_; |
| --depth_.for_in_; |
| return; |
| } |
| case kSwitchStatement: { |
| AddSwitchVariable(); |
| builder_->ReadPosition(); // read position. |
| VisitExpression(); // read condition. |
| int case_count = builder_->ReadListLength(); // read number of cases. |
| for (intptr_t i = 0; i < case_count; ++i) { |
| int expression_count = |
| builder_->ReadListLength(); // read number of expressions. |
| for (intptr_t j = 0; j < expression_count; ++j) { |
| builder_->ReadPosition(); // read jth position. |
| VisitExpression(); // read jth expression. |
| } |
| builder_->ReadBool(); // read is_default. |
| VisitStatement(); // read body. |
| } |
| return; |
| } |
| case kContinueSwitchStatement: |
| builder_->ReadPosition(); // read position. |
| builder_->ReadUInt(); // read target_index. |
| return; |
| case kIfStatement: |
| builder_->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 == NULL)) { |
| 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; |
| } |
| |
| builder_->ReadPosition(); // read position |
| Tag tag = builder_->ReadTag(); // read (first part of) expression. |
| if (tag == kSomething) { |
| VisitExpression(); // read (rest of) expression. |
| } |
| return; |
| } |
| case kTryCatch: { |
| ++depth_.try_; |
| AddTryVariables(); |
| VisitStatement(); // read body. |
| --depth_.try_; |
| |
| ++depth_.catch_; |
| AddCatchVariables(); |
| |
| builder_->ReadByte(); // read flags |
| intptr_t catch_count = |
| builder_->ReadListLength(); // read number of catches. |
| for (intptr_t i = 0; i < catch_count; ++i) { |
| PositionScope scope(&builder_->reader_); |
| intptr_t offset = builder_->ReaderOffset(); // Catch has no tag. |
| |
| EnterScope(offset); |
| |
| builder_->ReadPosition(); // read position. |
| VisitDartType(); // Read the guard. |
| tag = builder_->ReadTag(); // read first part of exception. |
| if (tag == kSomething) { |
| VisitVariableDeclaration(); // read exception. |
| } |
| tag = builder_->ReadTag(); // read first part of stack trace. |
| if (tag == kSomething) { |
| VisitVariableDeclaration(); // read stack trace. |
| } |
| VisitStatement(); // read body. |
| |
| ExitScope(builder_->reader_.min_position(), |
| builder_->reader_.max_position()); |
| } |
| |
| FinalizeCatchVariables(); |
| |
| --depth_.catch_; |
| return; |
| } |
| case kTryFinally: { |
| ++depth_.try_; |
| ++depth_.finally_; |
| AddTryVariables(); |
| |
| VisitStatement(); // read body. |
| |
| --depth_.finally_; |
| --depth_.try_; |
| ++depth_.catch_; |
| AddCatchVariables(); |
| |
| VisitStatement(); // read finalizer. |
| |
| FinalizeCatchVariables(); |
| |
| --depth_.catch_; |
| return; |
| } |
| case kYieldStatement: { |
| builder_->ReadPosition(); // read position. |
| word flags = builder_->ReadByte(); // read flags. |
| VisitExpression(); // read expression. |
| |
| ASSERT(flags == kNativeYieldFlags); |
| if (depth_.function_ == 0) { |
| AddSwitchVariable(); |
| // Promote all currently visible local variables into the context. |
| // TODO(27590) CaptureLocalVariables promotes to many variables into |
| // the scope. Mark those variables as stack_local. |
| // TODO(27590) we don't need to promote those variables that are |
| // not used across yields. |
| scope_->CaptureLocalVariables(current_function_scope_); |
| } |
| return; |
| } |
| case kVariableDeclaration: |
| VisitVariableDeclaration(); // read variable declaration. |
| return; |
| case kFunctionDeclaration: { |
| intptr_t offset = |
| builder_->ReaderOffset() - 1; // -1 to include tag byte. |
| builder_->ReadPosition(); // read position. |
| VisitVariableDeclaration(); // read variable declaration. |
| HandleLocalFunction(offset); // read function node. |
| return; |
| } |
| default: |
| ReportUnexpectedTag("declaration", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void StreamingScopeBuilder::VisitArguments() { |
| builder_->ReadUInt(); // read argument_count. |
| |
| // Types |
| intptr_t list_length = builder_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitDartType(); // read ith type. |
| } |
| |
| // Positional. |
| list_length = builder_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| VisitExpression(); // read ith positional. |
| } |
| |
| // Named. |
| list_length = builder_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| builder_->SkipStringReference(); // read ith name index. |
| VisitExpression(); // read ith expression. |
| } |
| } |
| |
| void StreamingScopeBuilder::VisitVariableDeclaration() { |
| PositionScope scope(&builder_->reader_); |
| |
| intptr_t kernel_offset_no_tag = builder_->ReaderOffset(); |
| VariableDeclarationHelper helper(builder_); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
| AbstractType& type = BuildAndVisitVariableType(); |
| |
| // In case `declaration->IsConst()` the flow graph building will take care of |
| // evaluating the constant and setting it via |
| // `declaration->SetConstantValue()`. |
| const String& name = (H.StringSize(helper.name_index_) == 0) |
| ? GenerateName(":var", name_index_++) |
| : H.DartSymbolObfuscate(helper.name_index_); |
| |
| Tag tag = builder_->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 = builder_->reader_.max_position(); |
| if (end_position.IsReal()) { |
| end_position.Next(); |
| } |
| LocalVariable* variable = |
| MakeVariable(helper.position_, end_position, name, type); |
| if (helper.IsFinal()) { |
| variable->set_is_final(); |
| } |
| scope_->AddVariable(variable); |
| result_->locals.Insert(builder_->data_program_offset_ + kernel_offset_no_tag, |
| variable); |
| } |
| |
| AbstractType& StreamingScopeBuilder::BuildAndVisitVariableType() { |
| const intptr_t offset = builder_->ReaderOffset(); |
| AbstractType& type = T.BuildVariableType(); |
| builder_->SetOffset(offset); // rewind |
| VisitDartType(); |
| return type; |
| } |
| |
| void StreamingScopeBuilder::VisitDartType() { |
| Tag tag = builder_->ReadTag(); |
| switch (tag) { |
| case kInvalidType: |
| case kDynamicType: |
| case kVoidType: |
| case kBottomType: |
| // those contain nothing. |
| return; |
| case kInterfaceType: |
| VisitInterfaceType(false); |
| return; |
| case kSimpleInterfaceType: |
| VisitInterfaceType(true); |
| return; |
| case kFunctionType: |
| VisitFunctionType(false); |
| return; |
| case kSimpleFunctionType: |
| VisitFunctionType(true); |
| return; |
| case kTypeParameterType: |
| VisitTypeParameterType(); |
| return; |
| default: |
| ReportUnexpectedTag("type", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void StreamingScopeBuilder::VisitInterfaceType(bool simple) { |
| builder_->ReadUInt(); // read klass_name. |
| if (!simple) { |
| intptr_t length = builder_->ReadListLength(); // read number of types. |
| for (intptr_t i = 0; i < length; ++i) { |
| VisitDartType(); // read the ith type. |
| } |
| } |
| } |
| |
| void StreamingScopeBuilder::VisitFunctionType(bool simple) { |
| if (!simple) { |
| intptr_t list_length = |
| builder_->ReadListLength(); // read type_parameters list length. |
| for (int i = 0; i < list_length; ++i) { |
| TypeParameterHelper helper(builder_); |
| helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound); |
| VisitDartType(); // read bound. |
| helper.ReadUntilExcludingAndSetJustRead( |
| TypeParameterHelper::kDefaultType); |
| if (builder_->ReadTag() == kSomething) { |
| VisitDartType(); // read default type. |
| } |
| helper.Finish(); |
| } |
| builder_->ReadUInt(); // read required parameter count. |
| builder_->ReadUInt(); // read total parameter count. |
| } |
| |
| const intptr_t positional_count = |
| builder_->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 = |
| builder_->ReadListLength(); // read named_parameters list length. |
| for (intptr_t i = 0; i < named_count; ++i) { |
| // read string reference (i.e. named_parameters[i].name). |
| builder_->SkipStringReference(); |
| VisitDartType(); // read named_parameters[i].type. |
| } |
| } |
| |
| builder_->SkipListOfStrings(); // read positional parameter names. |
| |
| if (!simple) { |
| builder_->SkipCanonicalNameReference(); // read typedef reference. |
| } |
| |
| VisitDartType(); // read return type. |
| } |
| |
| void StreamingScopeBuilder::VisitTypeParameterType() { |
| Function& function = Function::Handle(Z, parsed_function_->function().raw()); |
| while (function.IsClosureFunction()) { |
| function = function.parent_function(); |
| } |
| |
| // 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 = builder_->ReadUInt(); // read index for parameter. |
| |
| if (function.IsFactory()) { |
| // The type argument vector is passed as the very first argument to the |
| // factory constructor function. |
| HandleSpecialLoad(&result_->type_arguments_variable, |
| Symbols::TypeArgumentsParameter()); |
| } else { |
| // 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()) { |
| HandleSpecialLoad(&result_->this_variable, Symbols::This()); |
| } |
| } |
| |
| builder_->SkipOptionalDartType(); // read bound bound. |
| } |
| |
| void StreamingScopeBuilder::HandleLocalFunction(intptr_t parent_kernel_offset) { |
| // "Peek" ahead into the function node |
| intptr_t offset = builder_->ReaderOffset(); |
| |
| FunctionNodeHelper function_node_helper(builder_); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
| |
| LocalScope* saved_function_scope = current_function_scope_; |
| FunctionNodeHelper::AsyncMarker saved_function_async_marker = |
| current_function_async_marker_; |
| DepthState saved_depth_state = depth_; |
| depth_ = DepthState(depth_.function_ + 1); |
| EnterScope(parent_kernel_offset); |
| current_function_scope_ = scope_; |
| current_function_async_marker_ = function_node_helper.async_marker_; |
| if (depth_.function_ == 1) { |
| FunctionScope function_scope = {offset, scope_}; |
| result_->function_scopes.Add(function_scope); |
| } |
| |
| int num_type_params = 0; |
| { |
| AlternativeReadingScope _(&builder_->reader_); |
| num_type_params = builder_->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. |
| StreamingDartTypeTranslator::TypeParameterScope scope(&type_translator_, |
| num_type_params); |
| |
| // read positional_parameters and named_parameters. |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| |
| ProcedureAttributesMetadata default_attrs; |
| AddPositionalAndNamedParameters(0, kTypeCheckAllParameters, default_attrs); |
| |
| // "Peek" is now done. |
| builder_->SetOffset(offset); |
| |
| VisitFunctionNode(); // read function node. |
| |
| ExitScope(function_node_helper.position_, function_node_helper.end_position_); |
| depth_ = saved_depth_state; |
| current_function_scope_ = saved_function_scope; |
| current_function_async_marker_ = saved_function_async_marker; |
| } |
| |
| void StreamingScopeBuilder::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 StreamingScopeBuilder::ExitScope(TokenPosition start_position, |
| TokenPosition end_position |