| // 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/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/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, |
| }; |
| |
| 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_); |
| 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: { |
| StringIndex target = builder_->ReadStringReference(); |
| name = H.DartSymbolPlain(target).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.LookupStaticFunction(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(obj)); |
| ASSERT(!obj.IsNull()); |
| } break; |
| case ConstantPoolTag::kInstance: { |
| cls = |
| H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference()); |
| obj = Instance::New(cls, Heap::kOld); |
| intptr_t elem_index = builder_->ReadUInt(); |
| ASSERT(elem_index < i); |
| elem = pool.ObjectAt(elem_index); |
| if (!elem.IsNull()) { |
| ASSERT(elem.IsTypeArguments()); |
| Instance::Cast(obj).SetTypeArguments(TypeArguments::Cast(elem)); |
| } |
| 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 kTypeArgumentsForInstanceAllocation: { |
| cls = |
| H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference()); |
| intptr_t elem_index = builder_->ReadUInt(); |
| ASSERT(elem_index < i); |
| elem = pool.ObjectAt(elem_index); |
| ASSERT(elem.IsNull() || elem.IsTypeArguments()); |
| elem = |
| Type::New(cls, TypeArguments::Cast(elem), TokenPosition::kNoSource); |
| elem = ClassFinalizer::FinalizeType(cls, Type::Cast(elem)); |
| obj = Type::Cast(elem).arguments(); |
| } 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++; |
| |
| 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.DartSymbolObfuscate(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.DartSymbolObfuscate(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; |
| 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 ObjectPool& pool = |
| ObjectPool::Handle(builder_->zone_, bytecode.object_pool()); |
| AbstractType& handled_type = AbstractType::Handle(builder_->zone_); |
| |
| // Encoding of ExceptionsTable is described in |
| // pkg/vm/lib/bytecode/exceptions.dart. |
| intptr_t try_block_count = builder_->reader_.ReadListLength(); |
| for (intptr_t i = 0; i < try_block_count; i++) { |
| intptr_t outer_try_index_plus1 = builder_->reader_.ReadUInt(); |
| intptr_t outer_try_index = outer_try_index_plus1 - 1; |
| USE(outer_try_index); |
| intptr_t start_pc = builder_->reader_.ReadUInt(); |
| USE(start_pc); |
| intptr_t end_pc = builder_->reader_.ReadUInt(); |
| USE(end_pc); |
| intptr_t handler_pc = builder_->reader_.ReadUInt(); |
| USE(handler_pc); |
| uint8_t flags = builder_->reader_.ReadByte(); |
| // flagNeedsStackTrace = 1 << 0; |
| // flagIsSynthetic = 1 << 1; |
| USE(flags); |
| |
| intptr_t type_count = builder_->reader_.ReadListLength(); |
| for (intptr_t j = 0; j < type_count; j++) { |
| intptr_t type_index = builder_->reader_.ReadUInt(); |
| ASSERT(type_index < pool.Length()); |
| handled_type ^= pool.ObjectAt(type_index); |
| } |
| } |
| // TODO(regis): Generate exception handlers (as well as pc descriptors) |
| // and store in bytecode: bytecode.set_exception_handlers(exception_handlers); |
| } |
| #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())), |
| type_translator_(builder_, /*finalize=*/true) { |
| H.InitFromScript(builder_->script()); |
| type_translator_.active_class_ = &active_class_; |
| 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(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) { |
| scope_->set_begin_token_pos(start_position); |
| scope_->set_end_token_pos(end_position); |
| scope_ = scope_->parent(); |
| } |
| |
| void StreamingScopeBuilder::AddPositionalAndNamedParameters( |
| intptr_t pos, |
| ParameterTypeCheckMode type_check_mode /* = kTypeCheckAllParameters*/, |
| const ProcedureAttributesMetadata& attrs) { |
| // List of positional. |
| intptr_t list_length = builder_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| AddVariableDeclarationParameter(pos++, type_check_mode, attrs); |
| } |
| |
| // List of named. |
| list_length = builder_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| AddVariableDeclarationParameter(pos++, type_check_mode, attrs); |
| } |
| } |
| |
| void StreamingScopeBuilder::AddVariableDeclarationParameter( |
| intptr_t pos, |
| ParameterTypeCheckMode type_check_mode, |
| const ProcedureAttributesMetadata& attrs) { |
| intptr_t kernel_offset = builder_->ReaderOffset(); // no tag. |
| const InferredTypeMetadata parameter_type = |
| builder_->inferred_type_metadata_helper_.GetInferredType(kernel_offset); |
| VariableDeclarationHelper helper(builder_); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
| String& name = H.DartSymbolObfuscate(helper.name_index_); |
| AbstractType& type = BuildAndVisitVariableType(); // read type. |
| helper.SetJustRead(VariableDeclarationHelper::kType); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
| |
| LocalVariable* variable = MakeVariable(helper.position_, helper.position_, |
| name, type, ¶meter_type); |
| if (helper.IsFinal()) { |
| variable->set_is_final(); |
| } |
| if (variable->name().raw() == Symbols::IteratorParameter().raw()) { |
| variable->set_is_forced_stack(); |
| } |
| |
| const bool needs_covariant_checke_in_method = |
| helper.IsCovariant() || |
| (helper.IsGenericCovariantImpl() && attrs.has_non_this_uses); |
| |
| switch (type_check_mode) { |
| case kTypeCheckAllParameters: |
| variable->set_type_check_mode(LocalVariable::kDoTypeCheck); |
| break; |
| case kTypeCheckForTearOffOfNonDynamicallyInvokedMethod: |
| if (needs_covariant_checke_in_method) { |
| // Don't type check covariant parameters - they will be checked by |
| // a function we forward to. Their types however are not known. |
| variable->set_type_check_mode(LocalVariable::kSkipTypeCheck); |
| } else { |
| variable->set_type_check_mode(LocalVariable::kDoTypeCheck); |
| } |
| break; |
| case kTypeCheckForNonDynamicallyInvokedMethod: |
| if (needs_covariant_checke_in_method) { |
| variable->set_type_check_mode(LocalVariable::kDoTypeCheck); |
| } else { |
| // Types of non-covariant parameters are guaranteed to match by |
| // front-end enforcing strong mode types at call site. |
| variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller); |
| } |
| break; |
| case kTypeCheckForStaticFunction: |
| variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller); |
| break; |
| } |
| scope_->InsertParameterAt(pos, variable); |
| result_->locals.Insert(builder_->data_program_offset_ + kernel_offset, |
| variable); |
| |
| // The default value may contain 'let' bindings for which the constant |
| // evaluator needs scope bindings. |
| Tag tag = builder_->ReadTag(); |
| if (tag == kSomething) { |
| VisitExpression(); // read initializer. |
| } |
| } |
| |
| LocalVariable* StreamingScopeBuilder::MakeVariable( |
| TokenPosition declaration_pos, |
| TokenPosition token_pos, |
| const String& name, |
| const AbstractType& type, |
| const InferredTypeMetadata* param_type_md /* = NULL */) { |
| CompileType* param_type = NULL; |
| if ((param_type_md != NULL) && !param_type_md->IsTrivial()) { |
| param_type = new (Z) CompileType(CompileType::CreateNullable( |
| param_type_md->nullable, param_type_md->cid)); |
| } |
| return new (Z) |
| LocalVariable(declaration_pos, token_pos, name, type, param_type); |
| } |
| |
| void StreamingScopeBuilder::AddExceptionVariable( |
| GrowableArray<LocalVariable*>* variables, |
| const char* prefix, |
| intptr_t nesting_depth) { |
| LocalVariable* v = NULL; |
| |
| // If we are inside a function with yield points then Kernel transformer |
| // could have lifted some of the auxiliary exception variables into the |
| // context to preserve them across yield points because they might |
| // be needed for rethrow. |
| // Check if it did and capture such variables instead of introducing |
| // new local ones. |
| // Note: function that wrap kSyncYielding function does not contain |
| // its own try/catches. |
| if (current_function_async_marker_ == FunctionNodeHelper::kSyncYielding) { |
| ASSERT(current_function_scope_->parent() != NULL); |
| v = current_function_scope_->parent()->LocalLookupVariable( |
| GenerateName(prefix, nesting_depth - 1)); |
| if (v != NULL) { |
| scope_->CaptureVariable(v); |
| } |
| } |
| |
| // No need to create variables for try/catch-statements inside |
| // nested functions. |
| if (depth_.function_ > 0) return; |
| if (variables->length() >= nesting_depth) return; |
| |
| // If variable was not lifted by the transformer introduce a new |
| // one into the current function scope. |
| if (v == NULL) { |
| v = MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| GenerateName(prefix, nesting_depth - 1), |
| AbstractType::dynamic_type()); |
| |
| // If transformer did not lift the variable then there is no need |
| // to lift it into the context when we encouter a YieldStatement. |
| v->set_is_forced_stack(); |
| current_function_scope_->AddVariable(v); |
| } |
| |
| variables->Add(v); |
| } |
| |
| void StreamingScopeBuilder::FinalizeExceptionVariable( |
| GrowableArray<LocalVariable*>* variables, |
| GrowableArray<LocalVariable*>* raw_variables, |
| const String& symbol, |
| intptr_t nesting_depth) { |
| // No need to create variables for try/catch-statements inside |
| // nested functions. |
| if (depth_.function_ > 0) return; |
| |
| LocalVariable* variable = (*variables)[nesting_depth - 1]; |
| LocalVariable* raw_variable; |
| if (variable->is_captured()) { |
| raw_variable = |
| new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| symbol, AbstractType::dynamic_type()); |
| const bool ok = scope_->AddVariable(raw_variable); |
| ASSERT(ok); |
| } else { |
| raw_variable = variable; |
| } |
| raw_variables->EnsureLength(nesting_depth, nullptr); |
| (*raw_variables)[nesting_depth - 1] = raw_variable; |
| } |
| |
| void StreamingScopeBuilder::AddTryVariables() { |
| AddExceptionVariable(&result_->catch_context_variables, |
| ":saved_try_context_var", depth_.try_); |
| } |
| |
| void StreamingScopeBuilder::AddCatchVariables() { |
| AddExceptionVariable(&result_->exception_variables, ":exception", |
| depth_.catch_); |
| AddExceptionVariable(&result_->stack_trace_variables, ":stack_trace", |
| depth_.catch_); |
| } |
| |
| void StreamingScopeBuilder::FinalizeCatchVariables() { |
| const intptr_t unique_id = result_->raw_variable_counter_++; |
| FinalizeExceptionVariable( |
| &result_->exception_variables, &result_->raw_exception_variables, |
| GenerateName(":raw_exception", unique_id), depth_.catch_); |
| FinalizeExceptionVariable( |
| &result_->stack_trace_variables, &result_->raw_stack_trace_variables, |
| GenerateName(":raw_stacktrace", unique_id), depth_.catch_); |
| } |
| |
| void StreamingScopeBuilder::AddIteratorVariable() { |
| if (depth_.function_ > 0) return; |
| if (result_->iterator_variables.length() >= depth_.for_in_) return; |
| |
| ASSERT(result_->iterator_variables.length() == depth_.for_in_ - 1); |
| LocalVariable* iterator = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| GenerateName(":iterator", depth_.for_in_ - 1), |
| AbstractType::dynamic_type()); |
| current_function_scope_->AddVariable(iterator); |
| result_->iterator_variables.Add(iterator); |
| } |
| |
| void StreamingScopeBuilder::AddSwitchVariable() { |
| if ((depth_.function_ == 0) && (result_->switch_variable == NULL)) { |
| LocalVariable* variable = |
| MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| Symbols::SwitchExpr(), AbstractType::dynamic_type()); |
| variable->set_is_forced_stack(); |
| current_function_scope_->AddVariable(variable); |
| result_->switch_variable = variable; |
| } |
| } |
| |
| void StreamingScopeBuilder::LookupVariable(intptr_t declaration_binary_offset) { |
| LocalVariable* variable = result_->locals.Lookup(declaration_binary_offset); |
| if (variable == NULL) { |
| // We have not seen a declaration of the variable, so it must be the |
| // case that we are compiling a nested function and the variable is |
| // declared in an outer scope. In that case, look it up in the scope by |
| // name and add it to the variable map to simplify later lookup. |
| ASSERT(current_function_scope_->parent() != NULL); |
| StringIndex var_name = builder_->GetNameFromVariableDeclaration( |
| declaration_binary_offset - builder_->data_program_offset_, |
| parsed_function_->function()); |
| |
| const String& name = H.DartSymbolObfuscate(var_name); |
| variable = current_function_scope_->parent()->LookupVariable(name, true); |
| ASSERT(variable != NULL); |
| result_->locals.Insert(declaration_binary_offset, variable); |
| } |
| |
| if (variable->owner()->function_level() < scope_->function_level()) { |
| // We call `LocalScope->CaptureVariable(variable)` in two scenarios for two |
| // different reasons: |
| // Scenario 1: |
| // We need to know which variables defined in this function |
| // are closed over by nested closures in order to ensure we will |
| // create a [Context] object of appropriate size and store captured |
| // variables there instead of the stack. |
| // Scenario 2: |
| // We need to find out which variables defined in enclosing functions |
| // are closed over by this function/closure or nested closures. This |
| // is necessary in order to build a fat flattened [ContextScope] |
| // object. |
| scope_->CaptureVariable(variable); |
| } else { |
| ASSERT(variable->owner()->function_level() == scope_->function_level()); |
| } |
| } |
| |
| const String& StreamingScopeBuilder::GenerateName(const char* prefix, |
| intptr_t suffix) { |
| char name[64]; |
| Utils::SNPrint(name, 64, "%s%" Pd "", prefix, suffix); |
| return H.DartSymbolObfuscate(name); |
| } |
| |
| void StreamingScopeBuilder::HandleSpecialLoad(LocalVariable** variable, |
| const String& symbol) { |
| if (current_function_scope_->parent() != NULL) { |
| // We are building the scope tree of a closure function and saw [node]. We |
| // lazily populate the variable using the parent function scope. |
| if (*variable == NULL) { |
| *variable = |
| current_function_scope_->parent()->LookupVariable(symbol, true); |
| ASSERT(*variable != NULL); |
| } |
| } |
| |
| if ((current_function_scope_->parent() != NULL) || |
| (scope_->function_level() > 0)) { |
| // Every scope we use the [variable] from needs to be notified of the usage |
| // in order to ensure that preserving the context scope on that particular |
| // use-site also includes the [variable]. |
| scope_->CaptureVariable(*variable); |
| } |
| } |
| |
| void StreamingScopeBuilder::LookupCapturedVariableByName( |
| LocalVariable** variable, |
| const String& name) { |
| if (*variable == NULL) { |
| *variable = scope_->LookupVariable(name, true); |
| ASSERT(*variable != NULL); |
| scope_->CaptureVariable(*variable); |
| } |
| } |
| |
| StreamingDartTypeTranslator::StreamingDartTypeTranslator( |
| StreamingFlowGraphBuilder* builder, |
| bool finalize) |
| : builder_(builder), |
| translation_helper_(builder->translation_helper_), |
| active_class_(builder->active_class()), |
| type_parameter_scope_(NULL), |
| zone_(translation_helper_.zone()), |
| result_(AbstractType::Handle(translation_helper_.zone())), |
| finalize_(finalize) {} |
| |
| AbstractType& StreamingDartTypeTranslator::BuildType() { |
| BuildTypeInternal(); |
| |
| // We return a new `ZoneHandle` here on purpose: The intermediate language |
| // instructions do not make a copy of the handle, so we do it. |
| return AbstractType::ZoneHandle(Z, result_.raw()); |
| } |
| |
| AbstractType& StreamingDartTypeTranslator::BuildTypeWithoutFinalization() { |
| bool saved_finalize = finalize_; |
| finalize_ = false; |
| BuildTypeInternal(); |
| finalize_ = saved_finalize; |
| |
| // We return a new `ZoneHandle` here on purpose: The intermediate language |
| // instructions do not make a copy of the handle, so we do it. |
| return AbstractType::ZoneHandle(Z, result_.raw()); |
| } |
| |
| AbstractType& StreamingDartTypeTranslator::BuildVariableType() { |
| AbstractType& abstract_type = BuildType(); |
| |
| // We return a new `ZoneHandle` here on purpose: The intermediate language |
| // instructions do not make a copy of the handle, so we do it. |
| AbstractType& type = Type::ZoneHandle(Z); |
| |
| if (abstract_type.IsMalformed()) { |
| type = AbstractType::dynamic_type().raw(); |
| } else { |
| type = result_.raw(); |
| } |
| |
| return type; |
| } |
| |
| void StreamingDartTypeTranslator::BuildTypeInternal(bool invalid_as_dynamic) { |
| Tag tag = builder_->ReadTag(); |
| switch (tag) { |
| case kInvalidType: |
| if (invalid_as_dynamic) { |
| result_ = Object::dynamic_type().raw(); |
| } else { |
| result_ = ClassFinalizer::NewFinalizedMalformedType( |
| Error::Handle(Z), // No previous error. |
| Script::Handle(Z, Script::null()), TokenPosition::kNoSource, |
| "[InvalidType] in Kernel IR."); |
| } |
| break; |
| case kDynamicType: |
| result_ = Object::dynamic_type().raw(); |
| break; |
| case kVoidType: |
| result_ = Object::void_type().raw(); |
| break; |
| case kBottomType: |
| result_ = |
| Class::Handle(Z, I->object_store()->null_class()).CanonicalType(); |
| break; |
| case kInterfaceType: |
| BuildInterfaceType(false); |
| break; |
| case kSimpleInterfaceType: |
| BuildInterfaceType(true); |
| break; |
| case kFunctionType: |
| BuildFunctionType(false); |
| break; |
| case kSimpleFunctionType: |
| BuildFunctionType(true); |
| break; |
| case kTypeParameterType: |
| BuildTypeParameterType(); |
| break; |
| default: |
| builder_->ReportUnexpectedTag("type", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void StreamingDartTypeTranslator::BuildInterfaceType(bool simple) { |
| // NOTE: That an interface type like `T<A, B>` is considered to be |
| // malformed iff `T` is malformed. |
| // => We therefore ignore errors in `A` or `B`. |
| |
| NameIndex klass_name = |
| builder_->ReadCanonicalNameReference(); // read klass_name. |
| |
| intptr_t length; |
| if (simple) { |
| length = 0; |
| } else { |
| length = builder_->ReadListLength(); // read type_arguments list length. |
| } |
| const TypeArguments& type_arguments = |
| BuildTypeArguments(length); // read type arguments. |
| |
| Object& klass = Object::Handle(Z, H.LookupClassByKernelClass(klass_name)); |
| result_ = Type::New(klass, type_arguments, TokenPosition::kNoSource); |
| if (finalize_) { |
| ASSERT(active_class_->klass != NULL); |
| result_ = ClassFinalizer::FinalizeType(*active_class_->klass, result_); |
| } |
| } |
| |
| void StreamingDartTypeTranslator::BuildFunctionType(bool simple) { |
| Function& signature_function = Function::ZoneHandle( |
| Z, Function::NewSignatureFunction(*active_class_->klass, |
| active_class_->enclosing != NULL |
| ? *active_class_->enclosing |
| : Function::Handle(Z), |
| TokenPosition::kNoSource)); |
| |
| // Suspend finalization of types inside this one. They will be finalized after |
| // the whole function type is constructed. |
| // |
| // TODO(31213): Test further when nested generic function types |
| // are supported by fasta. |
| bool finalize = finalize_; |
| finalize_ = false; |
| |
| if (!simple) { |
| builder_->LoadAndSetupTypeParameters(active_class_, signature_function, |
| builder_->ReadListLength(), |
| signature_function); |
| } |
| |
| ActiveTypeParametersScope scope( |
| active_class_, &signature_function, |
| TypeArguments::Handle(Z, signature_function.type_parameters()), Z); |
| |
| intptr_t required_count; |
| intptr_t all_count; |
| intptr_t positional_count; |
| if (!simple) { |
| required_count = builder_->ReadUInt(); // read required parameter count. |
| all_count = builder_->ReadUInt(); // read total parameter count. |
| positional_count = |
| builder_->ReadListLength(); // read positional_parameters list length. |
| } else { |
| positional_count = |
| builder_->ReadListLength(); // read positional_parameters list length. |
| required_count = positional_count; |
| all_count = positional_count; |
| } |
| |
| const Array& parameter_types = |
| Array::Handle(Z, Array::New(1 + all_count, Heap::kOld)); |
| signature_function.set_parameter_types(parameter_types); |
| const Array& parameter_names = |
| Array::Handle(Z, Array::New(1 + all_count, Heap::kOld)); |
| signature_function.set_parameter_names(parameter_names); |
| |
| intptr_t pos = 0; |
| parameter_types.SetAt(pos, AbstractType::dynamic_type()); |
| parameter_names.SetAt(pos, H.DartSymbolPlain("_receiver_")); |
| ++pos; |
| for (intptr_t i = 0; i < positional_count; ++i, ++pos) { |
| BuildTypeInternal(); // read ith positional parameter. |
| if (result_.IsMalformed()) { |
| result_ = AbstractType::dynamic_type().raw(); |
| } |
| parameter_types.SetAt(pos, result_); |
| parameter_names.SetAt(pos, H.DartSymbolPlain("noname")); |
| } |
| |
| // The additional first parameter is the receiver type (set to dynamic). |
| signature_function.set_num_fixed_parameters(1 + required_count); |
| signature_function.SetNumOptionalParameters( |
| all_count - required_count, positional_count > required_count); |
| |
| if (!simple) { |
| const intptr_t named_count = |
| builder_->ReadListLength(); // read named_parameters list length. |
| for (intptr_t i = 0; i < named_count; ++i, ++pos) { |
| // read string reference (i.e. named_parameters[i].name). |
| String& name = H.DartSymbolObfuscate(builder_->ReadStringReference()); |
| BuildTypeInternal(); // read named_parameters[i].type. |
| if (result_.IsMalformed()) { |
| result_ = AbstractType::dynamic_type().raw(); |
| } |
| parameter_types.SetAt(pos, result_); |
| parameter_names.SetAt(pos, name); |
| } |
| } |
| |
| builder_->SkipListOfStrings(); // read positional parameter names. |
| |
| if (!simple) { |
| builder_->SkipCanonicalNameReference(); // read typedef reference. |
| } |
| |
| BuildTypeInternal(); // read return type. |
| if (result_.IsMalformed()) { |
| result_ = AbstractType::dynamic_type().raw(); |
| } |
| signature_function.set_result_type(result_); |
| |
| finalize_ = finalize; |
| |
| Type& signature_type = |
| Type::ZoneHandle(Z, signature_function.SignatureType()); |
| |
| if (finalize_) { |
| signature_type ^= |
| ClassFinalizer::FinalizeType(*active_class_->klass, signature_type); |
| // Do not refer to signature_function anymore, since it may have been |
| // replaced during canonicalization. |
| signature_function = Function::null(); |
| } |
| |
| result_ = signature_type.raw(); |
| } |
| |
| void StreamingDartTypeTranslator::BuildTypeParameterType() { |
| intptr_t parameter_index = builder_->ReadUInt(); // read parameter index. |
| builder_->SkipOptionalDartType(); // read bound. |
| |
| const TypeArguments& class_types = |
| TypeArguments::Handle(Z, active_class_->klass->type_parameters()); |
| if (parameter_index < class_types.Length()) { |
| // The index of the type parameter in [parameters] is |
| // the same index into the `klass->type_parameters()` array. |
| result_ ^= class_types.TypeAt(parameter_index); |
| return; |
| } |
| parameter_index -= class_types.Length(); |
| |
| if (active_class_->HasMember()) { |
| if (active_class_->MemberIsFactoryProcedure()) { |
| // |
| // WARNING: This is a little hackish: |
| // |
| // We have a static factory constructor. The kernel IR gives the factory |
| // constructor function its own type parameters (which are equal in name |
| // and number to the ones of the enclosing class). I.e., |
| // |
| // class A<T> { |
| // factory A.x() { return new B<T>(); } |
| // } |
| // |
| // is basically translated to this: |
| // |
| // class A<T> { |
| // static A.x<T'>() { return new B<T'>(); } |
| // } |
| // |
| if (class_types.Length() > parameter_index) { |
| result_ ^= class_types.TypeAt(parameter_index); |
| return; |
| } |
| parameter_index -= class_types.Length(); |
| } |
| |
| intptr_t procedure_type_parameter_count = |
| active_class_->MemberIsProcedure() |
| ? active_class_->MemberTypeParameterCount(Z) |
| : 0; |
| if (procedure_type_parameter_count > 0) { |
| if (procedure_type_parameter_count > parameter_index) { |
| if (I->reify_generic_functions()) { |
| result_ ^= |
| TypeArguments::Handle(Z, active_class_->member->type_parameters()) |
| .TypeAt(parameter_index); |
| } else { |
| result_ ^= Type::DynamicType(); |
| } |
| return; |
| } |
| parameter_index -= procedure_type_parameter_count; |
| } |
| } |
| |
| if (active_class_->local_type_parameters != NULL) { |
| if (parameter_index < active_class_->local_type_parameters->Length()) { |
| if (I->reify_generic_functions()) { |
| result_ ^= |
| active_class_->local_type_parameters->TypeAt(parameter_index); |
| } else { |
| result_ ^= Type::DynamicType(); |
| } |
| return; |
| } |
| parameter_index -= active_class_->local_type_parameters->Length(); |
| } |
| |
| if (type_parameter_scope_ != NULL && |
| parameter_index < type_parameter_scope_->outer_parameter_count() + |
| type_parameter_scope_->parameter_count()) { |
| result_ ^= Type::DynamicType(); |
| return; |
| } |
| |
| H.ReportError( |
| builder_->script(), TokenPosition::kNoSource, |
| "Unbound type parameter found in %s. Please report this at dartbug.com.", |
| active_class_->ToCString()); |
| } |
| |
| const TypeArguments& StreamingDartTypeTranslator::BuildTypeArguments( |
| intptr_t length) { |
| bool only_dynamic = true; |
| intptr_t offset = builder_->ReaderOffset(); |
| for (intptr_t i = 0; i < length; ++i) { |
| if (builder_->ReadTag() != kDynamicType) { // Read the ith types tag. |
| only_dynamic = false; |
| builder_->SetOffset(offset); |
| break; |
| } |
| } |
| TypeArguments& type_arguments = TypeArguments::ZoneHandle(Z); |
| if (!only_dynamic) { |
| type_arguments = TypeArguments::New(length); |
| for (intptr_t i = 0; i < length; ++i) { |
| BuildTypeInternal(true); // read ith type. |
| type_arguments.SetTypeAt(i, result_); |
| } |
| |
| if (finalize_) { |
| type_arguments = type_arguments.Canonicalize(); |
| } |
| } |
| return type_arguments; |
| } |
| |
| const TypeArguments& |
| StreamingDartTypeTranslator::BuildInstantiatedTypeArguments( |
| const Class& receiver_class, |
| intptr_t length) { |
| const TypeArguments& type_arguments = BuildTypeArguments(length); |
| |
| // If type_arguments is null all arguments are dynamic. |
| // If, however, this class doesn't specify all the type arguments directly we |
| // still need to finalize the type below in order to get any non-dynamic types |
| // from any super. See http://www.dartbug.com/29537. |
| if (type_arguments.IsNull() && receiver_class.NumTypeArguments() == length) { |
| return type_arguments; |
| } |
| |
| // We make a temporary [Type] object and use `ClassFinalizer::FinalizeType` to |
| // finalize the argument types. |
| // (This can for example make the [type_arguments] vector larger) |
| Type& type = Type::Handle( |
| Z, Type::New(receiver_class, type_arguments, TokenPosition::kNoSource)); |
| if (finalize_) { |
| type ^= ClassFinalizer::FinalizeType(*active_class_->klass, type); |
| } |
| |
| const TypeArguments& instantiated_type_arguments = |
| TypeArguments::ZoneHandle(Z, type.arguments()); |
| return instantiated_type_arguments; |
| } |
| |
| const Type& StreamingDartTypeTranslator::ReceiverType(const Class& klass) { |
| ASSERT(!klass.IsNull()); |
| ASSERT(!klass.IsTypedefClass()); |
| // Note that if klass is _Closure, the returned type will be _Closure, |
| // and not the signature type. |
| Type& type = Type::ZoneHandle(Z, klass.CanonicalType()); |
| if (!type.IsNull()) { |
| return type; |
| } |
| type = Type::New(klass, TypeArguments::Handle(Z, klass.type_parameters()), |
| klass.token_pos()); |
| if (klass.is_type_finalized()) { |
| type ^= ClassFinalizer::FinalizeType(klass, type); |
| klass.SetCanonicalType(type); |
| } |
| return type; |
| } |
| |
| StreamingConstantEvaluator::StreamingConstantEvaluator( |
| StreamingFlowGraphBuilder* builder) |
| : builder_(builder), |
| isolate_(Isolate::Current()), |
| zone_(builder_->zone_), |
| translation_helper_(builder_->translation_helper_), |
| type_translator_(builder_->type_translator_), |
| script_(builder_->script()), |
| result_(Instance::Handle(zone_)) {} |
| |
| bool StreamingConstantEvaluator::IsCached(intptr_t offset) { |
| return GetCachedConstant(offset, &result_); |
| } |
| |
| RawInstance* StreamingConstantEvaluator::EvaluateExpression( |
| intptr_t offset, |
| bool reset_position) { |
| ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
| if (!GetCachedConstant(offset, &result_)) { |
| ASSERT(IsAllowedToEvaluate()); |
| intptr_t original_offset = builder_->ReaderOffset(); |
| builder_->SetOffset(offset); |
| uint8_t payload = 0; |
| Tag tag = builder_->ReadTag(&payload); // read tag. |
| switch (tag) { |
| case kVariableGet: |
| EvaluateVariableGet(); |
| break; |
| case kSpecializedVariableGet: |
| EvaluateVariableGet(payload); |
| break; |
| case kPropertyGet: |
| EvaluatePropertyGet(); |
| break; |
| case kDirectPropertyGet: |
| EvaluateDirectPropertyGet(); |
| break; |
| case kStaticGet: |
| EvaluateStaticGet(); |
| break; |
| case kMethodInvocation: |
| EvaluateMethodInvocation(); |
| break; |
| case kDirectMethodInvocation: |
| EvaluateDirectMethodInvocation(); |
| break; |
| case kSuperMethodInvocation: |
| EvaluateSuperMethodInvocation(); |
| break; |
| case kStaticInvocation: |
| case kConstStaticInvocation: |
| EvaluateStaticInvocation(); |
| break; |
| case kConstConstructorInvocation: |
| EvaluateConstructorInvocationInternal(); |
| break; |
| case kNot: |
| EvaluateNot(); |
| break; |
| case kLogicalExpression: |
| EvaluateLogicalExpression(); |
| break; |
| case kConditionalExpression: |
| EvaluateConditionalExpression(); |
| break; |
| case kStringConcatenation: |
| EvaluateStringConcatenation(); |
| break; |
| case kSymbolLiteral: |
| EvaluateSymbolLiteral(); |
| break; |
| case kTypeLiteral: |
| EvaluateTypeLiteral(); |
| break; |
| case kAsExpression: |
| EvaluateAsExpression(); |
| break; |
| case kConstListLiteral: |
| EvaluateListLiteralInternal(); |
| break; |
| case kConstMapLiteral: |
| EvaluateMapLiteralInternal(); |
| break; |
| case kLet: |
| EvaluateLet(); |
| break; |
| case kInstantiation: |
| EvaluatePartialTearoffInstantiation(); |
| break; |
| case kBigIntLiteral: |
| EvaluateBigIntLiteral(); |
| break; |
| case kStringLiteral: |
| EvaluateStringLiteral(); |
| break; |
| case kSpecializedIntLiteral: |
| EvaluateIntLiteral(payload); |
| break; |
| case kNegativeIntLiteral: |
| EvaluateIntLiteral(true); |
| break; |
| case kPositiveIntLiteral: |
| EvaluateIntLiteral(false); |
| break; |
| case kDoubleLiteral: |
| EvaluateDoubleLiteral(); |
| break; |
| case kTrueLiteral: |
| EvaluateBoolLiteral(true); |
| break; |
| case kFalseLiteral: |
| EvaluateBoolLiteral(false); |
| break; |
| case kNullLiteral: |
| EvaluateNullLiteral(); |
| break; |
| case kConstantExpression: |
| EvaluateConstantExpression(); |
| break; |
| default: |
| H.ReportError( |
| script_, TokenPosition::kNoSource, |
| "Not a constant expression: unexpected kernel tag %s (%" Pd ")", |
| Reader::TagName(tag), tag); |
| } |
| |
| CacheConstantValue(offset, result_); |
| if (reset_position) builder_->SetOffset(original_offset); |
| } else { |
| if (!reset_position) { |
| builder_->SetOffset(offset); |
| builder_->SkipExpression(); |
| } |
| } |
| return result_.raw(); |
| } |
| |
| Instance& StreamingConstantEvaluator::EvaluateListLiteral(intptr_t offset, |
| bool reset_position) { |
| if (!GetCachedConstant(offset, &result_)) { |
| ASSERT(IsAllowedToEvaluate()); |
| intptr_t original_offset = builder_->ReaderOffset(); |
| builder_->SetOffset(offset); |
| builder_->ReadTag(); // skip tag. |
| EvaluateListLiteralInternal(); |
| |
| CacheConstantValue(offset, result_); |
| if (reset_position) builder_->SetOffset(original_offset); |
| } |
| // We return a new `ZoneHandle` here on purpose: The intermediate language |
| // instructions do not make a copy of the handle, so we do it. |
| return Instance::ZoneHandle(Z, result_.raw()); |
| } |
| |
| Instance& StreamingConstantEvaluator::EvaluateMapLiteral(intptr_t offset, |
| bool reset_position) { |
| if (!GetCachedConstant(offset, &result_)) { |
| ASSERT(IsAllowedToEvaluate()); |
| intptr_t original_offset = builder_->ReaderOffset(); |
| builder_->SetOffset(offset); |
| builder_->ReadTag(); // skip tag. |
| EvaluateMapLiteralInternal(); |
| |
| CacheConstantValue(offset, result_); |
| if (reset_position) builder_->SetOffset(original_offset); |
| } |
| // We return a new `ZoneHandle` here on purpose: The intermediate language |
| // instructions do not make a copy of the handle, so we do it. |
| return Instance::ZoneHandle(Z, result_.raw()); |
| } |
| |
| Instance& StreamingConstantEvaluator::EvaluateConstructorInvocation( |
| intptr_t offset, |
| bool reset_position) { |
| if (!GetCachedConstant(offset, &result_)) { |
| ASSERT(IsAllowedToEvaluate()); |
| intptr_t original_offset = builder_->ReaderOffset(); |
| builder_->SetOffset(offset); |
| builder_->ReadTag(); // skip tag. |
| EvaluateConstructorInvocationInternal(); |
| |
| CacheConstantValue(offset, result_); |
| if (reset_position) builder_->SetOffset(original_offset); |
| } |
| // We return a new `ZoneHandle` here on purpose: The intermediate language |
| // instructions do not make a copy of the handle, so we do it. |
| return Instance::ZoneHandle(Z, result_.raw()); |
| } |
| |
| RawObject* StreamingConstantEvaluator::EvaluateExpressionSafe(intptr_t offset) { |
| LongJumpScope jump; |
| if (setjmp(*jump.Set()) == 0) { |
| return EvaluateExpression(offset); |
| } else { |
| Thread* thread = H.thread(); |
| Error& error = Error::Handle(Z); |
| error = thread->sticky_error(); |
| thread->clear_sticky_error(); |
| return error.raw(); |
| } |
| } |
| |
| bool StreamingConstantEvaluator::IsAllowedToEvaluate() { |
| return FLAG_precompiled_mode || builder_->flow_graph_builder_ == NULL || |
| !builder_->optimizing(); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateVariableGet() { |
| // When we see a [VariableGet] the corresponding [VariableDeclaration] must've |
| // been executed already. It therefore must have a constant object associated |
| // with it. |
| builder_->ReadPosition(); // read position. |
| intptr_t variable_kernel_position = |
| builder_->ReadUInt(); // read kernel position. |
| builder_->ReadUInt(); // read relative variable index. |
| builder_->SkipOptionalDartType(); // read promoted type. |
| LocalVariable* variable = builder_->LookupVariable(variable_kernel_position); |
| ASSERT(variable->IsConst()); |
| result_ = variable->ConstValue()->raw(); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateVariableGet(uint8_t payload) { |
| // When we see a [VariableGet] the corresponding [VariableDeclaration] must've |
| // been executed already. It therefore must have a constant object associated |
| // with it. |
| builder_->ReadPosition(); // read position. |
| intptr_t variable_kernel_position = |
| builder_->ReadUInt(); // read kernel position. |
| LocalVariable* variable = builder_->LookupVariable(variable_kernel_position); |
| ASSERT(variable->IsConst()); |
| result_ = variable->ConstValue()->raw(); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateGetStringLength( |
| intptr_t expression_offset, |
| TokenPosition position) { |
| EvaluateExpression(expression_offset); |
| if (result_.IsString()) { |
| const String& str = String::Handle(Z, String::RawCast(result_.raw())); |
| result_ = Integer::New(str.Length(), H.allocation_space()); |
| } else { |
| H.ReportError( |
| script_, position, |
| "Constant expressions can only call 'length' on string constants."); |
| } |
| } |
| |
| void StreamingConstantEvaluator::EvaluatePropertyGet() { |
| const TokenPosition position = builder_->ReadPosition(); // read position. |
| intptr_t expression_offset = builder_->ReaderOffset(); |
| builder_->SkipExpression(); // read receiver. |
| StringIndex name = builder_->ReadNameAsStringIndex(); // read name. |
| builder_->SkipCanonicalNameReference(); // read interface_target_reference. |
| |
| if (H.StringEquals(name, "length")) { |
| EvaluateGetStringLength(expression_offset, position); |
| } else { |
| H.ReportError( |
| script_, position, |
| "Constant expressions can only call 'length' on string constants."); |
| } |
| } |
| |
| void StreamingConstantEvaluator::EvaluateDirectPropertyGet() { |
| TokenPosition position = builder_->ReadPosition(); // read position. |
| intptr_t expression_offset = builder_->ReaderOffset(); |
| builder_->SkipExpression(); // read receiver. |
| NameIndex kernel_name = |
| builder_->ReadCanonicalNameReference(); // read target_reference. |
| |
| // TODO(vegorov): add check based on the complete canonical name. |
| if (H.IsGetter(kernel_name) && |
| H.StringEquals(H.CanonicalNameString(kernel_name), "length")) { |
| EvaluateGetStringLength(expression_offset, position); |
| } else { |
| H.ReportError( |
| script_, position, |
| "Constant expressions can only call 'length' on string constants."); |
| } |
| } |
| |
| void StreamingConstantEvaluator::EvaluateStaticGet() { |
| TokenPosition position = builder_->ReadPosition(); // read position. |
| NameIndex target = |
| builder_->ReadCanonicalNameReference(); // read target_reference. |
| |
| ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
| |
| if (H.IsField(target)) { |
| const Field& field = Field::Handle(Z, H.LookupFieldByKernelField(target)); |
| if (!field.is_const()) { |
| H.ReportError(script_, position, "Not a constant field."); |
| } |
| if (field.StaticValue() == Object::transition_sentinel().raw()) { |
| builder_->InlineBailout( |
| "kernel::StreamingConstantEvaluator::EvaluateStaticGet::Cyclic"); |
| H.ReportError(script_, position, "Not a constant expression."); |
| } else if (field.StaticValue() == Object::sentinel().raw()) { |
| field.SetStaticValue(Object::transition_sentinel()); |
| const Object& value = |
| Object::Handle(Compiler::EvaluateStaticInitializer(field)); |
| if (value.IsError()) { |
| field.SetStaticValue(Object::null_instance()); |
| H.ReportError(Error::Cast(value), script_, position, |
| "Not a constant expression."); |
| UNREACHABLE(); |
| } |
| Thread* thread = H.thread(); |
| const Error& error = |
| Error::Handle(thread->zone(), thread->sticky_error()); |
| if (!error.IsNull()) { |
| field.SetStaticValue(Object::null_instance()); |
| thread->clear_sticky_error(); |
| H.ReportError(error, script_, position, "Not a constant expression."); |
| UNREACHABLE(); |
| } |
| ASSERT(value.IsNull() || value.IsInstance()); |
| field.SetStaticValue(value.IsNull() ? Instance::null_instance() |
| : Instance::Cast(value)); |
| |
| result_ = field.StaticValue(); |
| result_ = H.Canonicalize(result_); |
| field.SetStaticValue(result_, true); |
| } else { |
| result_ = field.StaticValue(); |
| } |
| } else if (H.IsProcedure(target)) { |
| const Function& function = |
| Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target)); |
| |
| if (H.IsMethod(target)) { |
| Function& closure_function = |
| Function::ZoneHandle(Z, function.ImplicitClosureFunction()); |
| result_ = closure_function.ImplicitStaticClosure(); |
| result_ = H.Canonicalize(result_); |
| } else if (H.IsGetter(target)) { |
| H.ReportError(script_, position, "Not a constant expression."); |
| } else { |
| H.ReportError(script_, position, "Not a constant expression."); |
| } |
| } |
| } |
| |
| void StreamingConstantEvaluator::EvaluateMethodInvocation() { |
| builder_->ReadPosition(); // read position. |
| // This method call wasn't cached, so receiver et al. isn't cached either. |
| const Instance& receiver = Instance::Handle( |
| Z, |
| EvaluateExpression(builder_->ReaderOffset(), false)); // read receiver. |
| Class& klass = |
| Class::Handle(Z, isolate_->class_table()->At(receiver.GetClassId())); |
| ASSERT(!klass.IsNull()); |
| |
| // Search the superclass chain for the selector. |
| const String& method_name = builder_->ReadNameAsMethodName(); // read name. |
| Function& function = |
| builder_->FindMatchingFunctionAnyArgs(klass, method_name); |
| |
| // The frontend should guarantee that [MethodInvocation]s inside constant |
| // expressions are always valid. |
| ASSERT(!function.IsNull()); |
| |
| // Read arguments, run the method and canonicalize the result. |
| const Object& result = RunMethodCall(function, &receiver); |
| result_ ^= result.raw(); |
| result_ = H.Canonicalize(result_); |
| |
| builder_->SkipCanonicalNameReference(); // read interface_target_reference. |
| } |
| |
| void StreamingConstantEvaluator::EvaluateDirectMethodInvocation() { |
| builder_->ReadPosition(); // read position. |
| |
| const Instance& receiver = Instance::Handle( |
| Z, |
| EvaluateExpression(builder_->ReaderOffset(), false)); // read receiver. |
| |
| NameIndex kernel_name = |
| builder_->ReadCanonicalNameReference(); // read target_reference. |
| |
| const Function& function = Function::ZoneHandle( |
| Z, builder_->LookupMethodByMember(kernel_name, |
| H.DartProcedureName(kernel_name))); |
| |
| // Read arguments, run the method and canonicalize the result. |
| const Object& result = RunMethodCall(function, &receiver); |
| result_ ^= result.raw(); |
| result_ = H.Canonicalize(result_); |
| } |
| |
| Class& StreamingFlowGraphBuilder::GetSuperOrDie() { |
| Class& klass = Class::Handle(Z, parsed_function()->function().Owner()); |
| ASSERT(!klass.IsNull()); |
| klass = klass.SuperClass(); |
| ASSERT(!klass.IsNull()); |
| return klass; |
| } |
| |
| void StreamingConstantEvaluator::EvaluateSuperMethodInvocation() { |
| builder_->ReadPosition(); // read position. |
| |
| const LocalVariable* this_variable = builder_->scopes()->this_variable; |
| ASSERT(this_variable->IsConst()); |
| const Instance& receiver = |
| Instance::Handle(Z, this_variable->ConstValue()->raw()); |
| ASSERT(!receiver.IsNull()); |
| |
| Class& klass = builder_->GetSuperOrDie(); |
| |
| const String& method_name = builder_->ReadNameAsMethodName(); // read name. |
| Function& function = |
| builder_->FindMatchingFunctionAnyArgs(klass, method_name); |
| |
| // The frontend should guarantee that [MethodInvocation]s inside constant |
| // expressions are always valid. |
| ASSERT(!function.IsNull()); |
| |
| // Read arguments, run the method and canonicalize the result. |
| const Object& result = RunMethodCall(function, &receiver); |
| result_ ^= result.raw(); |
| result_ = H.Canonicalize(result_); |
| |
| builder_->SkipCanonicalNameReference(); // read interface_target_reference. |
| } |
| |
| void StreamingConstantEvaluator::EvaluateStaticInvocation() { |
| builder_->ReadPosition(); // read position. |
| NameIndex procedure_reference = |
| builder_->ReadCanonicalNameReference(); // read procedure reference. |
| |
| const Function& function = Function::ZoneHandle( |
| Z, H.LookupStaticMethodByKernelProcedure(procedure_reference)); |
| Class& klass = Class::Handle(Z, function.Owner()); |
| |
| intptr_t argument_count = |
| builder_->ReadUInt(); // read arguments part #1: arguments count. |
| |
| // Build the type arguments vector (if necessary). |
| const TypeArguments* type_arguments = |
| TranslateTypeArguments(function, &klass); // read argument types. |
| |
| // read positional and named parameters. |
| const Object& result = |
| RunFunction(function, argument_count, NULL, type_arguments); |
| result_ ^= result.raw(); |
| result_ = H.Canonicalize(result_); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateConstructorInvocationInternal() { |
| builder_->ReadPosition(); // read position. |
| |
| NameIndex target = builder_->ReadCanonicalNameReference(); // read target. |
| const Function& constructor = |
| Function::Handle(Z, H.LookupConstructorByKernelConstructor(target)); |
| Class& klass = Class::Handle(Z, constructor.Owner()); |
| |
| intptr_t argument_count = |
| builder_->ReadUInt(); // read arguments part #1: arguments count. |
| |
| // Build the type arguments vector (if necessary). |
| const TypeArguments* type_arguments = |
| TranslateTypeArguments(constructor, &klass); // read argument types. |
| |
| if (klass.NumTypeArguments() > 0 && !klass.IsGeneric()) { |
| Type& type = Type::ZoneHandle(Z, T.ReceiverType(klass).raw()); |
| // TODO(27590): Can we move this code into [ReceiverType]? |
| type ^= ClassFinalizer::FinalizeType(*builder_->active_class()->klass, type, |
| ClassFinalizer::kFinalize); |
| ASSERT(!type.IsMalformedOrMalbounded()); |
| |
| TypeArguments& canonicalized_type_arguments = |
| TypeArguments::ZoneHandle(Z, type.arguments()); |
| canonicalized_type_arguments = canonicalized_type_arguments.Canonicalize(); |
| type_arguments = &canonicalized_type_arguments; |
| } |
| |
| // Prepare either the instance or the type argument vector for the constructor |
| // call. |
| Instance* receiver = NULL; |
| const TypeArguments* type_arguments_argument = NULL; |
| if (!constructor.IsFactory()) { |
| receiver = &Instance::Handle(Z, Instance::New(klass, Heap::kOld)); |
| if (type_arguments != NULL) { |
| receiver->SetTypeArguments(*type_arguments); |
| } |
| } else { |
| type_arguments_argument = type_arguments; |
| } |
| |
| // read positional and named parameters. |
| const Object& result = RunFunction(constructor, argument_count, receiver, |
| type_arguments_argument); |
| |
| if (constructor.IsFactory()) { |
| // Factories return the new object. |
| result_ ^= result.raw(); |
| } else { |
| ASSERT(!receiver->IsNull()); |
| result_ ^= (*receiver).raw(); |
| } |
| if (I->obfuscate() && |
| (result_.clazz() == I->object_store()->symbol_class())) { |
| Obfuscator::ObfuscateSymbolInstance(H.thread(), result_); |
| } |
| result_ = H.Canonicalize(result_); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateNot() { |
| result_ ^= Bool::Get(!EvaluateBooleanExpressionHere()).raw(); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateLogicalExpression() { |
| bool left = EvaluateBooleanExpressionHere(); // read left. |
| LogicalOperator op = static_cast<LogicalOperator>(builder_->ReadByte()); |
| if (op == kAnd) { |
| if (left) { |
| EvaluateBooleanExpressionHere(); // read right. |
| } else { |
| builder_->SkipExpression(); // read right. |
| } |
| } else { |
| ASSERT(op == kOr); |
| if (!left) { |
| EvaluateBooleanExpressionHere(); // read right. |
| } else { |
| builder_->SkipExpression(); // read right. |
| } |
| } |
| } |
| |
| void StreamingConstantEvaluator::EvaluateAsExpression() { |
| builder_->ReadPosition(); |
| const uint8_t flags = builder_->ReadFlags(); |
| const bool is_type_error = (flags & (1 << 0)) != 0; |
| |
| // Check that this AsExpression was inserted by the front-end. |
| if (!is_type_error) { |
| H.ReportError( |
| script_, TokenPosition::kNoSource, |
| "explicit as operator is not permitted in constant expression"); |
| } |
| |
| EvaluateExpression(builder_->ReaderOffset(), false); |
| |
| const AbstractType& type = T.BuildType(); |
| if (!type.IsInstantiated() || type.IsMalformed()) { |
| const String& type_str = String::Handle(type.UserVisibleName()); |
| H.ReportError( |
| script_, TokenPosition::kNoSource, |
| "Not a constant expression: right hand side of an implicit " |
| "as-expression is expected to be an instantiated type, got %s", |
| type_str.ToCString()); |
| } |
| |
| const TypeArguments& instantiator_type_arguments = TypeArguments::Handle(); |
| const TypeArguments& function_type_arguments = TypeArguments::Handle(); |
| Error& error = Error::Handle(); |
| if (!result_.IsInstanceOf(type, instantiator_type_arguments, |
| function_type_arguments, &error)) { |
| const AbstractType& rtype = |
| AbstractType::Handle(result_.GetType(Heap::kNew)); |
| const String& result_str = String::Handle(rtype.UserVisibleName()); |
| const String& type_str = String::Handle(type.UserVisibleName()); |
| H.ReportError(script_, TokenPosition::kNoSource, |
| "Not a constant expression: %s is not an instance of %s", |
| result_str.ToCString(), type_str.ToCString()); |
| } |
| } |
| |
| void StreamingConstantEvaluator::EvaluateConditionalExpression() { |
| bool condition = EvaluateBooleanExpressionHere(); |
| if (condition) { |
| EvaluateExpression(builder_->ReaderOffset(), false); // read then. |
| builder_->SkipExpression(); // read otherwise. |
| } else { |
| builder_->SkipExpression(); // read then. |
| EvaluateExpression(builder_->ReaderOffset(), false); // read otherwise. |
| } |
| builder_->SkipOptionalDartType(); // read unused static type. |
| } |
| |
| void StreamingConstantEvaluator::EvaluateStringConcatenation() { |
| builder_->ReadPosition(); // read position. |
| intptr_t length = builder_->ReadListLength(); // read list length. |
| |
| bool all_string = true; |
| const Array& strings = |
| Array::Handle(Z, Array::New(length, H.allocation_space())); |
| for (intptr_t i = 0; i < length; ++i) { |
| EvaluateExpression(builder_->ReaderOffset(), |
| false); // read ith expression. |
| strings.SetAt(i, result_); |
| all_string = all_string && result_.IsString(); |
| } |
| if (all_string) { |
| result_ = String::ConcatAll(strings, Heap::kOld); |
| result_ = H.Canonicalize(result_); |
| } else { |
| // Get string interpolation function. |
| const Class& cls = |
| Class::Handle(Z, Library::LookupCoreClass(Symbols::StringBase())); |
| ASSERT(!cls.IsNull()); |
| const Function& func = Function::Handle( |
| Z, cls.LookupStaticFunction( |
| Library::PrivateCoreLibName(Symbols::Interpolate()))); |
| ASSERT(!func.IsNull()); |
| |
| // Build argument array to pass to the interpolation function. |
| const Array& interpolate_arg = Array::Handle(Z, Array::New(1, Heap::kOld)); |
| interpolate_arg.SetAt(0, strings); |
| |
| // Run and canonicalize. |
| const Object& result = |
| RunFunction(func, interpolate_arg, Array::null_array()); |
| result_ = H.Canonicalize(String::Cast(result)); |
| } |
| } |
| |
| void StreamingConstantEvaluator::EvaluateSymbolLiteral() { |
| // The symbol value is read plain and obfuscated later. |
| const String& symbol_value = H.DartSymbolPlain( |
| builder_->ReadStringReference()); // read index into string table. |
| |
| const Class& symbol_class = |
| Class::ZoneHandle(Z, I->object_store()->symbol_class()); |
| ASSERT(!symbol_class.IsNull()); |
| const Function& symbol_constructor = Function::ZoneHandle( |
| Z, symbol_class.LookupConstructor(Symbols::SymbolCtor())); |
| ASSERT(!symbol_constructor.IsNull()); |
| result_ ^= EvaluateConstConstructorCall( |
| symbol_class, TypeArguments::Handle(Z), symbol_constructor, symbol_value); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateTypeLiteral() { |
| const AbstractType& type = T.BuildType(); |
| if (type.IsMalformed()) { |
| H.ReportError(script_, TokenPosition::kNoSource, |
| "Malformed type literal in constant expression."); |
| } |
| result_ = type.raw(); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateListLiteralInternal() { |
| builder_->ReadPosition(); // read position. |
| const TypeArguments& type_arguments = T.BuildTypeArguments(1); // read type. |
| intptr_t length = builder_->ReadListLength(); // read list length. |
| const Array& const_list = |
| Array::ZoneHandle(Z, Array::New(length, Heap::kOld)); |
| const_list.SetTypeArguments(type_arguments); |
| Instance& expression = Instance::Handle(Z); |
| for (intptr_t i = 0; i < length; ++i) { |
| expression = EvaluateExpression(builder_->ReaderOffset(), |
| false); // read ith expression. |
| const_list.SetAt(i, expression); |
| } |
| const_list.MakeImmutable(); |
| result_ = H.Canonicalize(const_list); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateMapLiteralInternal() { |
| builder_->ReadPosition(); // read position. |
| const TypeArguments& type_arguments = |
| T.BuildTypeArguments(2); // read key type and value type. |
| |
| intptr_t length = builder_->ReadListLength(); // read length of entries. |
| |
| // This MapLiteral wasn't cached, so content isn't cached either. |
| Array& const_kv_array = Array::Handle(Z, Array::New(2 * length, Heap::kOld)); |
| Instance& temp = Instance::Handle(Z); |
| for (intptr_t i = 0; i < length; ++i) { |
| temp = EvaluateExpression(builder_->ReaderOffset(), false); // read key. |
| const_kv_array.SetAt(2 * i + 0, temp); |
| temp = EvaluateExpression(builder_->ReaderOffset(), false); // read value. |
| const_kv_array.SetAt(2 * i + 1, temp); |
| } |
| |
| const_kv_array.MakeImmutable(); |
| const_kv_array ^= H.Canonicalize(const_kv_array); |
| |
| const Class& map_class = |
| Class::Handle(Z, Library::LookupCoreClass(Symbols::ImmutableMap())); |
| ASSERT(!map_class.IsNull()); |
| ASSERT(map_class.NumTypeArguments() == 2); |
| |
| const Field& field = |
| Field::Handle(Z, map_class.LookupInstanceFieldAllowPrivate( |
| H.DartSymbolObfuscate("_kvPairs"))); |
| ASSERT(!field.IsNull()); |
| |
| // NOTE: This needs to be kept in sync with `runtime/lib/immutable_map.dart`! |
| result_ = Instance::New(map_class, Heap::kOld); |
| ASSERT(!result_.IsNull()); |
| result_.SetTypeArguments(type_arguments); |
| result_.SetField(field, const_kv_array); |
| result_ = H.Canonicalize(result_); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateLet() { |
| intptr_t kernel_position = |
| builder_->ReaderOffset() + builder_->data_program_offset_; |
| LocalVariable* local = builder_->LookupVariable(kernel_position); |
| |
| // read variable declaration. |
| VariableDeclarationHelper helper(builder_); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
| Tag tag = builder_->ReadTag(); // read (first part of) initializer. |
| if (tag == kNothing) { |
| local->SetConstValue(Instance::ZoneHandle(Z, Instance::null())); |
| } else { |
| local->SetConstValue(Instance::ZoneHandle( |
| Z, EvaluateExpression(builder_->ReaderOffset(), |
| false))); // read rest of initializer. |
| } |
| |
| EvaluateExpression(builder_->ReaderOffset(), false); // read body |
| } |
| |
| void StreamingConstantEvaluator::EvaluatePartialTearoffInstantiation() { |
| // This method call wasn't cached, so receiver et al. isn't cached either. |
| const Instance& receiver = Instance::Handle( |
| Z, |
| EvaluateExpression(builder_->ReaderOffset(), false)); // read receiver. |
| if (!receiver.IsClosure()) { |
| H.ReportError(script_, TokenPosition::kNoSource, "Expected closure."); |
| } |
| const Closure& old_closure = Closure::Cast(receiver); |
| |
| // read type arguments. |
| intptr_t num_type_args = builder_->ReadListLength(); |
| const TypeArguments* type_args = &T.BuildTypeArguments(num_type_args); |
| |
| // Create new closure with the type arguments inserted, and other things |
| // copied over. |
| Closure& new_closure = Closure::Handle( |
| Z, |
| Closure::New( |
| TypeArguments::Handle(Z, old_closure.instantiator_type_arguments()), |
| TypeArguments::Handle(old_closure.function_type_arguments()), |
| *type_args, Function::Handle(Z, old_closure.function()), |
| Context::Handle(Z, old_closure.context()), Heap::kOld)); |
| result_ = H.Canonicalize(new_closure); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateBigIntLiteral() { |
| const String& value = |
| H.DartString(builder_->ReadStringReference()); // read string reference. |
| result_ = Integer::New(value, Heap::kOld); |
| if (result_.IsNull()) { |
| H.ReportError(script_, TokenPosition::kNoSource, |
| "Integer literal %s is out of range", value.ToCString()); |
| } |
| result_ = H.Canonicalize(result_); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateStringLiteral() { |
| result_ = H.DartSymbolPlain(builder_->ReadStringReference()) |
| .raw(); // read string reference. |
| } |
| |
| void StreamingConstantEvaluator::EvaluateIntLiteral(uint8_t payload) { |
| int64_t value = static_cast<int32_t>(payload) - SpecializedIntLiteralBias; |
| result_ = Integer::New(value, Heap::kOld); |
| result_ = H.Canonicalize(result_); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateIntLiteral(bool is_negative) { |
| int64_t value = is_negative ? -static_cast<int64_t>(builder_->ReadUInt()) |
| : builder_->ReadUInt(); // read value. |
| result_ = Integer::New(value, Heap::kOld); |
| result_ = H.Canonicalize(result_); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateDoubleLiteral() { |
| result_ = Double::New(builder_->ReadDouble(), Heap::kOld); // read value. |
| result_ = H.Canonicalize(result_); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateBoolLiteral(bool value) { |
| result_ = Bool::Get(value).raw(); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateNullLiteral() { |
| result_ = Instance::null(); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateConstantExpression() { |
| KernelConstantsMap constant_map(H.constants().raw()); |
| result_ ^= constant_map.GetOrDie(builder_->ReadUInt()); |
| ASSERT(constant_map.Release().raw() == H.constants().raw()); |
| } |
| |
| // This depends on being about to read the list of positionals on arguments. |
| const Object& StreamingConstantEvaluator::RunFunction( |
| const Function& function, |
| intptr_t argument_count, |
| const Instance* receiver, |
| const TypeArguments* type_args) { |
| // We use a kernel2kernel constant evaluator in Dart 2.0 AOT compilation, so |
| // we should never end up evaluating constants using the VM's constant |
| // evaluator. |
| if (I->strong() && FLAG_precompiled_mode) { |
| UNREACHABLE(); |
| } |
| |
| // We do not support generic methods yet. |
| ASSERT((receiver == NULL) || (type_args == NULL)); |
| intptr_t extra_arguments = |
| (receiver != NULL ? 1 : 0) + (type_args != NULL ? 1 : 0); |
| |
| // Build up arguments. |
| const Array& arguments = Array::Handle( |
| Z, Array::New(extra_arguments + argument_count, H.allocation_space())); |
| intptr_t pos = 0; |
| if (receiver != NULL) { |
| arguments.SetAt(pos++, *receiver); |
| } |
| if (type_args != NULL) { |
| arguments.SetAt(pos++, *type_args); |
| } |
| |
| // List of positional. |
| intptr_t list_length = builder_->ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| EvaluateExpression(builder_->ReaderOffset(), |
| false); // read ith expression. |
| arguments.SetAt(pos++, result_); |
| } |
| |
| // List of named. |
| list_length = builder_->ReadListLength(); // read list length. |
| const Array& names = |
| Array::Handle(Z, Array::New(list_length, H.allocation_space())); |
| for (intptr_t i = 0; i < list_length; ++i) { |
| String& name = H.DartSymbolObfuscate( |
| builder_->ReadStringReference()); // read ith name index. |
| names.SetAt(i, name); |
| EvaluateExpression(builder_->ReaderOffset(), |
| false); // read ith expression. |
| arguments.SetAt(pos++, result_); |
| } |
| |
| return RunFunction(function, arguments, names); |
| } |
| |
| const Object& StreamingConstantEvaluator::RunFunction(const Function& function, |
| const Array& arguments, |
| const Array& names) { |
| // We do not support generic methods yet. |
| const int kTypeArgsLen = 0; |
| const Array& args_descriptor = Array::Handle( |
| Z, ArgumentsDescriptor::New(kTypeArgsLen, arguments.Length(), names)); |
| const Object& result = Object::Handle( |
| Z, DartEntry::InvokeFunction(function, arguments, args_descriptor)); |
| if (result.IsError()) { |
| H.ReportError(Error::Cast(result), "error evaluating constant constructor"); |
| } |
| return result; |
| } |
| |
| const Object& StreamingConstantEvaluator::RunMethodCall( |
| const Function& function, |
| const Instance* receiver) { |
| intptr_t argument_count = builder_->ReadUInt(); // read arguments count. |
| |
| // TODO(28109) Support generic methods in the VM or reify them away. |
| ASSERT(builder_->PeekListLength() == 0); |
| builder_->SkipListOfDartTypes(); // read list of types. |
| |
| // Run the method. |
| return RunFunction(function, argument_count, receiver, NULL); |
| } |
| |
| RawObject* StreamingConstantEvaluator::EvaluateConstConstructorCall( |
| const Class& type_class, |
| const TypeArguments& type_arguments, |
| const Function& constructor, |
| const Object& argument) { |
| // Factories have one extra argument: the type arguments. |
| // Constructors have 1 extra arguments: receiver. |
| const int kTypeArgsLen = 0; |
| const int kNumArgs = 1; |
| const int kNumExtraArgs = 1; |
| const int argument_count = kNumArgs + kNumExtraArgs; |
| const Array& arg_values = |
| Array::Handle(Z, Array::New(argument_count, Heap::kOld)); |
| Instance& instance = Instance::Handle(Z); |
| if (!constructor.IsFactory()) { |
| instance = Instance::New(type_class, Heap::kOld); |
| if (!type_arguments.IsNull()) { |
| ASSERT(type_arguments.IsInstantiated()); |
| instance.SetTypeArguments( |
| TypeArguments::Handle(Z, type_arguments.Canonicalize())); |
| } |
| arg_values.SetAt(0, instance); |
| } else { |
| // Prepend type_arguments to list of arguments to factory. |
| ASSERT(type_arguments.IsZoneHandle()); |
| arg_values.SetAt(0, type_arguments); |
| } |
| arg_values.SetAt((0 + kNumExtraArgs), argument); |
| const Array& args_descriptor = |
| Array::Handle(Z, ArgumentsDescriptor::New(kTypeArgsLen, argument_count, |
| Object::empty_array())); |
| const Object& result = Object::Handle( |
| Z, DartEntry::InvokeFunction(constructor, arg_values, args_descriptor)); |
| ASSERT(!result.IsError()); |
| if (constructor.IsFactory()) { |
| // The factory method returns the allocated object. |
| instance ^= result.raw(); |
| } |
| if (I->obfuscate() && |
| (instance.clazz() == I->object_store()->symbol_class())) { |
| Obfuscator::ObfuscateSymbolInstance(H.thread(), instance); |
| } |
| return H.Canonicalize(instance); |
| } |
| |
| const TypeArguments* StreamingConstantEvaluator::TranslateTypeArguments( |
| const Function& target, |
| Class* target_klass) { |
| intptr_t type_count = builder_->ReadListLength(); // read type count. |
| |
| const TypeArguments* type_arguments = NULL; |
| if (type_count > 0) { |
| type_arguments = &T.BuildInstantiatedTypeArguments( |
| *target_klass, type_count); // read types. |
| |
| if (!(type_arguments->IsNull() || type_arguments->IsInstantiated())) { |
| H.ReportError(script_, TokenPosition::kNoSource, |
| "Type must be constant in const constructor."); |
| } |
| } else if (target.IsFactory() && type_arguments == NULL) { |
| // All factories take a type arguments vector as first argument (independent |
| // of whether the class is generic or not). |
| type_arguments = &TypeArguments::ZoneHandle(Z, TypeArguments::null()); |
| } |
| return type_arguments; |
| } |
| |
| bool StreamingConstantEvaluator::EvaluateBooleanExpressionHere() { |
| EvaluateExpression(builder_->ReaderOffset(), false); |
| AssertBool(); |
| return result_.raw() == Bool::True().raw(); |
| } |
| |
| bool StreamingConstantEvaluator::GetCachedConstant(intptr_t kernel_offset, |
| Instance* value) { |
| if (builder_ == NULL || builder_->flow_graph_builder_ == NULL) return false; |
| |
| const Function& function = builder_->parsed_function()->function(); |
| if (function.kind() == RawFunction::kImplicitStaticFinalGetter) { |
| // Don't cache constants in initializer expressions. They get |
| // evaluated only once. |
| return false; |
| } |
| |
| bool is_present = false; |
| ASSERT(!script_.InVMHeap()); |
| if (script_.compile_time_constants() == Array::null()) { |
| return false; |
| } |
| KernelConstantsMap constants(script_.compile_time_constants()); |
| *value ^= constants.GetOrNull(kernel_offset + builder_->data_program_offset_, |
| &is_present); |
| // Mutator compiler thread may add constants while background compiler |
| // is running, and thus change the value of 'compile_time_constants'; |
| // do not assert that 'compile_time_constants' has not changed. |
| constants.Release(); |
| if (FLAG_compiler_stats && is_present) { |
| ++H.thread()->compiler_stats()->num_const_cache_hits; |
| } |
| return is_present; |
| } |
| |
| void StreamingConstantEvaluator::CacheConstantValue(intptr_t kernel_offset, |
| const Instance& value) { |
| ASSERT(Thread::Current()->IsMutatorThread()); |
| |
| if (builder_ == NULL || builder_->flow_graph_builder_ == NULL) return; |
| |
| const Function& function = builder_->parsed_function()->function(); |
| if (function.kind() == RawFunction::kImplicitStaticFinalGetter) { |
| // Don't cache constants in initializer expressions. They get |
| // evaluated only once. |
| return; |
| } |
| const intptr_t kInitialConstMapSize = 16; |
| ASSERT(!script_.InVMHeap()); |
| if (script_.compile_time_constants() == Array::null()) { |
| const Array& array = Array::Handle( |
| HashTables::New<KernelConstantsMap>(kInitialConstMapSize, Heap::kNew)); |
| script_.set_compile_time_constants(array); |
| } |
| KernelConstantsMap constants(script_.compile_time_constants()); |
| constants.InsertNewOrGetValue(kernel_offset + builder_->data_program_offset_, |
| value); |
| script_.set_compile_time_constants(constants.Release()); |
| } |
| |
| void StreamingFlowGraphBuilder::DiscoverEnclosingElements( |
| Zone* zone, |
| const Function& function, |
| Function* outermost_function) { |
| // Find out if there is an enclosing kernel class (which will be used to |
| // resolve type parameters). |
| *outermost_function = function.raw(); |
| while (outermost_function->parent_function() != Object::null()) { |
| *outermost_function = outermost_function->parent_function(); |
| } |
| } |
| |
| void KernelFingerprintHelper::BuildHash(uint32_t val) { |
| hash_ = CalculateHash(hash_, val); |
| } |
| |
| void KernelFingerprintHelper::CalculateConstructorFingerprint() { |
| ConstructorHelper helper(this); |
| |
| helper.ReadUntilExcluding(ConstructorHelper::kAnnotations); |
| CalculateListOfExpressionsFingerprint(); |
| CalculateFunctionNodeFingerprint(); |
| intptr_t len = ReadListLength(); |
| for (intptr_t i = 0; i < len; ++i) { |
| CalculateInitializerFingerprint(); |
| } |
| helper.SetJustRead(ConstructorHelper::kInitializers); |
| BuildHash(helper.flags_); |
| BuildHash(helper.annotation_count_); |
| } |
| |
| void KernelFingerprintHelper::CalculateArgumentsFingerprint() { |
| BuildHash(ReadUInt()); // read argument count. |
| |
| CalculateListOfDartTypesFingerprint(); // read list of types. |
| CalculateListOfExpressionsFingerprint(); // read positionals. |
| |
| // List of named. |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| CalculateStringReferenceFingerprint(); // read ith name index. |
| CalculateExpressionFingerprint(); // read ith expression. |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateVariableDeclarationFingerprint() { |
| VariableDeclarationHelper helper(this); |
| |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kAnnotations); |
| CalculateListOfExpressionsFingerprint(); |
| helper.SetJustRead(VariableDeclarationHelper::kAnnotations); |
| |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
| // We don't need to use the helper after this point. |
| CalculateDartTypeFingerprint(); |
| if (ReadTag() == kSomething) { |
| CalculateExpressionFingerprint(); |
| } |
| |
| BuildHash(helper.flags_); |
| } |
| |
| void KernelFingerprintHelper::CalculateStatementListFingerprint() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| CalculateStatementFingerprint(); // read ith expression. |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateListOfExpressionsFingerprint() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| CalculateExpressionFingerprint(); // read ith expression. |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateListOfDartTypesFingerprint() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| CalculateDartTypeFingerprint(); // read ith type. |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateStringReferenceFingerprint() { |
| BuildHash( |
| H.DartString(ReadStringReference()).Hash()); // read ith string index. |
| } |
| |
| void KernelFingerprintHelper::CalculateListOfStringsFingerprint() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| CalculateStringReferenceFingerprint(); // read ith string index. |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateListOfVariableDeclarationsFingerprint() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| // read ith variable declaration. |
| CalculateVariableDeclarationFingerprint(); |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateTypeParameterFingerprint() { |
| TypeParameterHelper helper(this); |
| |
| helper.ReadUntilExcluding(TypeParameterHelper::kAnnotations); |
| CalculateListOfExpressionsFingerprint(); |
| helper.SetJustRead(TypeParameterHelper::kAnnotations); |
| |
| helper.ReadUntilExcluding(TypeParameterHelper::kBound); |
| // The helper isn't needed after this point. |
| CalculateDartTypeFingerprint(); |
| if (ReadTag() == kSomething) { |
| CalculateDartTypeFingerprint(); |
| } |
| BuildHash(helper.flags_); |
| } |
| |
| void KernelFingerprintHelper::CalculateTypeParametersListFingerprint() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| CalculateTypeParameterFingerprint(); |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateCanonicalNameFingerprint() { |
| const StringIndex i = H.CanonicalNameString(ReadCanonicalNameReference()); |
| BuildHash(H.DartString(i).Hash()); |
| } |
| |
| void KernelFingerprintHelper::CalculateInitializerFingerprint() { |
| Tag tag = ReadTag(); |
| ReadByte(); // read isSynthetic flag. |
| switch (tag) { |
| case kInvalidInitializer: |
| return; |
| case kFieldInitializer: |
| BuildHash(H.DartFieldName(ReadCanonicalNameReference()).Hash()); |
| CalculateExpressionFingerprint(); // read value. |
| return; |
| case kSuperInitializer: |
| CalculateCanonicalNameFingerprint(); // read target_reference |
| CalculateArgumentsFingerprint(); // read arguments. |
| return; |
| case kRedirectingInitializer: |
| CalculateCanonicalNameFingerprint(); // read target_reference |
| CalculateArgumentsFingerprint(); // read arguments. |
| return; |
| case kLocalInitializer: |
| CalculateVariableDeclarationFingerprint(); // read variable. |
| return; |
| case kAssertInitializer: |
| CalculateStatementFingerprint(); |
| return; |
| default: |
| ReportUnexpectedTag("initializer", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateDartTypeFingerprint() { |
| Tag tag = ReadTag(); |
| BuildHash(tag); |
| switch (tag) { |
| case kInvalidType: |
| case kDynamicType: |
| case kVoidType: |
| case kBottomType: |
| // those contain nothing. |
| break; |
| UNIMPLEMENTED(); |
| case kInterfaceType: |
| CalculateInterfaceTypeFingerprint(false); |
| break; |
| case kSimpleInterfaceType: |
| CalculateInterfaceTypeFingerprint(true); |
| break; |
| case kFunctionType: |
| CalculateFunctionTypeFingerprint(false); |
| break; |
| case kSimpleFunctionType: |
| CalculateFunctionTypeFingerprint(true); |
| break; |
| case kTypeParameterType: |
| ReadUInt(); // read index for parameter. |
| CalculateOptionalDartTypeFingerprint(); // read bound bound. |
| break; |
| default: |
| ReportUnexpectedTag("type", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateOptionalDartTypeFingerprint() { |
| Tag tag = ReadTag(); // read tag. |
| BuildHash(tag); |
| if (tag == kNothing) { |
| return; |
| } |
| ASSERT(tag == kSomething); |
| CalculateDartTypeFingerprint(); // read type. |
| } |
| |
| void KernelFingerprintHelper::CalculateInterfaceTypeFingerprint(bool simple) { |
| BuildHash(ReadUInt()); // read klass_name. |
| if (!simple) { |
| CalculateListOfDartTypesFingerprint(); // read list of types. |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateFunctionTypeFingerprint(bool simple) { |
| if (!simple) { |
| CalculateTypeParametersListFingerprint(); // read type_parameters. |
| BuildHash(ReadUInt()); // read required parameter count. |
| BuildHash(ReadUInt()); // read total parameter count. |
| } |
| |
| CalculateListOfDartTypesFingerprint(); // read positional_parameters types. |
| |
| if (!simple) { |
| const intptr_t named_count = |
| ReadListLength(); // read named_parameters list length. |
| BuildHash(named_count); |
| for (intptr_t i = 0; i < named_count; ++i) { |
| // read string reference (i.e. named_parameters[i].name). |
| CalculateStringReferenceFingerprint(); |
| CalculateDartTypeFingerprint(); // read named_parameters[i].type. |
| } |
| } |
| |
| CalculateListOfStringsFingerprint(); // read positional parameter names. |
| |
| if (!simple) { |
| // TODO(bkonyi): include in hash. |
| SkipCanonicalNameReference(); // read typedef reference. |
| } |
| |
| CalculateDartTypeFingerprint(); // read return type. |
| } |
| |
| void KernelFingerprintHelper::CalculateGetterNameFingerprint() { |
| const NameIndex name = ReadCanonicalNameReference(); |
| if (I->strong() && !H.IsRoot(name) && (H.IsGetter(name) || H.IsField(name))) { |
| BuildHash(H.DartGetterName(name).Hash()); |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateSetterNameFingerprint() { |
| const NameIndex name = ReadCanonicalNameReference(); |
| if (I->strong() && !H.IsRoot(name)) { |
| BuildHash(H.DartSetterName(name).Hash()); |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateMethodNameFingerprint() { |
| const NameIndex name = |
| ReadCanonicalNameReference(); // read interface_target_reference. |
| if (I->strong() && !H.IsRoot(name) && !H.IsField(name)) { |
| BuildHash(H.DartProcedureName(name).Hash()); |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateExpressionFingerprint() { |
| uint8_t payload = 0; |
| Tag tag = ReadTag(&payload); |
| BuildHash(tag); |
| switch (tag) { |
| case kInvalidExpression: |
| ReadPosition(); |
| CalculateStringReferenceFingerprint(); |
| return; |
| case kVariableGet: |
| ReadPosition(); // read position. |
| ReadUInt(); // read kernel position. |
| ReadUInt(); // read relative variable index. |
| CalculateOptionalDartTypeFingerprint(); // read promoted type. |
| return; |
| case kSpecializedVariableGet: |
| ReadPosition(); // read position. |
| ReadUInt(); // read kernel position. |
| return; |
| case kVariableSet: |
| ReadPosition(); // read position. |
| ReadUInt(); // read kernel position. |
| ReadUInt(); // read relative variable index. |
| CalculateExpressionFingerprint(); // read expression. |
| return; |
| case kSpecializedVariableSet: |
| ReadPosition(); // read position. |
| ReadUInt(); // read kernel position. |
| CalculateExpressionFingerprint(); // read expression. |
| return; |
| case kPropertyGet: |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read receiver. |
| BuildHash(ReadNameAsGetterName().Hash()); // read name. |
| CalculateGetterNameFingerprint(); // read interface_target_reference. |
| return; |
| case kPropertySet: |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read receiver. |
| BuildHash(ReadNameAsSetterName().Hash()); // read name. |
| CalculateExpressionFingerprint(); // read value. |
| CalculateSetterNameFingerprint(); // read interface_target_reference. |
| return; |
| case kSuperPropertyGet: |
| ReadPosition(); // read position. |
| BuildHash(ReadNameAsGetterName().Hash()); // read name. |
| CalculateGetterNameFingerprint(); // read interface_target_reference. |
| return; |
| case kSuperPropertySet: |
| ReadPosition(); // read position. |
| BuildHash(ReadNameAsSetterName().Hash()); // read name. |
| CalculateExpressionFingerprint(); // read value. |
| CalculateSetterNameFingerprint(); // read interface_target_reference. |
| return; |
| case kDirectPropertyGet: |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read receiver. |
| CalculateCanonicalNameFingerprint(); // read target_reference. |
| return; |
| case kDirectPropertySet: |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read receiver. |
| CalculateCanonicalNameFingerprint(); // read target_reference. |
| CalculateExpressionFingerprint(); // read value· |
| return; |
| case kStaticGet: |
| ReadPosition(); // read position. |
| CalculateCanonicalNameFingerprint(); // read target_reference. |
| return; |
| case kStaticSet: |
| ReadPosition(); // read position. |
| CalculateCanonicalNameFingerprint(); // read target_reference. |
| CalculateExpressionFingerprint(); // read expression. |
| return; |
| case kMethodInvocation: |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read receiver. |
| BuildHash(ReadNameAsMethodName().Hash()); // read name. |
| CalculateArgumentsFingerprint(); // read arguments. |
| CalculateMethodNameFingerprint(); // read interface_target_reference. |
| return; |
| case kSuperMethodInvocation: |
| ReadPosition(); // read position. |
| BuildHash(ReadNameAsMethodName().Hash()); // read name. |
| CalculateArgumentsFingerprint(); // read arguments. |
| CalculateCanonicalNameFingerprint(); // read target_reference. |
| return; |
| case kDirectMethodInvocation: |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read receiver. |
| CalculateCanonicalNameFingerprint(); // read target_reference. |
| CalculateArgumentsFingerprint(); // read arguments. |
| return; |
| case kStaticInvocation: |
| case kConstStaticInvocation: |
| ReadPosition(); // read position. |
| CalculateCanonicalNameFingerprint(); // read target_reference. |
| CalculateArgumentsFingerprint(); // read arguments. |
| return; |
| case kConstructorInvocation: |
| case kConstConstructorInvocation: |
| ReadPosition(); // read position. |
| CalculateCanonicalNameFingerprint(); // read target_reference. |
| CalculateArgumentsFingerprint(); // read arguments. |
| return; |
| case kNot: |
| CalculateExpressionFingerprint(); // read expression. |
| return; |
| case kLogicalExpression: |
| CalculateExpressionFingerprint(); // read left. |
| SkipBytes(1); // read operator. |
| CalculateExpressionFingerprint(); // read right. |
| return; |
| case kConditionalExpression: |
| CalculateExpressionFingerprint(); // read condition. |
| CalculateExpressionFingerprint(); // read then. |
| CalculateExpressionFingerprint(); // read otherwise. |
| CalculateOptionalDartTypeFingerprint(); // read unused static type. |
| return; |
| case kStringConcatenation: |
| ReadPosition(); // read position. |
| CalculateListOfExpressionsFingerprint(); // read list of expressions. |
| return; |
| case kIsExpression: |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read operand. |
| CalculateDartTypeFingerprint(); // read type. |
| return; |
| case kAsExpression: |
| ReadPosition(); // read position. |
| BuildHash(ReadFlags()); // read flags. |
| CalculateExpressionFingerprint(); // read operand. |
| CalculateDartTypeFingerprint(); // read type. |
| return; |
| case kSymbolLiteral: |
| CalculateStringReferenceFingerprint(); // read index into string table. |
| return; |
| case kTypeLiteral: |
| CalculateDartTypeFingerprint(); // read type. |
| return; |
| case kThisExpression: |
| return; |
| case kRethrow: |
| ReadPosition(); // read position. |
| return; |
| case kThrow: |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read expression. |
| return; |
| case kListLiteral: |
| case kConstListLiteral: |
| ReadPosition(); // read position. |
| CalculateDartTypeFingerprint(); // read type. |
| CalculateListOfExpressionsFingerprint(); // read list of expressions. |
| return; |
| case kMapLiteral: |
| case kConstMapLiteral: { |
| ReadPosition(); // read position. |
| CalculateDartTypeFingerprint(); // read type. |
| CalculateDartTypeFingerprint(); // read value type. |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| CalculateExpressionFingerprint(); // read ith key. |
| CalculateExpressionFingerprint(); // read ith value. |
| } |
| return; |
| } |
| case kFunctionExpression: |
| ReadPosition(); // read position. |
| CalculateFunctionNodeFingerprint(); // read function node. |
| return; |
| case kLet: |
| CalculateVariableDeclarationFingerprint(); // read variable declaration. |
| CalculateExpressionFingerprint(); // read expression. |
| return; |
| case kInstantiation: |
| CalculateExpressionFingerprint(); // read expression. |
| CalculateListOfDartTypesFingerprint(); // read type arguments. |
| return; |
| case kBigIntLiteral: |
| CalculateStringReferenceFingerprint(); // read string reference. |
| return; |
| case kStringLiteral: |
| CalculateStringReferenceFingerprint(); // read string reference. |
| return; |
| case kSpecializedIntLiteral: |
| return; |
| case kNegativeIntLiteral: |
| BuildHash(ReadUInt()); // read value. |
| return; |
| case kPositiveIntLiteral: |
| BuildHash(ReadUInt()); // read value. |
| return; |
| case kDoubleLiteral: { |
| double value = ReadDouble(); // read value. |
| uint64_t data = bit_cast<uint64_t>(value); |
| BuildHash(static_cast<uint32_t>(data >> 32)); |
| BuildHash(static_cast<uint32_t>(data)); |
| return; |
| } |
| case kTrueLiteral: |
| return; |
| case kFalseLiteral: |
| return; |
| case kNullLiteral: |
| return; |
| case kConstantExpression: |
| SkipConstantReference(); |
| return; |
| case kLoadLibrary: |
| case kCheckLibraryIsLoaded: |
| ReadUInt(); // skip library index |
| return; |
| default: |
| ReportUnexpectedTag("expression", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void KernelFingerprintHelper::CalculateStatementFingerprint() { |
| Tag tag = ReadTag(); // read tag. |
| BuildHash(tag); |
| switch (tag) { |
| case kExpressionStatement: |
| CalculateExpressionFingerprint(); // read expression. |
| return; |
| case kBlock: |
| CalculateStatementListFingerprint(); |
| return; |
| case kEmptyStatement: |
| return; |
| case kAssertBlock: |
| CalculateStatementListFingerprint(); |
| return; |
| case kAssertStatement: |
| CalculateExpressionFingerprint(); // Read condition. |
| ReadPosition(); // read condition start offset. |
| ReadPosition(); // read condition end offset. |
| if (ReadTag() == kSomething) { |
| CalculateExpressionFingerprint(); // read (rest of) message. |
| } |
| return; |
| case kLabeledStatement: |
| CalculateStatementFingerprint(); // read body. |
| return; |
| case kBreakStatement: |
| ReadPosition(); // read position. |
| ReadUInt(); // read target_index. |
| return; |
| case kWhileStatement: |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read condition. |
| CalculateStatementFingerprint(); // read body. |
| return; |
| case kDoStatement: |
| ReadPosition(); // read position. |
| CalculateStatementFingerprint(); // read body. |
| CalculateExpressionFingerprint(); // read condition. |
| return; |
| case kForStatement: { |
| ReadPosition(); // read position. |
| CalculateListOfVariableDeclarationsFingerprint(); // read variables. |
| Tag tag = ReadTag(); // Read first part of condition. |
| if (tag == kSomething) { |
| CalculateExpressionFingerprint(); // read rest of condition. |
| } |
| CalculateListOfExpressionsFingerprint(); // read updates. |
| CalculateStatementFingerprint(); // read body. |
| return; |
| } |
| case kForInStatement: |
| case kAsyncForInStatement: |
| ReadPosition(); // read position. |
| ReadPosition(); // read body position. |
| CalculateVariableDeclarationFingerprint(); // read variable. |
| CalculateExpressionFingerprint(); // read iterable. |
| CalculateStatementFingerprint(); // read body. |
| return; |
| case kSwitchStatement: { |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read condition. |
| int case_count = ReadListLength(); // read number of cases. |
| for (intptr_t i = 0; i < case_count; ++i) { |
| int expression_count = ReadListLength(); // read number of expressions. |
| for (intptr_t j = 0; j < expression_count; ++j) { |
| ReadPosition(); // read jth position. |
| CalculateExpressionFingerprint(); // read jth expression. |
| } |
| BuildHash(ReadBool()); // read is_default. |
| CalculateStatementFingerprint(); // read body. |
| } |
| return; |
| } |
| case kContinueSwitchStatement: |
| ReadPosition(); // read position. |
| ReadUInt(); // read target_index. |
| return; |
| case kIfStatement: |
| ReadPosition(); // read position. |
| CalculateExpressionFingerprint(); // read condition. |
| CalculateStatementFingerprint(); // read then. |
| CalculateStatementFingerprint(); // read otherwise. |
| return; |
| case kReturnStatement: { |
| ReadPosition(); // read position |
| Tag tag = ReadTag(); // read (first part of) expression. |
| BuildHash(tag); |
| if (tag == kSomething) { |
| CalculateExpressionFingerprint(); // read (rest of) expression. |
| } |
| return; |
| } |
| case kTryCatch: { |
| CalculateStatementFingerprint(); // read body. |
| BuildHash(ReadByte()); // read flags |
| intptr_t catch_count = ReadListLength(); // read number of catches. |
| for (intptr_t i = 0; i < catch_count; ++i) { |
| ReadPosition(); // read position. |
| CalculateDartTypeFingerprint(); // read guard. |
| tag = ReadTag(); // read first part of exception. |
| BuildHash(tag); |
| if (tag == kSomething) { |
| CalculateVariableDeclarationFingerprint(); // read exception. |
| } |
| tag = ReadTag(); // read first part of stack trace. |
| BuildHash(tag); |
| if (tag == kSomething) { |
| CalculateVariableDeclarationFingerprint(); // read stack trace. |
| } |
| CalculateStatementFingerprint(); // read body. |
| } |
| return; |
| } |
| case kTryFinally: |
| CalculateStatementFingerprint(); // read body. |
| CalculateStatementFingerprint(); // read finalizer. |
| return; |
| case kYieldStatement: { |
| ReadPosition(); // read position. |
| BuildHash(ReadByte()); // read flags. |
| CalculateExpressionFingerprint(); // read expression. |
| return; |
| } |
| case kVariableDeclaration: |
| CalculateVariableDeclarationFingerprint(); // read variable declaration. |
| return; |
| case kFunctionDeclaration: |
| ReadPosition(); // read position. |
| CalculateVariableDeclarationFingerprint(); // read variable. |
| CalculateFunctionNodeFingerprint(); // read function node. |
| return; |
| default: |
| ReportUnexpectedTag("statement", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| uint32_t KernelFingerprintHelper::CalculateFieldFingerprint() { |
| hash_ = 0; |
| FieldHelper field_helper(this); |
| |
| field_helper.ReadUntilExcluding(FieldHelper::kName); |
| const String& name = ReadNameAsFieldName(); // read name. |
| field_helper.SetJustRead(FieldHelper::kName); |
| |
| field_helper.ReadUntilExcluding(FieldHelper::kType); |
| CalculateDartTypeFingerprint(); // read type. |
| field_helper.SetJustRead(FieldHelper::kType); |
| |
| if (ReadTag() == kSomething) { |
| if (PeekTag() == kFunctionExpression) { |
| AlternativeReadingScope alt(&reader_); |
| CalculateExpressionFingerprint(); |
| } |
| SkipExpression(); |
| } |
| |
| BuildHash(name.Hash()); |
| BuildHash(field_helper.flags_); |
| BuildHash(field_helper.annotation_count_); |
| return hash_; |
| } |
| |
| void KernelFingerprintHelper::CalculateFunctionNodeFingerprint() { |
| FunctionNodeHelper function_node_helper(this); |
| |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
| CalculateTypeParametersListFingerprint(); |
| function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters); |
| |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| CalculateListOfVariableDeclarationsFingerprint(); // read positionals |
| CalculateListOfVariableDeclarationsFingerprint(); // read named |
| CalculateDartTypeFingerprint(); // read return type. |
| |
| if (ReadTag() == kSomething) { |
| CalculateStatementFingerprint(); // Read body. |
| } |
| BuildHash(function_node_helper.total_parameter_count_); |
| BuildHash(function_node_helper.required_parameter_count_); |
| } |
| |
| uint32_t KernelFingerprintHelper::CalculateFunctionFingerprint() { |
| hash_ = 0; |
| Tag tag = PeekTag(); |
| if (tag == kField) { |
| return CalculateFieldFingerprint(); |
| } else if (tag == kConstructor) { |
| CalculateConstructorFingerprint(); |
| return hash_; |
| } |
| ProcedureHelper procedure_helper(this); |
| procedure_helper.ReadUntilExcluding(ProcedureHelper::kName); |
| const String& name = ReadNameAsMethodName(); // Read name. |
| procedure_helper.SetJustRead(ProcedureHelper::kName); |
| |
| procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction); |
| if (ReadTag() == kSomething) { |
| CalculateFunctionNodeFingerprint(); |
| } |
| |
| BuildHash(procedure_helper.kind_); |
| BuildHash(procedure_helper.flags_); |
| BuildHash(procedure_helper.annotation_count_); |
| BuildHash(name.Hash()); |
| return hash_; |
| } |
| |
| void StreamingFlowGraphBuilder::ReadUntilFunctionNode( |
| ParsedFunction* parsed_function) { |
| const Tag tag = PeekTag(); |
| if (tag == kProcedure) { |
| ProcedureHelper procedure_helper(this); |
| procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction); |
| if (ReadTag() == kNothing) { // read function node tag. |
| // Running a procedure without a function node doesn't make sense. |
| UNREACHABLE(); |
| } |
| if (parsed_function != NULL && flow_graph_builder_ != nullptr && |
| procedure_helper.IsForwardingStub() && !procedure_helper.IsAbstract()) { |
| ASSERT(procedure_helper.forwarding_stub_super_target_ != -1); |
| parsed_function->MarkForwardingStub( |
| procedure_helper.forwarding_stub_super_target_); |
| } |
| // Now at start of FunctionNode. |
| } else if (tag == kConstructor) { |
| ConstructorHelper constructor_helper(this); |
| constructor_helper.ReadUntilExcluding(ConstructorHelper::kFunction); |
| // Now at start of FunctionNode. |
| // Notice that we also have a list of initializers after that! |
| } else if (tag == kFunctionNode) { |
| // Already at start of FunctionNode. |
| } else { |
| ReportUnexpectedTag("a procedure, a constructor or a function node", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| StringIndex StreamingFlowGraphBuilder::GetNameFromVariableDeclaration( |
| intptr_t kernel_offset, |
| const Function& function) { |
| TypedData& kernel_data = TypedData::Handle(Z, function.KernelData()); |
| ASSERT(!kernel_data.IsNull()); |
| |
| // Temporarily go to the variable declaration, read the name. |
| AlternativeReadingScope alt(&reader_, &kernel_data, kernel_offset); |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilIncluding(VariableDeclarationHelper::kNameIndex); |
| return helper.name_index_; |
| } |
| |
| bool StreamingFlowGraphBuilder::optimizing() { |
| return flow_graph_builder_->optimizing_; |
| } |
| |
| FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldInitializer() { |
| FieldHelper field_helper(this); |
| field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
| |
| Tag initializer_tag = ReadTag(); // read first part of initializer. |
| if (initializer_tag != kSomething) { |
| UNREACHABLE(); |
| } |
| |
| TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry(); |
| flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr( |
| *parsed_function(), normal_entry, Compiler::kNoOSRDeoptId); |
| |
| Fragment body(normal_entry); |
| body += |
| flow_graph_builder_->CheckStackOverflowInPrologue(field_helper.position_); |
| if (field_helper.IsConst()) { |
| // this will (potentially) read the initializer, but reset the position. |
| body += Constant(Instance::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpression(ReaderOffset()))); |
| SkipExpression(); // read the initializer. |
| } else { |
| body += BuildExpression(); // read initializer. |
| } |
| body += Return(TokenPosition::kNoSource); |
| |
| PrologueInfo prologue_info(-1, -1); |
| return new (Z) |
| FlowGraph(*parsed_function(), flow_graph_builder_->graph_entry_, |
| flow_graph_builder_->last_used_block_id_, prologue_info); |
| } |
| |
| FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldAccessor( |
| LocalVariable* setter_value) { |
| FieldHelper field_helper(this); |
| field_helper.ReadUntilIncluding(FieldHelper::kCanonicalName); |
| |
| const Function& function = parsed_function()->function(); |
| |
| bool is_setter = function.IsImplicitSetterFunction(); |
| bool is_method = !function.IsStaticFunction(); |
| Field& field = Field::ZoneHandle( |
| Z, H.LookupFieldByKernelField(field_helper.canonical_name_)); |
| |
| TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry(); |
| flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr( |
| *parsed_function(), normal_entry, Compiler::kNoOSRDeoptId); |
| |
| Fragment body(normal_entry); |
| if (is_setter) { |
| if (is_method) { |
| body += LoadLocal(scopes()->this_variable); |
| } |
| body += LoadLocal(setter_value); |
| if (I->argument_type_checks() && setter_value->needs_type_check()) { |
| body += CheckArgumentType(setter_value, setter_value->type()); |
| } |
| if (is_method) { |
| body += flow_graph_builder_->StoreInstanceFieldGuarded(field, false); |
| } else { |
| body += StoreStaticField(TokenPosition::kNoSource, field); |
| } |
| body += NullConstant(); |
| } else if (is_method) { |
| body += LoadLocal(scopes()->this_variable); |
| body += flow_graph_builder_->LoadField(field); |
| } else if (field.is_const()) { |
| field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
| Tag initializer_tag = ReadTag(); // read first part of initializer. |
| |
| // If the parser needs to know the value of an uninitialized constant field |
| // it will set the value to the transition sentinel (used to detect circular |
| // initialization) and then call the implicit getter. Thus, the getter |
| // cannot contain the InitStaticField instruction that normal static getters |
| // contain because it would detect spurious circular initialization when it |
| // checks for the transition sentinel. |
| ASSERT(initializer_tag == kSomething); |
| body += Constant(Instance::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpression(ReaderOffset()))); |
| } else { |
| // The field always has an initializer because static fields without |
| // initializers are initialized eagerly and do not have implicit getters. |
| ASSERT(field.has_initializer()); |
| body += Constant(field); |
| body += flow_graph_builder_->InitStaticField(field); |
| body += Constant(field); |
| body += LoadStaticField(); |
| } |
| body += Return(TokenPosition::kNoSource); |
| |
| PrologueInfo prologue_info(-1, -1); |
| return new (Z) |
| FlowGraph(*parsed_function(), flow_graph_builder_->graph_entry_, |
| flow_graph_builder_->last_used_block_id_, prologue_info); |
| } |
| |
| void StreamingFlowGraphBuilder::SetupDefaultParameterValues() { |
| intptr_t optional_parameter_count = |
| parsed_function()->function().NumOptionalParameters(); |
| if (optional_parameter_count > 0) { |
| ZoneGrowableArray<const Instance*>* default_values = |
| new ZoneGrowableArray<const Instance*>(Z, optional_parameter_count); |
| |
| AlternativeReadingScope alt(&reader_); |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| |
| if (parsed_function()->function().HasOptionalNamedParameters()) { |
| // List of positional. |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| SkipVariableDeclaration(); // read ith variable declaration. |
| } |
| |
| // List of named. |
| list_length = ReadListLength(); // read list length. |
| ASSERT(optional_parameter_count == list_length); |
| ASSERT(!parsed_function()->function().HasOptionalPositionalParameters()); |
| for (intptr_t i = 0; i < list_length; ++i) { |
| Instance* default_value; |
| |
| // Read ith variable declaration |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
| Tag tag = ReadTag(); // read (first part of) initializer. |
| if (tag == kSomething) { |
| // this will (potentially) read the initializer, |
| // but reset the position. |
| default_value = &Instance::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpression(ReaderOffset())); |
| SkipExpression(); // read (actual) initializer. |
| } else { |
| default_value = &Instance::ZoneHandle(Z, Instance::null()); |
| } |
| default_values->Add(default_value); |
| } |
| } else { |
| // List of positional. |
| intptr_t list_length = ReadListLength(); // read list length. |
| ASSERT(list_length == function_node_helper.required_parameter_count_ + |
| optional_parameter_count); |
| ASSERT(parsed_function()->function().HasOptionalPositionalParameters()); |
| for (intptr_t i = 0; i < function_node_helper.required_parameter_count_; |
| ++i) { |
| SkipVariableDeclaration(); // read ith variable declaration. |
| } |
| for (intptr_t i = 0; i < optional_parameter_count; ++i) { |
| Instance* default_value; |
| |
| // Read ith variable declaration |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
| Tag tag = ReadTag(); // read (first part of) initializer. |
| if (tag == kSomething) { |
| // this will (potentially) read the initializer, |
| // but reset the position. |
| default_value = &Instance::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpression(ReaderOffset())); |
| SkipExpression(); // read (actual) initializer. |
| } else { |
| default_value = &Instance::ZoneHandle(Z, Instance::null()); |
| } |
| default_values->Add(default_value); |
| } |
| |
| // List of named. |
| list_length = ReadListLength(); // read list length. |
| ASSERT(list_length == 0); |
| } |
| parsed_function()->set_default_parameter_values(default_values); |
| } |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildFieldInitializer( |
| NameIndex canonical_name) { |
| ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
| Field& field = |
| Field::ZoneHandle(Z, H.LookupFieldByKernelField(canonical_name)); |
| if (PeekTag() == kNullLiteral) { |
| SkipExpression(); // read past the null literal. |
| field.RecordStore(Object::null_object()); |
| return Fragment(); |
| } |
| |
| Fragment instructions; |
| instructions += LoadLocal(scopes()->this_variable); |
| instructions += BuildExpression(); |
| instructions += flow_graph_builder_->StoreInstanceFieldGuarded(field, true); |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildInitializers( |
| const Class& parent_class) { |
| ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
| Fragment instructions; |
| |
| // Start by getting the position of the constructors initializer. |
| intptr_t initializers_offset = -1; |
| { |
| AlternativeReadingScope alt(&reader_); |
| SkipFunctionNode(); // read constructors function node. |
| initializers_offset = ReaderOffset(); |
| } |
| |
| // These come from: |
| // class A { |
| // var x = (expr); |
| // } |
| // We don't want to do that when this is a Redirecting Constructors though |
| // (i.e. has a single initializer being of type kRedirectingInitializer). |
| bool is_redirecting_constructor = false; |
| { |
| AlternativeReadingScope alt(&reader_, initializers_offset); |
| intptr_t list_length = ReadListLength(); // read initializers list length. |
| bool no_field_initializers = true; |
| for (intptr_t i = 0; i < list_length; ++i) { |
| if (PeekTag() == kRedirectingInitializer) { |
| is_redirecting_constructor = true; |
| } else if (PeekTag() == kFieldInitializer) { |
| no_field_initializers = false; |
| } |
| SkipInitializer(); |
| } |
| ASSERT(is_redirecting_constructor ? no_field_initializers : true); |
| } |
| |
| if (!is_redirecting_constructor) { |
| 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(&reader_, &kernel_data, field_offset); |
| FieldHelper field_helper(this); |
| field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
| Tag initializer_tag = ReadTag(); // read first part of initializer. |
| if (initializer_tag == kSomething) { |
| EnterScope(field_offset); |
| instructions += BuildFieldInitializer( |
| field_helper.canonical_name_); // read initializer. |
| ExitScope(field_offset); |
| } |
| } |
| } |
| } |
| |
| // These to come from: |
| // class A { |
| // var x; |
| // var y; |
| // A(this.x) : super(expr), y = (expr); |
| // } |
| { |
| AlternativeReadingScope alt(&reader_, initializers_offset); |
| intptr_t list_length = ReadListLength(); // read initializers list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| Tag tag = ReadTag(); |
| ReadByte(); // read isSynthetic flag. |
| switch (tag) { |
| case kInvalidInitializer: |
| UNIMPLEMENTED(); |
| return Fragment(); |
| case kFieldInitializer: { |
| NameIndex canonical_name = |
| ReadCanonicalNameReference(); // read field_reference. |
| instructions += BuildFieldInitializer(canonical_name); // read value. |
| break; |
| } |
| case kAssertInitializer: { |
| instructions += BuildStatement(); |
| break; |
| } |
| case kSuperInitializer: { |
| NameIndex canonical_target = |
| ReadCanonicalNameReference(); // read target_reference. |
| |
| instructions += LoadLocal(scopes()->this_variable); |
| instructions += PushArgument(); |
| |
| // TODO(jensj): ASSERT(init->arguments()->types().length() == 0); |
| Array& argument_names = Array::ZoneHandle(Z); |
| intptr_t argument_count; |
| instructions += BuildArguments( |
| &argument_names, &argument_count, |
| /* positional_parameter_count = */ NULL); // read arguments. |
| argument_count += 1; |
| |
| Class& parent_klass = GetSuperOrDie(); |
| |
| const Function& target = Function::ZoneHandle( |
| Z, H.LookupConstructorByKernelConstructor( |
| parent_klass, H.CanonicalNameString(canonical_target))); |
| instructions += |
| StaticCall(TokenPosition::kNoSource, target, argument_count, |
| argument_names, ICData::kStatic); |
| instructions += Drop(); |
| break; |
| } |
| case kRedirectingInitializer: { |
| NameIndex canonical_target = |
| ReadCanonicalNameReference(); // read target_reference. |
| |
| instructions += LoadLocal(scopes()->this_variable); |
| instructions += PushArgument(); |
| |
| // TODO(jensj): ASSERT(init->arguments()->types().length() == 0); |
| Array& argument_names = Array::ZoneHandle(Z); |
| intptr_t argument_count; |
| instructions += BuildArguments( |
| &argument_names, &argument_count, |
| /* positional_parameter_count = */ NULL); // read arguments. |
| argument_count += 1; |
| |
| const Function& target = Function::ZoneHandle( |
| Z, H.LookupConstructorByKernelConstructor(canonical_target)); |
| instructions += |
| StaticCall(TokenPosition::kNoSource, target, argument_count, |
| argument_names, ICData::kStatic); |
| instructions += Drop(); |
| break; |
| } |
| case kLocalInitializer: { |
| // The other initializers following this one might read the variable. |
| // This is used e.g. for evaluating the arguments to a super call |
| // first, run normal field initializers next and then make the actual |
| // super call: |
| // |
| // The frontend converts |
| // |
| // class A { |
| // var x; |
| // A(a, b) : super(a + b), x = 2*b {} |
| // } |
| // |
| // to |
| // |
| // class A { |
| // var x; |
| // A(a, b) : tmp = a + b, x = 2*b, super(tmp) {} |
| // } |
| // |
| // (This is strictly speaking not what one should do in terms of the |
| // specification but that is how it is currently implemented.) |
| LocalVariable* variable = |
| LookupVariable(ReaderOffset() + data_program_offset_); |
| |
| // Variable declaration |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
| ASSERT(!helper.IsConst()); |
| Tag tag = ReadTag(); // read (first part of) initializer. |
| if (tag != kSomething) { |
| UNREACHABLE(); |
| } |
| |
| instructions += BuildExpression(); // read initializer. |
| instructions += StoreLocal(TokenPosition::kNoSource, variable); |
| instructions += Drop(); |
| break; |
| } |
| default: |
| ReportUnexpectedTag("initializer", tag); |
| UNREACHABLE(); |
| } |
| } |
| } |
| return instructions; |
| } |
| |
| // If no type arguments are passed to a generic function, we need to fill the |
| // type arguments in with the default types stored on the TypeParameter nodes |
| // in Kernel. |
| Fragment StreamingFlowGraphBuilder::BuildDefaultTypeHandling( |
| const Function& function, |
| intptr_t type_parameters_offset) { |
| if (function.IsGeneric() && I->reify_generic_functions()) { |
| AlternativeReadingScope alt(&reader_); |
| SetOffset(type_parameters_offset); |
| intptr_t num_type_params = ReadListLength(); |
| ASSERT(num_type_params == function.NumTypeParameters()); |
| TypeArguments& default_types = |
| TypeArguments::ZoneHandle(TypeArguments::New(num_type_params)); |
| for (intptr_t i = 0; i < num_type_params; ++i) { |
| TypeParameterHelper helper(this); |
| helper.ReadUntilExcludingAndSetJustRead( |
| TypeParameterHelper::kDefaultType); |
| if (ReadTag() == kSomething) { |
| default_types.SetTypeAt(i, T.BuildType()); |
| } else { |
| default_types.SetTypeAt(i, Object::dynamic_type()); |
| } |
| helper.Finish(); |
| } |
| default_types = default_types.Canonicalize(); |
| |
| if (!default_types.IsNull()) { |
| // clang-format off |
| return B->TestAnyTypeArgs(/*then=*/{}, /*else=*/{ |
| TranslateInstantiatedTypeArguments(default_types), |
| StoreLocal(TokenPosition::kNoSource, |
| parsed_function()->function_type_arguments()), |
| Drop() |
| }); |
| // clang-format on |
| } |
| } |
| return Fragment(); |
| } |
| |
| FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfImplicitClosureFunction( |
| const Function& function) { |
| const Function& parent = Function::ZoneHandle(Z, function.parent_function()); |
| const String& func_name = String::ZoneHandle(Z, parent.name()); |
| const Class& owner = Class::ZoneHandle(Z, parent.Owner()); |
| Function& target = Function::ZoneHandle(Z, owner.LookupFunction(func_name)); |
| |
| if (!target.IsNull() && (target.raw() != parent.raw())) { |
| DEBUG_ASSERT(Isolate::Current()->HasAttemptedReload()); |
| if ((target.is_static() != parent.is_static()) || |
| (target.kind() != parent.kind())) { |
| target = Function::null(); |
| } |
| } |
| |
| if (target.IsNull() || |
| (parent.num_fixed_parameters() != target.num_fixed_parameters())) { |
| return BuildGraphOfNoSuchMethodForwarder(function, true, |
| parent.is_static()); |
| } |
| |
| // The prologue builder needs the default parameter values. |
| SetupDefaultParameterValues(); |
| |
| TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry(); |
| PrologueInfo prologue_info(-1, -1); |
| BlockEntryInstr* instruction_cursor = |
| flow_graph_builder_->BuildPrologue(normal_entry, &prologue_info); |
| |
| flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr( |
| *parsed_function(), normal_entry, Compiler::kNoOSRDeoptId); |
| |
| Fragment body(instruction_cursor); |
| body += |
| flow_graph_builder_->CheckStackOverflowInPrologue(function.token_pos()); |
| |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
| |
| body += BuildDefaultTypeHandling(function, ReaderOffset()); |
| |
| intptr_t type_args_len = 0; |
| if (I->reify_generic_functions() && function.IsGeneric()) { |
| type_args_len = function.NumTypeParameters(); |
| ASSERT(parsed_function()->function_type_arguments() != NULL); |
| body += LoadLocal(parsed_function()->function_type_arguments()); |
| body += PushArgument(); |
| } |
| |
| if (I->argument_type_checks()) { |
| if (!target.NeedsArgumentTypeChecks(I)) { |
| // Tearoffs of static methods needs to perform arguments checks since |
| // static methods they forward to don't do it themselves. |
| AlternativeReadingScope _(&reader_); |
| body += BuildArgumentTypeChecks(); |
| } else { |
| // Check if parent function was annotated with no-dynamic-invocations. |
| const ProcedureAttributesMetadata attrs = |
| procedure_attributes_metadata_helper_.GetProcedureAttributes( |
| parent.kernel_offset()); |
| if (!attrs.has_dynamic_invocations) { |
| // If it was then we might need to build some checks in the |
| // tear-off. |
| AlternativeReadingScope _(&reader_); |
| body += |
| BuildArgumentTypeChecks(kTypeChecksForNoDynamicInvocationsTearOff); |
| } |
| } |
| } |
| |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| |
| // Load all the arguments. |
| if (!target.is_static()) { |
| // The context has a fixed shape: a single variable which is the |
| // closed-over receiver. |
| body += LoadLocal(parsed_function()->current_context_var()); |
| body += flow_graph_builder_->LoadField(Context::variable_offset(0)); |
| body += PushArgument(); |
| } |
| |
| // Positional. |
| intptr_t positional_argument_count = ReadListLength(); |
| for (intptr_t i = 0; i < positional_argument_count; ++i) { |
| body += LoadLocal(LookupVariable( |
| ReaderOffset() + data_program_offset_)); // ith variable offset. |
| body += PushArgument(); |
| SkipVariableDeclaration(); // read ith variable. |
| } |
| |
| // Named. |
| intptr_t named_argument_count = ReadListLength(); |
| Array& argument_names = Array::ZoneHandle(Z); |
| if (named_argument_count > 0) { |
| argument_names = Array::New(named_argument_count, H.allocation_space()); |
| for (intptr_t i = 0; i < named_argument_count; ++i) { |
| // ith variable offset. |
| body += LoadLocal(LookupVariable(ReaderOffset() + data_program_offset_)); |
| body += PushArgument(); |
| |
| // read ith variable. |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd); |
| |
| argument_names.SetAt(i, H.DartSymbolObfuscate(helper.name_index_)); |
| } |
| } |
| |
| // Forward them to the parent. |
| intptr_t argument_count = positional_argument_count + named_argument_count; |
| if (!parent.is_static()) { |
| ++argument_count; |
| } |
| body += StaticCall(TokenPosition::kNoSource, target, argument_count, |
| argument_names, ICData::kNoRebind, |
| /* result_type = */ NULL, type_args_len); |
| |
| // Return the result. |
| body += Return(function_node_helper.end_position_); |
| |
| return new (Z) |
| FlowGraph(*parsed_function(), flow_graph_builder_->graph_entry_, |
| flow_graph_builder_->last_used_block_id_, prologue_info); |
| } |
| |
| // If throw_no_such_method_error is set to true (defaults to false), an |
| // instance of NoSuchMethodError is thrown. Otherwise, the instance |
| // noSuchMethod is called. |
| FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfNoSuchMethodForwarder( |
| const Function& function, |
| bool is_implicit_closure_function, |
| bool throw_no_such_method_error) { |
| // The prologue builder needs the default parameter values. |
| SetupDefaultParameterValues(); |
| |
| TargetEntryInstr* normal_entry = B->BuildTargetEntry(); |
| PrologueInfo prologue_info(-1, -1); |
| BlockEntryInstr* instruction_cursor = |
| B->BuildPrologue(normal_entry, &prologue_info); |
| |
| B->graph_entry_ = new (Z) GraphEntryInstr(*parsed_function(), normal_entry, |
| Compiler::kNoOSRDeoptId); |
| |
| Fragment body(instruction_cursor); |
| body += B->CheckStackOverflowInPrologue(function.token_pos()); |
| |
| // If we are inside the tearoff wrapper function (implicit closure), we need |
| // to extract the receiver from the context. We just replace it directly on |
| // the stack to simplify the rest of the code. |
| if (is_implicit_closure_function) { |
| if (parsed_function()->has_arg_desc_var()) { |
| body += B->LoadArgDescriptor(); |
| body += LoadField(ArgumentsDescriptor::count_offset()); |
| body += LoadLocal(parsed_function()->current_context_var()); |
| body += B->LoadField(Context::variable_offset(0)); |
| body += B->StoreFpRelativeSlot(kWordSize * kParamEndSlotFromFp); |
| body += Drop(); |
| } else { |
| body += LoadLocal(parsed_function()->current_context_var()); |
| body += B->LoadField(Context::variable_offset(0)); |
| body += B->StoreFpRelativeSlot( |
| kWordSize * (kParamEndSlotFromFp + function.NumParameters())); |
| body += Drop(); |
| } |
| } |
| |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
| |
| if (function.NeedsArgumentTypeChecks(I)) { |
| AlternativeReadingScope _(&reader_); |
| body += BuildArgumentTypeChecks(); |
| } |
| |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| |
| body += MakeTemp(); |
| LocalVariable* result = MakeTemporary(); |
| |
| // Do "++argument_count" if any type arguments were passed. |
| LocalVariable* argument_count_var = parsed_function()->expression_temp_var(); |
| body += IntConstant(0); |
| body += StoreLocal(TokenPosition::kNoSource, argument_count_var); |
| body += Drop(); |
| if (function.IsGeneric() && Isolate::Current()->reify_generic_functions()) { |
| // clang-format off |
| body += flow_graph_builder_->TestAnyTypeArgs(/*then=*/{ |
| IntConstant(1), |
| StoreLocal(TokenPosition::kNoSource, argument_count_var), |
| Drop() |
| }, /*else=*/{}); |
| // clang-format on |
| } |
| |
| if (function.HasOptionalParameters()) { |
| body += B->LoadArgDescriptor(); |
| body += LoadField(ArgumentsDescriptor::count_offset()); |
| } else { |
| body += IntConstant(function.NumParameters()); |
| } |
| body += LoadLocal(argument_count_var); |
| body += B->SmiBinaryOp(Token::kADD, /* truncate= */ true); |
| LocalVariable* argument_count = MakeTemporary(); |
| |
| // We are generating code like the following: |
| // |
| // var arguments = new Array<dynamic>(argument_count); |
| // |
| // int i = 0; |
| // if (any type arguments are passed) { |
| // arguments[0] = function_type_arguments; |
| // ++i; |
| // } |
| // |
| // for (; i < argument_count; ++i) { |
| // arguments[i] = LoadFpRelativeSlot( |
| // kWordSize * (kParamEndSlotFromFp + argument_count - i)); |
| // } |
| body += Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null())); |
| body += LoadLocal(argument_count); |
| body += CreateArray(); |
| LocalVariable* arguments = MakeTemporary(); |
| |
| { |
| // int i = 0 |
| LocalVariable* index = parsed_function()->expression_temp_var(); |
| body += IntConstant(0); |
| body += StoreLocal(TokenPosition::kNoSource, index); |
| body += Drop(); |
| |
| // if (any type arguments are passed) { |
| // arguments[0] = function_type_arguments; |
| // i = 1; |
| // } |
| if (function.IsGeneric() && Isolate::Current()->reify_generic_functions()) { |
| // clang-format off |
| Fragment store_type_arguments = { |
| LoadLocal(arguments), |
| IntConstant(0), |
| LoadFunctionTypeArguments(), |
| StoreIndexed(kArrayCid), |
| Drop(), |
| IntConstant(1), |
| StoreLocal(TokenPosition::kNoSource, index), |
| Drop() |
| }; |
| // clang-format on |
| body += B->TestAnyTypeArgs(store_type_arguments, {}); |
| } |
| |
| TargetEntryInstr* body_entry; |
| TargetEntryInstr* loop_exit; |
| |
| Fragment condition; |
| // i < argument_count |
| condition += LoadLocal(index); |
| condition += LoadLocal(argument_count); |
| condition += B->SmiRelationalOp(Token::kLT); |
| condition += BranchIfTrue(&body_entry, &loop_exit, /*negate=*/false); |
| |
| Fragment loop_body(body_entry); |
| |
| // arguments[i] = LoadFpRelativeSlot( |
| // kWordSize * (kParamEndSlotFromFp + argument_count - i)); |
| loop_body += LoadLocal(arguments); |
| loop_body += LoadLocal(index); |
| loop_body += LoadLocal(argument_count); |
| loop_body += LoadLocal(index); |
| loop_body += B->SmiBinaryOp(Token::kSUB, /*truncate=*/true); |
| loop_body += B->LoadFpRelativeSlot(kWordSize * kParamEndSlotFromFp); |
| loop_body += StoreIndexed(kArrayCid); |
| loop_body += Drop(); |
| |
| // ++i |
| loop_body += LoadLocal(index); |
| loop_body += IntConstant(1); |
| loop_body += B->SmiBinaryOp(Token::kADD, /*truncate=*/true); |
| loop_body += StoreLocal(TokenPosition::kNoSource, index); |
| loop_body += Drop(); |
| |
| JoinEntryInstr* join = BuildJoinEntry(); |
| loop_body += Goto(join); |
| |
| Fragment loop(join); |
| loop += condition; |
| |
| Instruction* entry = |
| new (Z) GotoInstr(join, Thread::Current()->GetNextDeoptId()); |
| body += Fragment(entry, loop_exit); |
| } |
| |
| // Load receiver. |
| if (is_implicit_closure_function) { |
| if (throw_no_such_method_error) { |
| const Function& parent = |
| Function::ZoneHandle(Z, function.parent_function()); |
| const Class& owner = Class::ZoneHandle(Z, parent.Owner()); |
| AbstractType& type = AbstractType::ZoneHandle(Z); |
| type ^= Type::New(owner, TypeArguments::Handle(Z), owner.token_pos(), |
| Heap::kOld); |
| // If the current class is the result of a mixin application, we must |
| // use the class scope of the class from which the function originates. |
| if (owner.IsMixinApplication()) { |
| ClassFinalizer::FinalizeType( |
| Class::Handle(Z, parsed_function()->function().origin()), type); |
| } else { |
| type ^= ClassFinalizer::FinalizeType(owner, type); |
| } |
| body += Constant(type); |
| } else { |
| body += LoadLocal(parsed_function()->current_context_var()); |
| body += B->LoadField(Context::variable_offset(0)); |
| } |
| } else { |
| LocalScope* scope = parsed_function()->node_sequence()->scope(); |
| body += LoadLocal(scope->VariableAt(0)); |
| } |
| body += PushArgument(); |
| |
| body += Constant(String::ZoneHandle(Z, function.name())); |
| body += PushArgument(); |
| |
| if (!parsed_function()->has_arg_desc_var()) { |
| // If there is no variable for the arguments descriptor (this function's |
| // signature doesn't require it), then we need to create one. |
| Array& args_desc = Array::ZoneHandle( |
| Z, ArgumentsDescriptor::New(0, function.NumParameters())); |
| body += Constant(args_desc); |
| } else { |
| body += B->LoadArgDescriptor(); |
| } |
| body += PushArgument(); |
| |
| body += LoadLocal(arguments); |
| body += PushArgument(); |
| |
| if (throw_no_such_method_error) { |
| const Function& parent = |
| Function::ZoneHandle(Z, function.parent_function()); |
| const Class& owner = Class::ZoneHandle(Z, parent.Owner()); |
| InvocationMirror::Level im_level = owner.IsTopLevel() |
| ? InvocationMirror::kTopLevel |
| : InvocationMirror::kStatic; |
| InvocationMirror::Kind im_kind; |
| if (function.IsImplicitGetterFunction() || function.IsGetterFunction()) { |
| im_kind = InvocationMirror::kGetter; |
| } else if (function.IsImplicitSetterFunction() || |
| function.IsSetterFunction()) { |
| im_kind = InvocationMirror::kSetter; |
| } else { |
| im_kind = InvocationMirror::kMethod; |
| } |
| body += IntConstant(InvocationMirror::EncodeType(im_level, im_kind)); |
| } else { |
| body += NullConstant(); |
| } |
| body += PushArgument(); |
| |
| // Push the number of delayed type arguments. |
| if (function.IsClosureFunction()) { |
| LocalVariable* closure = |
| parsed_function()->node_sequence()->scope()->VariableAt(0); |
| body += B->TestDelayedTypeArgs( |
| closure, |
| Fragment({IntConstant(function.NumTypeParameters()), |
| StoreLocal(TokenPosition::kNoSource, argument_count_var), |
| Drop()}), |
| Fragment({IntConstant(0), |
| StoreLocal(TokenPosition::kNoSource, argument_count_var), |
| Drop()})); |
| body += LoadLocal(argument_count_var); |
| } else { |
| body += IntConstant(0); |
| } |
| body += PushArgument(); |
| |
| const Class& mirror_class = |
| Class::Handle(Z, Library::LookupCoreClass(Symbols::InvocationMirror())); |
| ASSERT(!mirror_class.IsNull()); |
| const Function& allocation_function = Function::ZoneHandle( |
| Z, mirror_class.LookupStaticFunction(Library::PrivateCoreLibName( |
| Symbols::AllocateInvocationMirrorForClosure()))); |
| ASSERT(!allocation_function.IsNull()); |
| body += StaticCall(TokenPosition::kMinSource, allocation_function, |
| /* argument_count = */ 5, ICData::kStatic); |
| body += PushArgument(); // For the call to noSuchMethod. |
| |
| if (throw_no_such_method_error) { |
| const Class& klass = Class::ZoneHandle( |
| Z, Library::LookupCoreClass(Symbols::NoSuchMethodError())); |
| ASSERT(!klass.IsNull()); |
| const Function& throw_function = Function::ZoneHandle( |
| Z, |
| klass.LookupStaticFunctionAllowPrivate(Symbols::ThrowNewInvocation())); |
| ASSERT(!throw_function.IsNull()); |
| body += StaticCall(TokenPosition::kNoSource, throw_function, 2, |
| ICData::kStatic); |
| } else { |
| body += InstanceCall(TokenPosition::kNoSource, Symbols::NoSuchMethod(), |
| Token::kILLEGAL, 2, 1); |
| } |
| body += StoreLocal(TokenPosition::kNoSource, result); |
| body += Drop(); |
| |
| body += Drop(); // arguments |
| body += Drop(); // argument count |
| |
| AbstractType& return_type = AbstractType::Handle(function.result_type()); |
| if (!return_type.IsDynamicType() && !return_type.IsVoidType() && |
| !return_type.IsObjectType()) { |
| body += flow_graph_builder_->AssertAssignable( |
| TokenPosition::kNoSource, return_type, Symbols::Empty()); |
| } |
| body += Return(TokenPosition::kNoSource); |
| |
| return new (Z) FlowGraph(*parsed_function(), B->graph_entry_, |
| B->last_used_block_id_, prologue_info); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildArgumentTypeChecks( |
| TypeChecksToBuild mode /*= kDefaultTypeChecks*/) { |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.SetNext(FunctionNodeHelper::kTypeParameters); |
| const Function& dart_function = parsed_function()->function(); |
| |
| Fragment body; |
| |
| const Function* forwarding_target = NULL; |
| if (parsed_function()->is_forwarding_stub()) { |
| NameIndex target_name = parsed_function()->forwarding_stub_super_target(); |
| const String& name = dart_function.IsSetterFunction() |
| ? H.DartSetterName(target_name) |
| : H.DartProcedureName(target_name); |
| forwarding_target = |
| &Function::ZoneHandle(Z, LookupMethodByMember(target_name, name)); |
| ASSERT(!forwarding_target->IsNull()); |
| } |
| |
| // Type parameters |
| if (mode == kDefaultTypeChecks) { |
| intptr_t num_type_params = ReadListLength(); |
| TypeArguments& forwarding_params = TypeArguments::Handle(Z); |
| if (forwarding_target != NULL) { |
| forwarding_params = forwarding_target->type_parameters(); |
| ASSERT(forwarding_params.Length() == num_type_params); |
| } |
| TypeParameter& forwarding_param = TypeParameter::Handle(Z); |
| for (intptr_t i = 0; i < num_type_params; ++i) { |
| TypeParameterHelper helper(this); |
| helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound); |
| String& name = H.DartSymbolObfuscate(helper.name_index_); |
| AbstractType& bound = T.BuildType(); // read bound |
| helper.Finish(); |
| |
| if (forwarding_target != NULL) { |
| forwarding_param ^= forwarding_params.TypeAt(i); |
| bound = forwarding_param.bound(); |
| } |
| |
| if (I->strong() && !bound.IsObjectType() && !bound.IsDynamicType() && |
| (I->reify_generic_functions() || dart_function.IsFactory())) { |
| TypeParameter& param = TypeParameter::Handle(Z); |
| if (dart_function.IsFactory()) { |
| param ^= TypeArguments::Handle( |
| Class::Handle(dart_function.Owner()).type_parameters()) |
| .TypeAt(i); |
| } else { |
| param ^= |
| TypeArguments::Handle(dart_function.type_parameters()).TypeAt(i); |
| } |
| ASSERT(param.IsFinalized()); |
| body += CheckTypeArgumentBound(param, bound, name); |
| } |
| } |
| function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters); |
| } |
| |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| |
| // Positional. |
| const intptr_t num_positional_params = ReadListLength(); |
| const intptr_t kFirstParameterOffset = 1; |
| for (intptr_t i = 0; i < num_positional_params; ++i) { |
| // ith variable offset. |
| const intptr_t offset = ReaderOffset(); |
| SkipVariableDeclaration(); |
| |
| LocalVariable* param = LookupVariable(offset + data_program_offset_); |
| if (!param->needs_type_check()) { |
| continue; |
| } |
| |
| const AbstractType* target_type = ¶m->type(); |
| if (forwarding_target != NULL) { |
| // We add 1 to the parameter index to account for the receiver. |
| target_type = &AbstractType::ZoneHandle( |
| Z, forwarding_target->ParameterTypeAt(kFirstParameterOffset + i)); |
| } |
| body += LoadLocal(param); |
| body += CheckArgumentType(param, *target_type); |
| body += Drop(); |
| } |
| |
| // Named. |
| const intptr_t num_named_params = ReadListLength(); |
| for (intptr_t i = 0; i < num_named_params; ++i) { |
| // ith variable offset. |
| const intptr_t offset = ReaderOffset(); |
| SkipVariableDeclaration(); |
| |
| LocalVariable* param = LookupVariable(offset + data_program_offset_); |
| if (!param->needs_type_check()) { |
| continue; |
| } |
| |
| const AbstractType* target_type = ¶m->type(); |
| if (forwarding_target != NULL) { |
| // We add 1 to the parameter index to account for the receiver. |
| target_type = &AbstractType::ZoneHandle( |
| Z, forwarding_target->ParameterTypeAt(num_positional_params + i + 1)); |
| } |
| body += LoadLocal(param); |
| body += CheckArgumentType(param, *target_type); |
| body += Drop(); |
| } |
| |
| return body; |
| } |
| |
| FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(bool constructor) { |
| // The prologue builder needs the default parameter values. |
| SetupDefaultParameterValues(); |
| |
| const Function& dart_function = parsed_function()->function(); |
| TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry(); |
| PrologueInfo prologue_info(-1, -1); |
| BlockEntryInstr* instruction_cursor = |
| flow_graph_builder_->BuildPrologue(normal_entry, &prologue_info); |
| |
| flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr( |
| *parsed_function(), normal_entry, flow_graph_builder_->osr_id_); |
| |
| Fragment body; |
| |
| LocalVariable* closure = NULL; |
| if (dart_function.IsClosureFunction()) { |
| closure = parsed_function()->node_sequence()->scope()->VariableAt(0); |
| } |
| |
| if (!dart_function.is_native()) { |
| body += flow_graph_builder_->CheckStackOverflowInPrologue( |
| dart_function.token_pos()); |
| } |
| intptr_t context_size = |
| parsed_function()->node_sequence()->scope()->num_context_variables(); |
| if (context_size > 0) { |
| body += flow_graph_builder_->PushContext(context_size); |
| LocalVariable* context = MakeTemporary(); |
| |
| // Copy captured parameters from the stack into the context. |
| LocalScope* scope = parsed_function()->node_sequence()->scope(); |
| intptr_t parameter_count = dart_function.NumParameters(); |
| intptr_t parameter_index = parsed_function()->first_parameter_index(); |
| |
| const ParsedFunction& pf = *flow_graph_builder_->parsed_function_; |
| const Function& function = pf.function(); |
| |
| for (intptr_t i = 0; i < parameter_count; ++i, --parameter_index) { |
| LocalVariable* variable = scope->VariableAt(i); |
| if (variable->is_captured()) { |
| LocalVariable& raw_parameter = *pf.RawParameterVariable(i); |
| ASSERT((function.HasOptionalParameters() && |
| raw_parameter.owner() == scope) || |
| (!function.HasOptionalParameters() && |
| raw_parameter.owner() == NULL)); |
| ASSERT(!raw_parameter.is_captured()); |
| |
| // Copy the parameter from the stack to the context. Overwrite it |
| // with a null constant on the stack so the original value is |
| // eligible for garbage collection. |
| body += LoadLocal(context); |
| body += LoadLocal(&raw_parameter); |
| body += flow_graph_builder_->StoreInstanceField( |
| TokenPosition::kNoSource, |
| Context::variable_offset(variable->index())); |
| body += NullConstant(); |
| body += StoreLocal(TokenPosition::kNoSource, &raw_parameter); |
| body += Drop(); |
| } |
| } |
| body += Drop(); // The context. |
| } |
| if (constructor) { |
| // TODO(27590): Currently the [VariableDeclaration]s from the |
| // initializers will be visible inside the entire body of the constructor. |
| // We should make a separate scope for them. |
| body += BuildInitializers(Class::Handle(Z, dart_function.Owner())); |
| } |
| |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
| intptr_t type_parameters_offset = ReaderOffset(); |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| intptr_t first_parameter_offset = -1; |
| { |
| AlternativeReadingScope alt(&reader_); |
| intptr_t list_length = ReadListLength(); // read number of positionals. |
| if (list_length > 0) { |
| first_parameter_offset = ReaderOffset() + data_program_offset_; |
| } |
| } |
| // Current position: About to read list of positionals. |
| |
| // The specification defines the result of `a == b` to be: |
| // |
| // a) if either side is `null` then the result is `identical(a, b)`. |
| // b) else the result is `a.operator==(b)` |
| // |
| // For user-defined implementations of `operator==` we need therefore |
| // implement the handling of a). |
| // |
| // The default `operator==` implementation in `Object` is implemented in terms |
| // of identical (which we assume here!) which means that case a) is actually |
| // included in b). So we just use the normal implementation in the body. |
| if ((dart_function.NumParameters() == 2) && |
| (dart_function.name() == Symbols::EqualOperator().raw()) && |
| (dart_function.Owner() != I->object_store()->object_class())) { |
| LocalVariable* parameter = LookupVariable(first_parameter_offset); |
| |
| TargetEntryInstr* null_entry; |
| TargetEntryInstr* non_null_entry; |
| |
| body += LoadLocal(parameter); |
| body += BranchIfNull(&null_entry, &non_null_entry); |
| |
| // The argument was `null` and the receiver is not the null class (we only |
| // go into this branch for user-defined == operators) so we can return |
| // false. |
| Fragment null_fragment(null_entry); |
| null_fragment += Constant(Bool::False()); |
| null_fragment += Return(dart_function.end_token_pos()); |
| |
| body = Fragment(body.entry, non_null_entry); |
| } |
| |
| // If we run in checked mode or strong mode, we have to check the type of the |
| // passed arguments. |
| if (dart_function.NeedsArgumentTypeChecks(I)) { |
| AlternativeReadingScope _(&reader_); |
| SetOffset(type_parameters_offset); |
| body += BuildArgumentTypeChecks(); |
| } |
| |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody); |
| |
| bool has_body = ReadTag() == kSomething; // read first part of body. |
| |
| if (dart_function.is_native()) { |
| body += flow_graph_builder_->NativeFunctionBody(first_parameter_offset, |
| dart_function); |
| } else if (has_body) { |
| body += BuildStatement(); // read body. |
| } |
| if (body.is_open()) { |
| body += NullConstant(); |
| body += Return(dart_function.end_token_pos()); |
| } |
| |
| // If functions body contains any yield points build switch statement that |
| // selects a continuation point based on the value of :await_jump_var. |
| if (!yield_continuations().is_empty()) { |
| // The code we are building will be executed right after we enter |
| // the function and before any nested contexts are allocated. |
| // Reset current context_depth_ to match this. |
| const intptr_t current_context_depth = flow_graph_builder_->context_depth_; |
| flow_graph_builder_->context_depth_ = |
| scopes()->yield_jump_variable->owner()->context_level(); |
| |
| // Prepend an entry corresponding to normal entry to the function. |
| yield_continuations().InsertAt( |
| 0, YieldContinuation(new (Z) DropTempsInstr(0, NULL), |
| CatchClauseNode::kInvalidTryIndex)); |
| yield_continuations()[0].entry->LinkTo(body.entry); |
| |
| // Build a switch statement. |
| Fragment dispatch; |
| |
| // Load :await_jump_var into a temporary. |
| dispatch += LoadLocal(scopes()->yield_jump_variable); |
| dispatch += StoreLocal(TokenPosition::kNoSource, scopes()->switch_variable); |
| dispatch += Drop(); |
| |
| BlockEntryInstr* block = NULL; |
| for (intptr_t i = 0; i < yield_continuations().length(); i++) { |
| if (i == 1) { |
| // This is not a normal entry but a resumption. Restore |
| // :current_context_var from :await_ctx_var. |
| // Note: after this point context_depth_ does not match current context |
| // depth so we should not access any local variables anymore. |
| dispatch += LoadLocal(scopes()->yield_context_variable); |
| dispatch += StoreLocal(TokenPosition::kNoSource, |
| parsed_function()->current_context_var()); |
| dispatch += Drop(); |
| } |
| if (i == (yield_continuations().length() - 1)) { |
| // We reached the last possibility, no need to build more ifs. |
| // Continue to the last continuation. |
| // Note: continuations start with nop DropTemps instruction |
| // which acts like an anchor, so we need to skip it. |
| block->set_try_index(yield_continuations()[i].try_index); |
| dispatch <<= yield_continuations()[i].entry->next(); |
| break; |
| } |
| |
| // Build comparison: |
| // |
| // if (:await_ctx_var == i) { |
| // -> yield_continuations()[i] |
| // } else ... |
| // |
| TargetEntryInstr* then; |
| TargetEntryInstr* otherwise; |
| dispatch += LoadLocal(scopes()->switch_variable); |
| dispatch += IntConstant(i); |
| dispatch += flow_graph_builder_->BranchIfStrictEqual(&then, &otherwise); |
| |
| // True branch is linked to appropriate continuation point. |
| // Note: continuations start with nop DropTemps instruction |
| // which acts like an anchor, so we need to skip it. |
| then->LinkTo(yield_continuations()[i].entry->next()); |
| then->set_try_index(yield_continuations()[i].try_index); |
| // False branch will contain the next comparison. |
| dispatch = Fragment(dispatch.entry, otherwise); |
| block = otherwise; |
| } |
| body = dispatch; |
| |
| flow_graph_builder_->context_depth_ = current_context_depth; |
| } |
| |
| // :function_type_arguments_var handling is built here and prepended to the |
| // body because it needs to be executed everytime we enter the function - |
| // even if we are resuming from the yield. |
| Fragment prologue; |
| |
| prologue += BuildDefaultTypeHandling(dart_function, type_parameters_offset); |
| |
| if (dart_function.IsClosureFunction() && |
| dart_function.NumParentTypeParameters() > 0 && |
| I->reify_generic_functions()) { |
| // Function with yield points can not be generic itself but the outer |
| // function can be. |
| ASSERT(yield_continuations().is_empty() || !dart_function.IsGeneric()); |
| |
| LocalVariable* fn_type_args = parsed_function()->function_type_arguments(); |
| ASSERT(fn_type_args != NULL && closure != NULL); |
| |
| if (dart_function.IsGeneric()) { |
| prologue += LoadLocal(fn_type_args); |
| prologue += PushArgument(); |
| prologue += LoadLocal(closure); |
| prologue += LoadField(Closure::function_type_arguments_offset()); |
| prologue += PushArgument(); |
| prologue += IntConstant(dart_function.NumParentTypeParameters()); |
| prologue += PushArgument(); |
| prologue += IntConstant(dart_function.NumTypeParameters() + |
| dart_function.NumParentTypeParameters()); |
| prologue += PushArgument(); |
| |
| const Library& dart_internal = |
| Library::Handle(Z, Library::InternalLibrary()); |
| const Function& prepend_function = |
| Function::ZoneHandle(Z, dart_internal.LookupFunctionAllowPrivate( |
| Symbols::PrependTypeArguments())); |
| ASSERT(!prepend_function.IsNull()); |
| |
| prologue += StaticCall(TokenPosition::kNoSource, prepend_function, 4, |
| ICData::kStatic); |
| prologue += StoreLocal(TokenPosition::kNoSource, fn_type_args); |
| prologue += Drop(); |
| } else { |
| prologue += LoadLocal(closure); |
| prologue += LoadField(Closure::function_type_arguments_offset()); |
| prologue += StoreLocal(TokenPosition::kNoSource, fn_type_args); |
| prologue += Drop(); |
| } |
| } |
| |
| body = prologue + body; |
| |
| if (FLAG_causal_async_stacks && |
| (dart_function.IsAsyncClosure() || dart_function.IsAsyncGenClosure())) { |
| // The code we are building will be executed right after we enter |
| // the function and before any nested contexts are allocated. |
| // Reset current context_depth_ to match this. |
| const intptr_t current_context_depth = flow_graph_builder_->context_depth_; |
| flow_graph_builder_->context_depth_ = |
| scopes()->yield_jump_variable->owner()->context_level(); |
| |
| Fragment instructions; |
| LocalScope* scope = parsed_function()->node_sequence()->scope(); |
| |
| const Function& target = Function::ZoneHandle( |
| Z, I->object_store()->async_set_thread_stack_trace()); |
| ASSERT(!target.IsNull()); |
| |
| // Fetch and load :async_stack_trace |
| LocalVariable* async_stack_trace_var = |
| scope->LookupVariable(Symbols::AsyncStackTraceVar(), false); |
| ASSERT((async_stack_trace_var != NULL) && |
| async_stack_trace_var->is_captured()); |
| instructions += LoadLocal(async_stack_trace_var); |
| instructions += PushArgument(); |
| |
| // Call _asyncSetThreadStackTrace |
| instructions += StaticCall(TokenPosition::kNoSource, target, |
| /* argument_count = */ 1, ICData::kStatic); |
| instructions += Drop(); |
| |
| // TODO(29737): This sequence should be generated in order. |
| body = instructions + body; |
| flow_graph_builder_->context_depth_ = current_context_depth; |
| } |
| |
| if (NeedsDebugStepCheck(dart_function, function_node_helper.position_)) { |
| const intptr_t current_context_depth = flow_graph_builder_->context_depth_; |
| flow_graph_builder_->context_depth_ = 0; |
| // If a switch was added above: Start the switch by injecting a debuggable |
| // safepoint so stepping over an await works. |
| // If not, still start the body with a debuggable safepoint to ensure |
| // breaking on a method always happens, even if there are no |
| // assignments/calls/runtimecalls in the first basic block. |
| // Place this check at the last parameter to ensure parameters |
| // are in scope in the debugger at method entry. |
| const int parameter_count = dart_function.NumParameters(); |
| TokenPosition check_pos = TokenPosition::kNoSource; |
| if (parameter_count > 0) { |
| LocalScope* scope = parsed_function()->node_sequence()->scope(); |
| const LocalVariable& parameter = *scope->VariableAt(parameter_count - 1); |
| check_pos = parameter.token_pos(); |
| } |
| if (!check_pos.IsDebugPause()) { |
| // No parameters or synthetic parameters. |
| check_pos = function_node_helper.position_; |
| ASSERT(check_pos.IsDebugPause()); |
| } |
| |
| // TODO(29737): This sequence should be generated in order. |
| body = DebugStepCheck(check_pos) + body; |
| flow_graph_builder_->context_depth_ = current_context_depth; |
| } |
| |
| instruction_cursor->LinkTo(body.entry); |
| |
| GraphEntryInstr* graph_entry = flow_graph_builder_->graph_entry_; |
| // When compiling for OSR, use a depth first search to find the OSR |
| // entry and make graph entry jump to it instead of normal entry. |
| // Catch entries are always considered reachable, even if they |
| // become unreachable after OSR. |
| if (flow_graph_builder_->osr_id_ != Compiler::kNoOSRDeoptId) { |
| graph_entry->RelinkToOsrEntry(Z, |
| flow_graph_builder_->last_used_block_id_ + 1); |
| } |
| return new (Z) |
| FlowGraph(*parsed_function(), graph_entry, |
| flow_graph_builder_->last_used_block_id_, prologue_info); |
| } |
| |
| FlowGraph* StreamingFlowGraphBuilder::BuildGraph(intptr_t kernel_offset) { |
| ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
| 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_, parsed_function()->function().Owner()); |
| Function& outermost_function = Function::Handle(Z); |
| 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); |
| |
| SetOffset(kernel_offset); |
| |
| #if defined(DART_USE_INTERPRETER) |
| // TODO(regis): Clean up this logic of when to compile. |
| // If the bytecode was previously loaded, we really want to compile. |
| if (!function.HasBytecode()) { |
| bytecode_metadata_helper_.ReadMetadata(function); |
| if (function.HasBytecode()) { |
| return NULL; |
| } |
| } |
| #endif |
| |
| // We need to read out the NSM-forwarder bit before we can build scopes. |
| switch (function.kind()) { |
| case RawFunction::kImplicitClosureFunction: |
| case RawFunction::kRegularFunction: { |
| AlternativeReadingScope alt(&reader_); |
| ReadUntilFunctionNode(parsed_function()); // read until function node. |
| } |
| default: {} |
| } |
| |
| // The IR builder will create its own local variables and scopes, and it |
| // will not need an AST. The code generator will assume that there is a |
| // local variable stack slot allocated for the current context and (I |
| // think) that the runtime will expect it to be at a fixed offset which |
| // requires allocating an unused expression temporary variable. |
| set_scopes(parsed_function()->EnsureKernelScopes()); |
| |
| switch (function.kind()) { |
| case RawFunction::kRegularFunction: |
| case RawFunction::kImplicitClosureFunction: |
| case RawFunction::kGetterFunction: |
| case RawFunction::kSetterFunction: { |
| ReadUntilFunctionNode(parsed_function()); |
| if (function.is_no_such_method_forwarder()) { |
| return BuildGraphOfNoSuchMethodForwarder( |
| function, function.IsImplicitClosureFunction()); |
| } else if (function.IsImplicitClosureFunction()) { |
| return BuildGraphOfImplicitClosureFunction(function); |
| } |
| } |
| /* Falls through */ |
| case RawFunction::kClosureFunction: { |
| ReadUntilFunctionNode(parsed_function()); // read until function node. |
| return BuildGraphOfFunction(false); |
| } |
| case RawFunction::kConstructor: { |
| ReadUntilFunctionNode(parsed_function()); // read until function node. |
| return BuildGraphOfFunction(!function.IsFactory()); |
| } |
| case RawFunction::kImplicitGetter: |
| case RawFunction::kImplicitStaticFinalGetter: |
| case RawFunction::kImplicitSetter: { |
| return IsFieldInitializer(function, Z) |
| ? BuildGraphOfFieldInitializer() |
| : BuildGraphOfFieldAccessor(scopes()->setter_value); |
| } |
| case RawFunction::kMethodExtractor: |
| return flow_graph_builder_->BuildGraphOfMethodExtractor(function); |
| case RawFunction::kNoSuchMethodDispatcher: |
| return flow_graph_builder_->BuildGraphOfNoSuchMethodDispatcher(function); |
| case RawFunction::kInvokeFieldDispatcher: |
| return flow_graph_builder_->BuildGraphOfInvokeFieldDispatcher(function); |
| case RawFunction::kSignatureFunction: |
| case RawFunction::kIrregexpFunction: |
| break; |
| } |
| UNREACHABLE(); |
| return NULL; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStatementAt(intptr_t kernel_offset) { |
| SetOffset(kernel_offset); |
| return BuildStatement(); // read statement. |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildExpression(TokenPosition* position) { |
| uint8_t payload = 0; |
| Tag tag = ReadTag(&payload); // read tag. |
| switch (tag) { |
| case kInvalidExpression: |
| return BuildInvalidExpression(position); |
| case kVariableGet: |
| return BuildVariableGet(position); |
| case kSpecializedVariableGet: |
| return BuildVariableGet(payload, position); |
| case kVariableSet: |
| return BuildVariableSet(position); |
| case kSpecializedVariableSet: |
| return BuildVariableSet(payload, position); |
| case kPropertyGet: |
| return BuildPropertyGet(position); |
| case kPropertySet: |
| return BuildPropertySet(position); |
| case kDirectPropertyGet: |
| return BuildDirectPropertyGet(position); |
| case kDirectPropertySet: |
| return BuildDirectPropertySet(position); |
| case kSuperPropertyGet: |
| return BuildSuperPropertyGet(position); |
| case kSuperPropertySet: |
| return BuildSuperPropertySet(position); |
| case kStaticGet: |
| return BuildStaticGet(position); |
| case kStaticSet: |
| return BuildStaticSet(position); |
| case kMethodInvocation: |
| return BuildMethodInvocation(position); |
| case kSuperMethodInvocation: |
| return BuildSuperMethodInvocation(position); |
| case kDirectMethodInvocation: |
| return BuildDirectMethodInvocation(position); |
| case kStaticInvocation: |
| return BuildStaticInvocation(false, position); |
| case kConstStaticInvocation: |
| return BuildStaticInvocation(true, position); |
| case kConstructorInvocation: |
| return BuildConstructorInvocation(false, position); |
| case kConstConstructorInvocation: |
| return BuildConstructorInvocation(true, position); |
| case kNot: |
| return BuildNot(position); |
| case kLogicalExpression: |
| return BuildLogicalExpression(position); |
| case kConditionalExpression: |
| return BuildConditionalExpression(position); |
| case kStringConcatenation: |
| return BuildStringConcatenation(position); |
| case kIsExpression: |
| return BuildIsExpression(position); |
| case kAsExpression: |
| return BuildAsExpression(position); |
| case kSymbolLiteral: |
| return BuildSymbolLiteral(position); |
| case kTypeLiteral: |
| return BuildTypeLiteral(position); |
| case kThisExpression: |
| return BuildThisExpression(position); |
| case kRethrow: |
| return BuildRethrow(position); |
| case kThrow: |
| return BuildThrow(position); |
| case kListLiteral: |
| return BuildListLiteral(false, position); |
| case kConstListLiteral: |
| return BuildListLiteral(true, position); |
| case kMapLiteral: |
| return BuildMapLiteral(false, position); |
| case kConstMapLiteral: |
| return BuildMapLiteral(true, position); |
| case kFunctionExpression: |
| return BuildFunctionExpression(); |
| case kLet: |
| return BuildLet(position); |
| case kBigIntLiteral: |
| return BuildBigIntLiteral(position); |
| case kStringLiteral: |
| return BuildStringLiteral(position); |
| case kSpecializedIntLiteral: |
| return BuildIntLiteral(payload, position); |
| case kNegativeIntLiteral: |
| return BuildIntLiteral(true, position); |
| case kPositiveIntLiteral: |
| return BuildIntLiteral(false, position); |
| case kDoubleLiteral: |
| return BuildDoubleLiteral(position); |
| case kTrueLiteral: |
| return BuildBoolLiteral(true, position); |
| case kFalseLiteral: |
| return BuildBoolLiteral(false, position); |
| case kNullLiteral: |
| return BuildNullLiteral(position); |
| case kConstantExpression: |
| return BuildConstantExpression(position); |
| case kInstantiation: |
| return BuildPartialTearoffInstantiation(position); |
| case kLoadLibrary: |
| case kCheckLibraryIsLoaded: |
| ReadUInt(); // skip library index |
| return BuildFutureNullValue(position); |
| default: |
| ReportUnexpectedTag("expression", tag); |
| UNREACHABLE(); |
| } |
| |
| return Fragment(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStatement() { |
| Tag tag = ReadTag(); // read tag. |
| switch (tag) { |
| case kExpressionStatement: |
| return BuildExpressionStatement(); |
| case kBlock: |
| return BuildBlock(); |
| case kEmptyStatement: |
| return BuildEmptyStatement(); |
| case kAssertBlock: |
| return BuildAssertBlock(); |
| case kAssertStatement: |
| return BuildAssertStatement(); |
| case kLabeledStatement: |
| return BuildLabeledStatement(); |
| case kBreakStatement: |
| return BuildBreakStatement(); |
| case kWhileStatement: |
| return BuildWhileStatement(); |
| case kDoStatement: |
| return BuildDoStatement(); |
| case kForStatement: |
| return BuildForStatement(); |
| case kForInStatement: |
| return BuildForInStatement(false); |
| case kAsyncForInStatement: |
| return BuildForInStatement(true); |
| case kSwitchStatement: |
| return BuildSwitchStatement(); |
| case kContinueSwitchStatement: |
| return BuildContinueSwitchStatement(); |
| case kIfStatement: |
| return BuildIfStatement(); |
| case kReturnStatement: |
| return BuildReturnStatement(); |
| case kTryCatch: |
| return BuildTryCatch(); |
| case kTryFinally: |
| return BuildTryFinally(); |
| case kYieldStatement: |
| return BuildYieldStatement(); |
| case kVariableDeclaration: |
| return BuildVariableDeclaration(); |
| case kFunctionDeclaration: |
| return BuildFunctionDeclaration(); |
| default: |
| ReportUnexpectedTag("statement", tag); |
| UNREACHABLE(); |
| } |
| return Fragment(); |
| } |
| |
| intptr_t KernelReaderHelper::ReaderOffset() const { |
| return reader_.offset(); |
| } |
| |
| void KernelReaderHelper::SetOffset(intptr_t offset) { |
| reader_.set_offset(offset); |
| } |
| |
| void KernelReaderHelper::SkipBytes(intptr_t bytes) { |
| reader_.set_offset(ReaderOffset() + bytes); |
| } |
| |
| bool KernelReaderHelper::ReadBool() { |
| return reader_.ReadBool(); |
| } |
| |
| uint8_t KernelReaderHelper::ReadByte() { |
| return reader_.ReadByte(); |
| } |
| |
| uint32_t KernelReaderHelper::ReadUInt() { |
| return reader_.ReadUInt(); |
| } |
| |
| uint32_t KernelReaderHelper::ReadUInt32() { |
| return reader_.ReadUInt32(); |
| } |
| |
| uint32_t KernelReaderHelper::PeekUInt() { |
| AlternativeReadingScope alt(&reader_); |
| return reader_.ReadUInt(); |
| } |
| |
| double KernelReaderHelper::ReadDouble() { |
| return reader_.ReadDouble(); |
| } |
| |
| uint32_t KernelReaderHelper::PeekListLength() { |
| AlternativeReadingScope alt(&reader_); |
| return reader_.ReadListLength(); |
| } |
| |
| intptr_t KernelReaderHelper::ReadListLength() { |
| return reader_.ReadListLength(); |
| } |
| |
| StringIndex KernelReaderHelper::ReadStringReference() { |
| return StringIndex(ReadUInt()); |
| } |
| |
| NameIndex KernelReaderHelper::ReadCanonicalNameReference() { |
| return reader_.ReadCanonicalNameReference(); |
| } |
| |
| StringIndex KernelReaderHelper::ReadNameAsStringIndex() { |
| StringIndex name_index = ReadStringReference(); // read name index. |
| if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') { |
| ReadUInt(); // read library index. |
| } |
| return name_index; |
| } |
| |
| const String& KernelReaderHelper::ReadNameAsMethodName() { |
| StringIndex name_index = ReadStringReference(); // read name index. |
| if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') { |
| NameIndex library_reference = |
| ReadCanonicalNameReference(); // read library index. |
| return H.DartMethodName(library_reference, name_index); |
| } else { |
| return H.DartMethodName(NameIndex(), name_index); |
| } |
| } |
| |
| const String& KernelReaderHelper::ReadNameAsSetterName() { |
| StringIndex name_index = ReadStringReference(); // read name index. |
| if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') { |
| NameIndex library_reference = |
| ReadCanonicalNameReference(); // read library index. |
| return H.DartSetterName(library_reference, name_index); |
| } else { |
| return H.DartSetterName(NameIndex(), name_index); |
| } |
| } |
| |
| const String& KernelReaderHelper::ReadNameAsGetterName() { |
| StringIndex name_index = ReadStringReference(); // read name index. |
| if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') { |
| NameIndex library_reference = |
| ReadCanonicalNameReference(); // read library index. |
| return H.DartGetterName(library_reference, name_index); |
| } else { |
| return H.DartGetterName(NameIndex(), name_index); |
| } |
| } |
| |
| const String& KernelReaderHelper::ReadNameAsFieldName() { |
| StringIndex name_index = ReadStringReference(); // read name index. |
| if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') { |
| NameIndex library_reference = |
| ReadCanonicalNameReference(); // read library index. |
| return H.DartFieldName(library_reference, name_index); |
| } else { |
| return H.DartFieldName(NameIndex(), name_index); |
| } |
| } |
| |
| void KernelReaderHelper::SkipFlags() { |
| ReadFlags(); |
| } |
| |
| void KernelReaderHelper::SkipStringReference() { |
| ReadUInt(); |
| } |
| |
| void KernelReaderHelper::SkipConstantReference() { |
| ReadUInt(); |
| } |
| |
| void KernelReaderHelper::SkipCanonicalNameReference() { |
| ReadUInt(); |
| } |
| |
| void KernelReaderHelper::ReportUnexpectedTag(const char* variant, Tag tag) { |
| H.ReportError(script_, TokenPosition::kNoSource, |
| "Unexpected tag %d (%s) in ?, expected %s", tag, |
| Reader::TagName(tag), variant); |
| } |
| |
| void StreamingFlowGraphBuilder::ReportUnexpectedTag(const char* variant, |
| Tag tag) { |
| if ((flow_graph_builder_ == NULL) || (parsed_function() == NULL)) { |
| KernelReaderHelper::ReportUnexpectedTag(variant, tag); |
| } else { |
| H.ReportError(script_, TokenPosition::kNoSource, |
| "Unexpected tag %d (%s) in %s, expected %s", tag, |
| Reader::TagName(tag), |
| parsed_function()->function().ToQualifiedCString(), variant); |
| } |
| } |
| |
| void KernelReaderHelper::SkipDartType() { |
| Tag tag = ReadTag(); |
| switch (tag) { |
| case kInvalidType: |
| case kDynamicType: |
| case kVoidType: |
| case kBottomType: |
| // those contain nothing. |
| return; |
| case kInterfaceType: |
| SkipInterfaceType(false); |
| return; |
| case kSimpleInterfaceType: |
| SkipInterfaceType(true); |
| return; |
| case kFunctionType: |
| SkipFunctionType(false); |
| return; |
| case kSimpleFunctionType: |
| SkipFunctionType(true); |
| return; |
| case kTypeParameterType: |
| ReadUInt(); // read index for parameter. |
| SkipOptionalDartType(); // read bound bound. |
| return; |
| default: |
| ReportUnexpectedTag("type", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void KernelReaderHelper::SkipOptionalDartType() { |
| Tag tag = ReadTag(); // read tag. |
| if (tag == kNothing) { |
| return; |
| } |
| ASSERT(tag == kSomething); |
| |
| SkipDartType(); // read type. |
| } |
| |
| void KernelReaderHelper::SkipInterfaceType(bool simple) { |
| ReadUInt(); // read klass_name. |
| if (!simple) { |
| SkipListOfDartTypes(); // read list of types. |
| } |
| } |
| |
| void KernelReaderHelper::SkipFunctionType(bool simple) { |
| if (!simple) { |
| SkipTypeParametersList(); // read type_parameters. |
| ReadUInt(); // read required parameter count. |
| ReadUInt(); // read total parameter count. |
| } |
| |
| SkipListOfDartTypes(); // read positional_parameters types. |
| |
| if (!simple) { |
| const intptr_t named_count = |
| ReadListLength(); // read named_parameters list length. |
| for (intptr_t i = 0; i < named_count; ++i) { |
| // read string reference (i.e. named_parameters[i].name). |
| SkipStringReference(); |
| SkipDartType(); // read named_parameters[i].type. |
| } |
| } |
| |
| SkipListOfStrings(); // read positional parameter names. |
| |
| if (!simple) { |
| SkipCanonicalNameReference(); // read typedef reference. |
| } |
| |
| SkipDartType(); // read return type. |
| } |
| |
| void KernelReaderHelper::SkipStatementList() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| SkipStatement(); // read ith expression. |
| } |
| } |
| |
| void KernelReaderHelper::SkipListOfExpressions() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| SkipExpression(); // read ith expression. |
| } |
| } |
| |
| void KernelReaderHelper::SkipListOfDartTypes() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| SkipDartType(); // read ith type. |
| } |
| } |
| |
| void KernelReaderHelper::SkipListOfStrings() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| SkipStringReference(); // read ith string index. |
| } |
| } |
| |
| void KernelReaderHelper::SkipListOfVariableDeclarations() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| SkipVariableDeclaration(); // read ith variable declaration. |
| } |
| } |
| |
| void KernelReaderHelper::SkipTypeParametersList() { |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| TypeParameterHelper helper(this); |
| helper.Finish(); |
| } |
| } |
| |
| void KernelReaderHelper::SkipInitializer() { |
| Tag tag = ReadTag(); |
| ReadByte(); // read isSynthetic flag. |
| switch (tag) { |
| case kInvalidInitializer: |
| return; |
| case kFieldInitializer: |
| SkipCanonicalNameReference(); // read field_reference. |
| SkipExpression(); // read value. |
| return; |
| case kSuperInitializer: |
| SkipCanonicalNameReference(); // read target_reference. |
| SkipArguments(); // read arguments. |
| return; |
| case kRedirectingInitializer: |
| SkipCanonicalNameReference(); // read target_reference. |
| SkipArguments(); // read arguments. |
| return; |
| case kLocalInitializer: |
| SkipVariableDeclaration(); // read variable. |
| return; |
| case kAssertInitializer: |
| SkipStatement(); |
| return; |
| default: |
| ReportUnexpectedTag("initializer", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void KernelReaderHelper::SkipExpression() { |
| uint8_t payload = 0; |
| Tag tag = ReadTag(&payload); |
| switch (tag) { |
| case kInvalidExpression: |
| ReadPosition(); |
| SkipStringReference(); |
| return; |
| case kVariableGet: |
| ReadPosition(); // read position. |
| ReadUInt(); // read kernel position. |
| ReadUInt(); // read relative variable index. |
| SkipOptionalDartType(); // read promoted type. |
| return; |
| case kSpecializedVariableGet: |
| ReadPosition(); // read position. |
| ReadUInt(); // read kernel position. |
| return; |
| case kVariableSet: |
| ReadPosition(); // read position. |
| ReadUInt(); // read kernel position. |
| ReadUInt(); // read relative variable index. |
| SkipExpression(); // read expression. |
| return; |
| case kSpecializedVariableSet: |
| ReadPosition(); // read position. |
| ReadUInt(); // read kernel position. |
| SkipExpression(); // read expression. |
| return; |
| case kPropertyGet: |
| ReadPosition(); // read position. |
| SkipExpression(); // read receiver. |
| SkipName(); // read name. |
| SkipCanonicalNameReference(); // read interface_target_reference. |
| return; |
| case kPropertySet: |
| ReadPosition(); // read position. |
| SkipExpression(); // read receiver. |
| SkipName(); // read name. |
| SkipExpression(); // read value. |
| SkipCanonicalNameReference(); // read interface_target_reference. |
| return; |
| case kSuperPropertyGet: |
| ReadPosition(); // read position. |
| SkipName(); // read name. |
| SkipCanonicalNameReference(); // read interface_target_reference. |
| return; |
| case kSuperPropertySet: |
| ReadPosition(); // read position. |
| SkipName(); // read name. |
| SkipExpression(); // read value. |
| SkipCanonicalNameReference(); // read interface_target_reference. |
| return; |
| case kDirectPropertyGet: |
| ReadPosition(); // read position. |
| SkipExpression(); // read receiver. |
| SkipCanonicalNameReference(); // read target_reference. |
| return; |
| case kDirectPropertySet: |
| ReadPosition(); // read position. |
| SkipExpression(); // read receiver. |
| SkipCanonicalNameReference(); // read target_reference. |
| SkipExpression(); // read value· |
| return; |
| case kStaticGet: |
| ReadPosition(); // read position. |
| SkipCanonicalNameReference(); // read target_reference. |
| return; |
| case kStaticSet: |
| ReadPosition(); // read position. |
| SkipCanonicalNameReference(); // read target_reference. |
| SkipExpression(); // read expression. |
| return; |
| case kMethodInvocation: |
| ReadPosition(); // read position. |
| SkipExpression(); // read receiver. |
| SkipName(); // read name. |
| SkipArguments(); // read arguments. |
| SkipCanonicalNameReference(); // read interface_target_reference. |
| return; |
| case kSuperMethodInvocation: |
| ReadPosition(); // read position. |
| SkipName(); // read name. |
| SkipArguments(); // read arguments. |
| SkipCanonicalNameReference(); // read interface_target_reference. |
| return; |
| case kDirectMethodInvocation: |
| ReadPosition(); // read position. |
| SkipExpression(); // read receiver. |
| SkipCanonicalNameReference(); // read target_reference. |
| SkipArguments(); // read arguments. |
| return; |
| case kStaticInvocation: |
| case kConstStaticInvocation: |
| ReadPosition(); // read position. |
| SkipCanonicalNameReference(); // read procedure_reference. |
| SkipArguments(); // read arguments. |
| return; |
| case kConstructorInvocation: |
| case kConstConstructorInvocation: |
| ReadPosition(); // read position. |
| SkipCanonicalNameReference(); // read target_reference. |
| SkipArguments(); // read arguments. |
| return; |
| case kNot: |
| SkipExpression(); // read expression. |
| return; |
| case kLogicalExpression: |
| SkipExpression(); // read left. |
| SkipBytes(1); // read operator. |
| SkipExpression(); // read right. |
| return; |
| case kConditionalExpression: |
| SkipExpression(); // read condition. |
| SkipExpression(); // read then. |
| SkipExpression(); // read otherwise. |
| SkipOptionalDartType(); // read unused static type. |
| return; |
| case kStringConcatenation: |
| ReadPosition(); // read position. |
| SkipListOfExpressions(); // read list of expressions. |
| return; |
| case kIsExpression: |
| ReadPosition(); // read position. |
| SkipExpression(); // read operand. |
| SkipDartType(); // read type. |
| return; |
| case kAsExpression: |
| ReadPosition(); // read position. |
| SkipFlags(); // read flags. |
| SkipExpression(); // read operand. |
| SkipDartType(); // read type. |
| return; |
| case kSymbolLiteral: |
| SkipStringReference(); // read index into string table. |
| return; |
| case kTypeLiteral: |
| SkipDartType(); // read type. |
| return; |
| case kThisExpression: |
| return; |
| case kRethrow: |
| ReadPosition(); // read position. |
| return; |
| case kThrow: |
| ReadPosition(); // read position. |
| SkipExpression(); // read expression. |
| return; |
| case kListLiteral: |
| case kConstListLiteral: |
| ReadPosition(); // read position. |
| SkipDartType(); // read type. |
| SkipListOfExpressions(); // read list of expressions. |
| return; |
| case kMapLiteral: |
| case kConstMapLiteral: { |
| ReadPosition(); // read position. |
| SkipDartType(); // read key type. |
| SkipDartType(); // read value type. |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| SkipExpression(); // read ith key. |
| SkipExpression(); // read ith value. |
| } |
| return; |
| } |
| case kFunctionExpression: |
| ReadPosition(); // read position. |
| SkipFunctionNode(); // read function node. |
| return; |
| case kLet: |
| SkipVariableDeclaration(); // read variable declaration. |
| SkipExpression(); // read expression. |
| return; |
| case kInstantiation: |
| SkipExpression(); // read expression. |
| SkipListOfDartTypes(); // read type arguments. |
| return; |
| case kBigIntLiteral: |
| SkipStringReference(); // read string reference. |
| return; |
| case kStringLiteral: |
| SkipStringReference(); // read string reference. |
| return; |
| case kSpecializedIntLiteral: |
| return; |
| case kNegativeIntLiteral: |
| ReadUInt(); // read value. |
| return; |
| case kPositiveIntLiteral: |
| ReadUInt(); // read value. |
| return; |
| case kDoubleLiteral: |
| ReadDouble(); // read value. |
| return; |
| case kTrueLiteral: |
| return; |
| case kFalseLiteral: |
| return; |
| case kNullLiteral: |
| return; |
| case kConstantExpression: |
| SkipConstantReference(); |
| return; |
| case kLoadLibrary: |
| case kCheckLibraryIsLoaded: |
| ReadUInt(); // skip library index |
| return; |
| default: |
| ReportUnexpectedTag("expression", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void KernelReaderHelper::SkipStatement() { |
| Tag tag = ReadTag(); // read tag. |
| switch (tag) { |
| case kExpressionStatement: |
| SkipExpression(); // read expression. |
| return; |
| case kBlock: |
| SkipStatementList(); |
| return; |
| case kEmptyStatement: |
| return; |
| case kAssertBlock: |
| SkipStatementList(); |
| return; |
| case kAssertStatement: |
| SkipExpression(); // Read condition. |
| ReadPosition(); // read condition start offset. |
| ReadPosition(); // read condition end offset. |
| if (ReadTag() == kSomething) { |
| SkipExpression(); // read (rest of) message. |
| } |
| return; |
| case kLabeledStatement: |
| SkipStatement(); // read body. |
| return; |
| case kBreakStatement: |
| ReadPosition(); // read position. |
| ReadUInt(); // read target_index. |
| return; |
| case kWhileStatement: |
| ReadPosition(); // read position. |
| SkipExpression(); // read condition. |
| SkipStatement(); // read body. |
| return; |
| case kDoStatement: |
| ReadPosition(); // read position. |
| SkipStatement(); // read body. |
| SkipExpression(); // read condition. |
| return; |
| case kForStatement: { |
| ReadPosition(); // read position. |
| SkipListOfVariableDeclarations(); // read variables. |
| Tag tag = ReadTag(); // Read first part of condition. |
| if (tag == kSomething) { |
| SkipExpression(); // read rest of condition. |
| } |
| SkipListOfExpressions(); // read updates. |
| SkipStatement(); // read body. |
| return; |
| } |
| case kForInStatement: |
| case kAsyncForInStatement: |
| ReadPosition(); // read position. |
| ReadPosition(); // read body position. |
| SkipVariableDeclaration(); // read variable. |
| SkipExpression(); // read iterable. |
| SkipStatement(); // read body. |
| return; |
| case kSwitchStatement: { |
| ReadPosition(); // read position. |
| SkipExpression(); // read condition. |
| int case_count = ReadListLength(); // read number of cases. |
| for (intptr_t i = 0; i < case_count; ++i) { |
| int expression_count = ReadListLength(); // read number of expressions. |
| for (intptr_t j = 0; j < expression_count; ++j) { |
| ReadPosition(); // read jth position. |
| SkipExpression(); // read jth expression. |
| } |
| ReadBool(); // read is_default. |
| SkipStatement(); // read body. |
| } |
| return; |
| } |
| case kContinueSwitchStatement: |
| ReadPosition(); // read position. |
| ReadUInt(); // read target_index. |
| return; |
| case kIfStatement: |
| ReadPosition(); // read position. |
| SkipExpression(); // read condition. |
| SkipStatement(); // read then. |
| SkipStatement(); // read otherwise. |
| return; |
| case kReturnStatement: { |
| ReadPosition(); // read position |
| Tag tag = ReadTag(); // read (first part of) expression. |
| if (tag == kSomething) { |
| SkipExpression(); // read (rest of) expression. |
| } |
| return; |
| } |
| case kTryCatch: { |
| SkipStatement(); // read body. |
| ReadByte(); // read flags |
| intptr_t catch_count = ReadListLength(); // read number of catches. |
| for (intptr_t i = 0; i < catch_count; ++i) { |
| ReadPosition(); // read position. |
| SkipDartType(); // read guard. |
| tag = ReadTag(); // read first part of exception. |
| if (tag == kSomething) { |
| SkipVariableDeclaration(); // read exception. |
| } |
| tag = ReadTag(); // read first part of stack trace. |
| if (tag == kSomething) { |
| SkipVariableDeclaration(); // read stack trace. |
| } |
| SkipStatement(); // read body. |
| } |
| return; |
| } |
| case kTryFinally: |
| SkipStatement(); // read body. |
| SkipStatement(); // read finalizer. |
| return; |
| case kYieldStatement: { |
| TokenPosition position = ReadPosition(); // read position. |
| RecordYieldPosition(position); |
| ReadByte(); // read flags. |
| SkipExpression(); // read expression. |
| return; |
| } |
| case kVariableDeclaration: |
| SkipVariableDeclaration(); // read variable declaration. |
| return; |
| case kFunctionDeclaration: |
| ReadPosition(); // read position. |
| SkipVariableDeclaration(); // read variable. |
| SkipFunctionNode(); // read function node. |
| return; |
| default: |
| ReportUnexpectedTag("statement", tag); |
| UNREACHABLE(); |
| } |
| } |
| |
| void KernelReaderHelper::SkipFunctionNode() { |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd); |
| } |
| |
| void KernelReaderHelper::SkipName() { |
| StringIndex name_index = ReadStringReference(); // read name index. |
| if ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') { |
| SkipCanonicalNameReference(); // read library index. |
| } |
| } |
| |
| void KernelReaderHelper::SkipArguments() { |
| ReadUInt(); // read argument count. |
| |
| SkipListOfDartTypes(); // read list of types. |
| SkipListOfExpressions(); // read positionals. |
| |
| // List of named. |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| SkipStringReference(); // read ith name index. |
| SkipExpression(); // read ith expression. |
| } |
| } |
| |
| void KernelReaderHelper::SkipVariableDeclaration() { |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd); |
| } |
| |
| void KernelReaderHelper::SkipLibraryCombinator() { |
| ReadBool(); // read is_show. |
| intptr_t name_count = ReadUInt(); // read list length. |
| for (intptr_t j = 0; j < name_count; ++j) { |
| ReadUInt(); // read ith entry of name_indices. |
| } |
| } |
| |
| void KernelReaderHelper::SkipLibraryDependency() { |
| ReadPosition(); // read file offset. |
| ReadFlags(); |
| SkipListOfExpressions(); // Annotations. |
| ReadCanonicalNameReference(); |
| ReadStringReference(); // Name. |
| intptr_t combinator_count = ReadListLength(); |
| for (intptr_t i = 0; i < combinator_count; ++i) { |
| SkipLibraryCombinator(); |
| } |
| } |
| |
| void KernelReaderHelper::SkipLibraryPart() { |
| SkipListOfExpressions(); // Read annotations. |
| SkipStringReference(); // Read part URI index. |
| } |
| |
| void KernelReaderHelper::SkipLibraryTypedef() { |
| SkipCanonicalNameReference(); // read canonical name. |
| ReadUInt(); // read source_uri_index. |
| ReadPosition(); // read position. |
| SkipStringReference(); // read name index. |
| SkipListOfExpressions(); // read annotations. |
| SkipTypeParametersList(); // read type parameters. |
| SkipDartType(); // read type. |
| } |
| |
| TokenPosition KernelReaderHelper::ReadPosition(bool record) { |
| TokenPosition position = reader_.ReadPosition(); |
| if (record) { |
| RecordTokenPosition(position); |
| } |
| return position; |
| } |
| |
| void StreamingFlowGraphBuilder::RecordTokenPosition(TokenPosition position) { |
| if (record_for_script_id_ == current_script_id_ && |
| record_token_positions_into_ != NULL && position.IsReal()) { |
| record_token_positions_into_->Add(position.value()); |
| } |
| } |
| |
| void StreamingFlowGraphBuilder::RecordYieldPosition(TokenPosition position) { |
| if (record_for_script_id_ == current_script_id_ && |
| record_yield_positions_into_ != NULL && position.IsReal()) { |
| record_yield_positions_into_->Add(position.value()); |
| } |
| } |
| |
| Tag KernelReaderHelper::ReadTag(uint8_t* payload) { |
| return reader_.ReadTag(payload); |
| } |
| |
| Tag KernelReaderHelper::PeekTag(uint8_t* payload) { |
| return reader_.PeekTag(payload); |
| } |
| |
| void StreamingFlowGraphBuilder::loop_depth_inc() { |
| ++flow_graph_builder_->loop_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::loop_depth_dec() { |
| --flow_graph_builder_->loop_depth_; |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::for_in_depth() { |
| return flow_graph_builder_->for_in_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::for_in_depth_inc() { |
| ++flow_graph_builder_->for_in_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::for_in_depth_dec() { |
| --flow_graph_builder_->for_in_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::catch_depth_inc() { |
| ++flow_graph_builder_->catch_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::catch_depth_dec() { |
| --flow_graph_builder_->catch_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::try_depth_inc() { |
| ++flow_graph_builder_->try_depth_; |
| } |
| |
| void StreamingFlowGraphBuilder::try_depth_dec() { |
| --flow_graph_builder_->try_depth_; |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::CurrentTryIndex() { |
| return flow_graph_builder_->CurrentTryIndex(); |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::AllocateTryIndex() { |
| return flow_graph_builder_->AllocateTryIndex(); |
| } |
| |
| LocalVariable* StreamingFlowGraphBuilder::CurrentException() { |
| return flow_graph_builder_->CurrentException(); |
| } |
| |
| LocalVariable* StreamingFlowGraphBuilder::CurrentStackTrace() { |
| return flow_graph_builder_->CurrentStackTrace(); |
| } |
| |
| CatchBlock* StreamingFlowGraphBuilder::catch_block() { |
| return flow_graph_builder_->catch_block_; |
| } |
| |
| ActiveClass* StreamingFlowGraphBuilder::active_class() { |
| return (flow_graph_builder_ != NULL) ? &flow_graph_builder_->active_class_ |
| : NULL; |
| } |
| |
| ScopeBuildingResult* StreamingFlowGraphBuilder::scopes() { |
| return flow_graph_builder_->scopes_; |
| } |
| |
| void StreamingFlowGraphBuilder::set_scopes(ScopeBuildingResult* scope) { |
| flow_graph_builder_->scopes_ = scope; |
| } |
| |
| ParsedFunction* StreamingFlowGraphBuilder::parsed_function() { |
| return flow_graph_builder_->parsed_function_; |
| } |
| |
| TryFinallyBlock* StreamingFlowGraphBuilder::try_finally_block() { |
| return flow_graph_builder_->try_finally_block_; |
| } |
| |
| SwitchBlock* StreamingFlowGraphBuilder::switch_block() { |
| return flow_graph_builder_->switch_block_; |
| } |
| |
| BreakableBlock* StreamingFlowGraphBuilder::breakable_block() { |
| return flow_graph_builder_->breakable_block_; |
| } |
| |
| GrowableArray<YieldContinuation>& |
| StreamingFlowGraphBuilder::yield_continuations() { |
| return flow_graph_builder_->yield_continuations_; |
| } |
| |
| Value* StreamingFlowGraphBuilder::stack() { |
| return flow_graph_builder_->stack_; |
| } |
| |
| void StreamingFlowGraphBuilder::Push(Definition* definition) { |
| flow_graph_builder_->Push(definition); |
| } |
| |
| Value* StreamingFlowGraphBuilder::Pop() { |
| return flow_graph_builder_->Pop(); |
| } |
| |
| Tag StreamingFlowGraphBuilder::PeekArgumentsFirstPositionalTag() { |
| // read parts of arguments, then go back to before doing so. |
| AlternativeReadingScope alt(&reader_); |
| ReadUInt(); // read number of arguments. |
| |
| SkipListOfDartTypes(); // Read list of types. |
| |
| // List of positional. |
| intptr_t list_length = ReadListLength(); // read list length. |
| if (list_length > 0) { |
| return ReadTag(); // read first tag. |
| } |
| |
| UNREACHABLE(); |
| return kNothing; |
| } |
| |
| const TypeArguments& StreamingFlowGraphBuilder::PeekArgumentsInstantiatedType( |
| const Class& klass) { |
| // read parts of arguments, then go back to before doing so. |
| AlternativeReadingScope alt(&reader_); |
| ReadUInt(); // read argument count. |
| intptr_t list_length = ReadListLength(); // read types list length. |
| return T.BuildInstantiatedTypeArguments(klass, list_length); // read types. |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::PeekArgumentsCount() { |
| return PeekUInt(); |
| } |
| |
| LocalVariable* StreamingFlowGraphBuilder::LookupVariable( |
| intptr_t kernel_offset) { |
| return flow_graph_builder_->LookupVariable(kernel_offset); |
| } |
| |
| LocalVariable* StreamingFlowGraphBuilder::MakeTemporary() { |
| return flow_graph_builder_->MakeTemporary(); |
| } |
| |
| RawFunction* StreamingFlowGraphBuilder::LookupMethodByMember( |
| NameIndex target, |
| const String& method_name) { |
| return flow_graph_builder_->LookupMethodByMember(target, method_name); |
| } |
| |
| Function& StreamingFlowGraphBuilder::FindMatchingFunctionAnyArgs( |
| const Class& klass, |
| const String& name) { |
| // Search the superclass chain for the selector. |
| Function& function = Function::Handle(Z); |
| Class& iterate_klass = Class::Handle(Z, klass.raw()); |
| while (!iterate_klass.IsNull()) { |
| function = iterate_klass.LookupDynamicFunctionAllowPrivate(name); |
| if (!function.IsNull()) break; |
| iterate_klass = iterate_klass.SuperClass(); |
| } |
| return function; |
| } |
| |
| Function& StreamingFlowGraphBuilder::FindMatchingFunction( |
| const Class& klass, |
| const String& name, |
| int type_args_len, |
| int argument_count, |
| const Array& argument_names) { |
| // Search the superclass chain for the selector. |
| Function& function = Function::Handle(Z); |
| Class& iterate_klass = Class::Handle(Z, klass.raw()); |
| while (!iterate_klass.IsNull()) { |
| function = iterate_klass.LookupDynamicFunctionAllowPrivate(name); |
| if (!function.IsNull()) { |
| if (function.AreValidArguments(type_args_len, argument_count, |
| argument_names, |
| /* error_message = */ NULL)) { |
| return function; |
| } |
| } |
| iterate_klass = iterate_klass.SuperClass(); |
| } |
| return Function::Handle(); |
| } |
| |
| bool StreamingFlowGraphBuilder::NeedsDebugStepCheck(const Function& function, |
| TokenPosition position) { |
| return flow_graph_builder_->NeedsDebugStepCheck(function, position); |
| } |
| |
| bool StreamingFlowGraphBuilder::NeedsDebugStepCheck(Value* value, |
| TokenPosition position) { |
| return flow_graph_builder_->NeedsDebugStepCheck(value, position); |
| } |
| |
| void StreamingFlowGraphBuilder::InlineBailout(const char* reason) { |
| flow_graph_builder_->InlineBailout(reason); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::DebugStepCheck(TokenPosition position) { |
| return flow_graph_builder_->DebugStepCheck(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::LoadLocal(LocalVariable* variable) { |
| return flow_graph_builder_->LoadLocal(variable); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::Return(TokenPosition position) { |
| return flow_graph_builder_->Return(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::PushArgument() { |
| return flow_graph_builder_->PushArgument(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::EvaluateAssertion() { |
| return flow_graph_builder_->EvaluateAssertion(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::RethrowException(TokenPosition position, |
| int catch_try_index) { |
| return flow_graph_builder_->RethrowException(position, catch_try_index); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::ThrowNoSuchMethodError() { |
| return flow_graph_builder_->ThrowNoSuchMethodError(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::Constant(const Object& value) { |
| return flow_graph_builder_->Constant(value); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::IntConstant(int64_t value) { |
| return flow_graph_builder_->IntConstant(value); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::LoadStaticField() { |
| return flow_graph_builder_->LoadStaticField(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckNull(TokenPosition position, |
| LocalVariable* receiver, |
| const String& function_name) { |
| return flow_graph_builder_->CheckNull(position, receiver, function_name); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StaticCall(TokenPosition position, |
| const Function& target, |
| intptr_t argument_count, |
| ICData::RebindRule rebind_rule) { |
| return flow_graph_builder_->StaticCall(position, target, argument_count, |
| rebind_rule); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StaticCall( |
| TokenPosition position, |
| const Function& target, |
| intptr_t argument_count, |
| const Array& argument_names, |
| ICData::RebindRule rebind_rule, |
| const InferredTypeMetadata* result_type, |
| intptr_t type_args_count) { |
| return flow_graph_builder_->StaticCall(position, target, argument_count, |
| argument_names, rebind_rule, |
| result_type, type_args_count); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::InstanceCall( |
| TokenPosition position, |
| const String& name, |
| Token::Kind kind, |
| intptr_t argument_count, |
| intptr_t checked_argument_count) { |
| const intptr_t kTypeArgsLen = 0; |
| return flow_graph_builder_->InstanceCall( |
| position, name, kind, kTypeArgsLen, argument_count, Array::null_array(), |
| checked_argument_count, Function::null_function()); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::InstanceCall( |
| TokenPosition position, |
| const String& name, |
| Token::Kind kind, |
| intptr_t type_args_len, |
| intptr_t argument_count, |
| const Array& argument_names, |
| intptr_t checked_argument_count, |
| const Function& interface_target, |
| const InferredTypeMetadata* result_type) { |
| return flow_graph_builder_->InstanceCall( |
| position, name, kind, type_args_len, argument_count, argument_names, |
| checked_argument_count, interface_target, result_type); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::ThrowException(TokenPosition position) { |
| return flow_graph_builder_->ThrowException(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BooleanNegate() { |
| return flow_graph_builder_->BooleanNegate(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::TranslateInstantiatedTypeArguments( |
| const TypeArguments& type_arguments) { |
| return flow_graph_builder_->TranslateInstantiatedTypeArguments( |
| type_arguments); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StrictCompare(Token::Kind kind, |
| bool number_check) { |
| return flow_graph_builder_->StrictCompare(kind, number_check); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::AllocateObject(TokenPosition position, |
| const Class& klass, |
| intptr_t argument_count) { |
| return flow_graph_builder_->AllocateObject(position, klass, argument_count); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::AllocateObject( |
| const Class& klass, |
| const Function& closure_function) { |
| return flow_graph_builder_->AllocateObject(klass, closure_function); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::AllocateContext(intptr_t size) { |
| return flow_graph_builder_->AllocateContext(size); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::LoadField(intptr_t offset) { |
| return flow_graph_builder_->LoadField(offset); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StoreLocal(TokenPosition position, |
| LocalVariable* variable) { |
| return flow_graph_builder_->StoreLocal(position, variable); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StoreStaticField(TokenPosition position, |
| const Field& field) { |
| return flow_graph_builder_->StoreStaticField(position, field); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StoreInstanceField(TokenPosition position, |
| intptr_t offset) { |
| return flow_graph_builder_->StoreInstanceField(position, offset); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StringInterpolate(TokenPosition position) { |
| return flow_graph_builder_->StringInterpolate(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StringInterpolateSingle( |
| TokenPosition position) { |
| return flow_graph_builder_->StringInterpolateSingle(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::ThrowTypeError() { |
| return flow_graph_builder_->ThrowTypeError(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::LoadInstantiatorTypeArguments() { |
| return flow_graph_builder_->LoadInstantiatorTypeArguments(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::LoadFunctionTypeArguments() { |
| return flow_graph_builder_->LoadFunctionTypeArguments(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::InstantiateType(const AbstractType& type) { |
| return flow_graph_builder_->InstantiateType(type); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CreateArray() { |
| return flow_graph_builder_->CreateArray(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::StoreIndexed(intptr_t class_id) { |
| return flow_graph_builder_->StoreIndexed(class_id); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckStackOverflow(TokenPosition position) { |
| return flow_graph_builder_->CheckStackOverflow(position); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CloneContext( |
| intptr_t num_context_variables) { |
| return flow_graph_builder_->CloneContext(num_context_variables); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::TranslateFinallyFinalizers( |
| TryFinallyBlock* outer_finally, |
| intptr_t target_context_depth) { |
| // TranslateFinallyFinalizers can move the readers offset. |
| // Save the current position and restore it afterwards. |
| AlternativeReadingScope alt(&reader_); |
| return flow_graph_builder_->TranslateFinallyFinalizers(outer_finally, |
| target_context_depth); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BranchIfTrue( |
| TargetEntryInstr** then_entry, |
| TargetEntryInstr** otherwise_entry, |
| bool negate) { |
| return flow_graph_builder_->BranchIfTrue(then_entry, otherwise_entry, negate); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BranchIfEqual( |
| TargetEntryInstr** then_entry, |
| TargetEntryInstr** otherwise_entry, |
| bool negate) { |
| return flow_graph_builder_->BranchIfEqual(then_entry, otherwise_entry, |
| negate); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BranchIfNull( |
| TargetEntryInstr** then_entry, |
| TargetEntryInstr** otherwise_entry, |
| bool negate) { |
| return flow_graph_builder_->BranchIfNull(then_entry, otherwise_entry, negate); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CatchBlockEntry(const Array& handler_types, |
| intptr_t handler_index, |
| bool needs_stacktrace, |
| bool is_synthesized) { |
| return flow_graph_builder_->CatchBlockEntry(handler_types, handler_index, |
| needs_stacktrace, is_synthesized); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::TryCatch(int try_handler_index) { |
| return flow_graph_builder_->TryCatch(try_handler_index); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::Drop() { |
| return flow_graph_builder_->Drop(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::DropTempsPreserveTop( |
| intptr_t num_temps_to_drop) { |
| return flow_graph_builder_->DropTempsPreserveTop(num_temps_to_drop); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::MakeTemp() { |
| return flow_graph_builder_->MakeTemp(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::NullConstant() { |
| return flow_graph_builder_->NullConstant(); |
| } |
| |
| JoinEntryInstr* StreamingFlowGraphBuilder::BuildJoinEntry() { |
| return flow_graph_builder_->BuildJoinEntry(); |
| } |
| |
| JoinEntryInstr* StreamingFlowGraphBuilder::BuildJoinEntry(intptr_t try_index) { |
| return flow_graph_builder_->BuildJoinEntry(try_index); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::Goto(JoinEntryInstr* destination) { |
| return flow_graph_builder_->Goto(destination); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildImplicitClosureCreation( |
| const Function& target) { |
| return flow_graph_builder_->BuildImplicitClosureCreation(target); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckBoolean() { |
| return flow_graph_builder_->CheckBoolean(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckAssignableInCheckedMode( |
| const AbstractType& dst_type, |
| const String& dst_name) { |
| if (I->type_checks()) { |
| return flow_graph_builder_->CheckAssignable(dst_type, dst_name); |
| } |
| return Fragment(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckArgumentType( |
| LocalVariable* variable, |
| const AbstractType& type) { |
| return flow_graph_builder_->CheckAssignable( |
| type, variable->name(), AssertAssignableInstr::kParameterCheck); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckTypeArgumentBound( |
| const AbstractType& parameter, |
| const AbstractType& bound, |
| const String& dst_name) { |
| return flow_graph_builder_->AssertSubtype(TokenPosition::kNoSource, parameter, |
| bound, dst_name); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckVariableTypeInCheckedMode( |
| intptr_t variable_kernel_position) { |
| if (I->type_checks()) { |
| LocalVariable* variable = LookupVariable(variable_kernel_position); |
| return flow_graph_builder_->CheckVariableTypeInCheckedMode( |
| variable->type(), variable->name()); |
| } |
| return Fragment(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::CheckVariableTypeInCheckedMode( |
| const AbstractType& dst_type, |
| const String& name_symbol) { |
| return flow_graph_builder_->CheckVariableTypeInCheckedMode(dst_type, |
| name_symbol); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::EnterScope( |
| intptr_t kernel_offset, |
| intptr_t* num_context_variables) { |
| return flow_graph_builder_->EnterScope(kernel_offset, num_context_variables); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::ExitScope(intptr_t kernel_offset) { |
| return flow_graph_builder_->ExitScope(kernel_offset); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::TranslateCondition(bool* negate) { |
| *negate = PeekTag() == kNot; |
| if (*negate) { |
| SkipBytes(1); // Skip Not tag, thus go directly to the inner expression. |
| } |
| Fragment instructions = BuildExpression(); // read expression. |
| instructions += CheckBoolean(); |
| return instructions; |
| } |
| |
| const TypeArguments& StreamingFlowGraphBuilder::BuildTypeArguments() { |
| ReadUInt(); // read arguments count. |
| intptr_t type_count = ReadListLength(); // read type count. |
| return T.BuildTypeArguments(type_count); // read types. |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildArguments(Array* argument_names, |
| intptr_t* argument_count, |
| intptr_t* positional_count, |
| bool skip_push_arguments, |
| bool do_drop) { |
| intptr_t dummy; |
| if (argument_count == NULL) argument_count = &dummy; |
| *argument_count = ReadUInt(); // read arguments count. |
| |
| // List of types. |
| SkipListOfDartTypes(); // read list of types. |
| |
| { |
| AlternativeReadingScope _(&reader_); |
| if (positional_count == NULL) positional_count = &dummy; |
| *positional_count = ReadListLength(); // read length of expression list |
| } |
| return BuildArgumentsFromActualArguments(argument_names, skip_push_arguments, |
| do_drop); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildArgumentsFromActualArguments( |
| Array* argument_names, |
| bool skip_push_arguments, |
| bool do_drop) { |
| Fragment instructions; |
| |
| // List of positional. |
| intptr_t list_length = ReadListLength(); // read list length. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| instructions += BuildExpression(); // read ith expression. |
| if (!skip_push_arguments) instructions += PushArgument(); |
| if (do_drop) instructions += Drop(); |
| } |
| |
| // List of named. |
| list_length = ReadListLength(); // read list length. |
| if (argument_names != NULL && list_length > 0) { |
| *argument_names ^= Array::New(list_length, Heap::kOld); |
| } |
| for (intptr_t i = 0; i < list_length; ++i) { |
| String& name = |
| H.DartSymbolObfuscate(ReadStringReference()); // read ith name index. |
| instructions += BuildExpression(); // read ith expression. |
| if (!skip_push_arguments) instructions += PushArgument(); |
| if (do_drop) instructions += Drop(); |
| if (argument_names != NULL) { |
| argument_names->SetAt(i, name); |
| } |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildInvalidExpression( |
| TokenPosition* position) { |
| // The frontend will take care of emitting normal errors (like |
| // [NoSuchMethodError]s) and only emit [InvalidExpression]s in very special |
| // situations (e.g. an invalid annotation). |
| TokenPosition pos = ReadPosition(); |
| if (position != NULL) *position = pos; |
| const String& message = H.DartString(ReadStringReference()); |
| H.ReportError(script(), pos, "%s", message.ToCString()); |
| return Fragment(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableGet(TokenPosition* position) { |
| (position != NULL) ? * position = ReadPosition() : ReadPosition(); |
| intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
| ReadUInt(); // read relative variable index. |
| SkipOptionalDartType(); // read promoted type. |
| return LoadLocal(LookupVariable(variable_kernel_position)); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableGet(uint8_t payload, |
| TokenPosition* position) { |
| (position != NULL) ? * position = ReadPosition() : ReadPosition(); |
| intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
| return LoadLocal(LookupVariable(variable_kernel_position)); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableSet(TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
| ReadUInt(); // read relative variable index. |
| Fragment instructions = BuildExpression(); // read expression. |
| |
| if (NeedsDebugStepCheck(stack(), position)) { |
| instructions = DebugStepCheck(position) + instructions; |
| } |
| instructions += CheckVariableTypeInCheckedMode(variable_kernel_position); |
| instructions += |
| StoreLocal(position, LookupVariable(variable_kernel_position)); |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableSet(uint8_t payload, |
| TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
| Fragment instructions = BuildExpression(); // read expression. |
| |
| if (NeedsDebugStepCheck(stack(), position)) { |
| instructions = DebugStepCheck(position) + instructions; |
| } |
| instructions += CheckVariableTypeInCheckedMode(variable_kernel_position); |
| instructions += |
| StoreLocal(position, LookupVariable(variable_kernel_position)); |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildPropertyGet(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const DirectCallMetadata direct_call = |
| direct_call_metadata_helper_.GetDirectTargetForPropertyGet(offset); |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| Fragment instructions = BuildExpression(); // read receiver. |
| |
| LocalVariable* receiver = NULL; |
| if (direct_call.check_receiver_for_null_) { |
| // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
| receiver = MakeTemporary(); |
| instructions += LoadLocal(receiver); |
| } |
| |
| instructions += PushArgument(); |
| |
| const String& getter_name = ReadNameAsGetterName(); // read name. |
| |
| const Function* interface_target = &Function::null_function(); |
| const NameIndex itarget_name = |
| ReadCanonicalNameReference(); // read interface_target_reference. |
| if (I->strong() && !H.IsRoot(itarget_name) && |
| (H.IsGetter(itarget_name) || H.IsField(itarget_name))) { |
| interface_target = &Function::ZoneHandle( |
| Z, LookupMethodByMember(itarget_name, H.DartGetterName(itarget_name))); |
| ASSERT(getter_name.raw() == interface_target->name()); |
| } |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += CheckNull(position, receiver, getter_name); |
| } |
| |
| if (!direct_call.target_.IsNull()) { |
| ASSERT(FLAG_precompiled_mode); |
| instructions += |
| StaticCall(position, direct_call.target_, 1, Array::null_array(), |
| ICData::kNoRebind, &result_type); |
| } else { |
| const intptr_t kTypeArgsLen = 0; |
| const intptr_t kNumArgsChecked = 1; |
| instructions += InstanceCall( |
| position, getter_name, Token::kGET, kTypeArgsLen, 1, |
| Array::null_array(), kNumArgsChecked, *interface_target, &result_type); |
| } |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += DropTempsPreserveTop(1); // Drop receiver, preserve result. |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildPropertySet(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| |
| const DirectCallMetadata direct_call = |
| direct_call_metadata_helper_.GetDirectTargetForPropertySet(offset); |
| |
| Fragment instructions(MakeTemp()); |
| LocalVariable* variable = MakeTemporary(); |
| |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| instructions += BuildExpression(); // read receiver. |
| |
| LocalVariable* receiver = NULL; |
| if (direct_call.check_receiver_for_null_) { |
| // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
| receiver = MakeTemporary(); |
| instructions += LoadLocal(receiver); |
| } |
| |
| instructions += PushArgument(); |
| |
| const String& setter_name = ReadNameAsSetterName(); // read name. |
| |
| instructions += BuildExpression(); // read value. |
| instructions += StoreLocal(TokenPosition::kNoSource, variable); |
| instructions += PushArgument(); |
| |
| const Function* interface_target = &Function::null_function(); |
| const NameIndex itarget_name = |
| ReadCanonicalNameReference(); // read interface_target_reference. |
| if (I->strong() && !H.IsRoot(itarget_name)) { |
| interface_target = &Function::ZoneHandle( |
| Z, LookupMethodByMember(itarget_name, H.DartSetterName(itarget_name))); |
| ASSERT(setter_name.raw() == interface_target->name()); |
| } |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += CheckNull(position, receiver, setter_name); |
| } |
| |
| if (!direct_call.target_.IsNull()) { |
| ASSERT(FLAG_precompiled_mode); |
| instructions += |
| StaticCall(position, direct_call.target_, 2, Array::null_array(), |
| ICData::kNoRebind, /* result_type = */ NULL); |
| } else { |
| const intptr_t kTypeArgsLen = 0; |
| const intptr_t kNumArgsChecked = 1; |
| instructions += |
| InstanceCall(position, setter_name, Token::kSET, kTypeArgsLen, 2, |
| Array::null_array(), kNumArgsChecked, *interface_target, |
| /* result_type = */ NULL); |
| } |
| |
| instructions += Drop(); // Drop result of the setter invocation. |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += Drop(); // Drop receiver. |
| } |
| |
| return instructions; |
| } |
| |
| static Function& GetNoSuchMethodOrDie(Zone* zone, const Class& klass) { |
| Function& nsm_function = Function::Handle(zone); |
| Class& iterate_klass = Class::Handle(zone, klass.raw()); |
| while (!iterate_klass.IsNull()) { |
| nsm_function = iterate_klass.LookupDynamicFunction(Symbols::NoSuchMethod()); |
| if (!nsm_function.IsNull() && nsm_function.NumParameters() == 2 && |
| nsm_function.NumTypeParameters() == 0) { |
| break; |
| } |
| iterate_klass = iterate_klass.SuperClass(); |
| } |
| // We are guaranteed to find noSuchMethod of class Object. |
| ASSERT(!nsm_function.IsNull()); |
| |
| return nsm_function; |
| } |
| |
| // Note, that this will always mark `super` flag to true. |
| Fragment StreamingFlowGraphBuilder::BuildAllocateInvocationMirrorCall( |
| TokenPosition position, |
| const String& name, |
| intptr_t num_type_arguments, |
| intptr_t num_arguments, |
| const Array& argument_names, |
| LocalVariable* actuals_array, |
| Fragment build_rest_of_actuals) { |
| Fragment instructions; |
| |
| // Populate array containing the actual arguments. Just add [this] here. |
| instructions += LoadLocal(actuals_array); // array |
| instructions += IntConstant(num_type_arguments == 0 ? 0 : 1); // index |
| instructions += LoadLocal(scopes()->this_variable); // receiver |
| instructions += StoreIndexed(kArrayCid); |
| instructions += Drop(); // dispose of stored value |
| instructions += build_rest_of_actuals; |
| |
| // First argument is receiver. |
| instructions += LoadLocal(scopes()->this_variable); |
| instructions += PushArgument(); |
| |
| // Push the arguments for allocating the invocation mirror: |
| // - the name. |
| instructions += Constant(String::ZoneHandle(Z, name.raw())); |
| instructions += PushArgument(); |
| |
| // - the arguments descriptor. |
| const Array& args_descriptor = |
| Array::Handle(Z, ArgumentsDescriptor::New(num_type_arguments, |
| num_arguments, argument_names)); |
| instructions += Constant(Array::ZoneHandle(Z, args_descriptor.raw())); |
| instructions += PushArgument(); |
| |
| // - an array containing the actual arguments. |
| instructions += LoadLocal(actuals_array); |
| instructions += PushArgument(); |
| |
| // - [true] indicating this is a `super` NoSuchMethod. |
| instructions += Constant(Bool::True()); |
| instructions += PushArgument(); |
| |
| const Class& mirror_class = |
| Class::Handle(Z, Library::LookupCoreClass(Symbols::InvocationMirror())); |
| ASSERT(!mirror_class.IsNull()); |
| const Function& allocation_function = Function::ZoneHandle( |
| Z, mirror_class.LookupStaticFunction( |
| Library::PrivateCoreLibName(Symbols::AllocateInvocationMirror()))); |
| ASSERT(!allocation_function.IsNull()); |
| instructions += StaticCall(position, allocation_function, |
| /* argument_count = */ 4, ICData::kStatic); |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildSuperPropertyGet(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| Class& klass = GetSuperOrDie(); |
| |
| StringIndex name_index = ReadStringReference(); // read name index. |
| NameIndex library_reference = |
| ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') |
| ? ReadCanonicalNameReference() // read library index. |
| : NameIndex(); |
| const String& getter_name = H.DartGetterName(library_reference, name_index); |
| const String& method_name = H.DartMethodName(library_reference, name_index); |
| |
| SkipCanonicalNameReference(); // skip target_reference. |
| |
| // Search the superclass chain for the selector looking for either getter or |
| // method. |
| Function& function = Function::Handle(Z); |
| while (!klass.IsNull()) { |
| function = klass.LookupDynamicFunction(method_name); |
| if (!function.IsNull()) { |
| Function& target = |
| Function::ZoneHandle(Z, function.ImplicitClosureFunction()); |
| ASSERT(!target.IsNull()); |
| // Generate inline code for allocation closure object with context |
| // which captures `this`. |
| return BuildImplicitClosureCreation(target); |
| } |
| function = klass.LookupDynamicFunction(getter_name); |
| if (!function.IsNull()) break; |
| klass = klass.SuperClass(); |
| } |
| |
| Fragment instructions; |
| if (klass.IsNull()) { |
| instructions += |
| Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null())); |
| instructions += IntConstant(1); // array size |
| instructions += CreateArray(); |
| LocalVariable* actuals_array = MakeTemporary(); |
| |
| Class& parent_klass = GetSuperOrDie(); |
| |
| instructions += BuildAllocateInvocationMirrorCall( |
| position, getter_name, |
| /* num_type_arguments = */ 0, |
| /* num_arguments = */ 1, |
| /* argument_names = */ Object::empty_array(), actuals_array, |
| /* build_rest_of_actuals = */ Fragment()); |
| instructions += PushArgument(); // second argument is invocation mirror |
| |
| Function& nsm_function = GetNoSuchMethodOrDie(Z, parent_klass); |
| instructions += |
| StaticCall(position, Function::ZoneHandle(Z, nsm_function.raw()), |
| /* argument_count = */ 2, ICData::kNSMDispatch); |
| instructions += DropTempsPreserveTop(1); // Drop array |
| } else { |
| ASSERT(!klass.IsNull()); |
| ASSERT(!function.IsNull()); |
| |
| instructions += LoadLocal(scopes()->this_variable); |
| instructions += PushArgument(); |
| |
| instructions += |
| StaticCall(position, Function::ZoneHandle(Z, function.raw()), |
| /* argument_count = */ 1, Array::null_array(), |
| ICData::kSuper, &result_type); |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildSuperPropertySet(TokenPosition* p) { |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| Class& klass = GetSuperOrDie(); |
| |
| const String& setter_name = ReadNameAsSetterName(); // read name. |
| |
| Function& function = FindMatchingFunctionAnyArgs(klass, setter_name); |
| |
| Fragment instructions(MakeTemp()); |
| LocalVariable* value = MakeTemporary(); // this holds RHS value |
| |
| if (function.IsNull()) { |
| instructions += |
| Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null())); |
| instructions += IntConstant(2); // array size |
| instructions += CreateArray(); |
| LocalVariable* actuals_array = MakeTemporary(); |
| |
| Fragment build_rest_of_actuals; |
| build_rest_of_actuals += LoadLocal(actuals_array); // array |
| build_rest_of_actuals += IntConstant(1); // index |
| build_rest_of_actuals += BuildExpression(); // value. |
| build_rest_of_actuals += StoreLocal(position, value); |
| build_rest_of_actuals += StoreIndexed(kArrayCid); |
| build_rest_of_actuals += Drop(); // dispose of stored value |
| |
| instructions += BuildAllocateInvocationMirrorCall( |
| position, setter_name, /* num_type_arguments = */ 0, |
| /* num_arguments = */ 2, |
| /* argument_names = */ Object::empty_array(), actuals_array, |
| build_rest_of_actuals); |
| instructions += PushArgument(); // second argument - invocation mirror |
| |
| SkipCanonicalNameReference(); // skip target_reference. |
| |
| Function& nsm_function = GetNoSuchMethodOrDie(Z, klass); |
| instructions += |
| StaticCall(position, Function::ZoneHandle(Z, nsm_function.raw()), |
| /* argument_count = */ 2, ICData::kNSMDispatch); |
| instructions += Drop(); // Drop result of NoSuchMethod invocation |
| instructions += Drop(); // Drop array |
| } else { |
| // receiver |
| instructions += LoadLocal(scopes()->this_variable); |
| instructions += PushArgument(); |
| |
| instructions += BuildExpression(); // read value. |
| instructions += StoreLocal(position, value); |
| instructions += PushArgument(); |
| |
| SkipCanonicalNameReference(); // skip target_reference. |
| |
| instructions += |
| StaticCall(position, Function::ZoneHandle(Z, function.raw()), |
| /* argument_count = */ 2, ICData::kSuper); |
| instructions += Drop(); // Drop result of the setter invocation. |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildDirectPropertyGet(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| const Tag receiver_tag = PeekTag(); // peek tag for receiver. |
| Fragment instructions = BuildExpression(); // read receiver. |
| const NameIndex kernel_name = |
| ReadCanonicalNameReference(); // read target_reference. |
| |
| Function& target = Function::ZoneHandle(Z); |
| if (H.IsProcedure(kernel_name)) { |
| if (H.IsGetter(kernel_name)) { |
| target = LookupMethodByMember(kernel_name, H.DartGetterName(kernel_name)); |
| } else if (receiver_tag == kThisExpression) { |
| // Undo stack change for the BuildExpression. |
| Pop(); |
| |
| target = LookupMethodByMember(kernel_name, H.DartMethodName(kernel_name)); |
| target = target.ImplicitClosureFunction(); |
| ASSERT(!target.IsNull()); |
| |
| // Generate inline code for allocating closure object with context which |
| // captures `this`. |
| return BuildImplicitClosureCreation(target); |
| } else { |
| // Need to create implicit closure (tear-off), receiver != this. |
| // Ensure method extractor exists and call it directly. |
| const Function& target_method = Function::ZoneHandle( |
| Z, LookupMethodByMember(kernel_name, H.DartMethodName(kernel_name))); |
| const String& getter_name = H.DartGetterName(kernel_name); |
| target = target_method.GetMethodExtractor(getter_name); |
| } |
| } else { |
| ASSERT(H.IsField(kernel_name)); |
| const String& getter_name = H.DartGetterName(kernel_name); |
| target = LookupMethodByMember(kernel_name, getter_name); |
| ASSERT(target.IsGetterFunction() || target.IsImplicitGetterFunction()); |
| } |
| |
| instructions += PushArgument(); |
| // Static calls are marked as "no-rebind", which is currently safe because |
| // DirectPropertyGet are only used in enums (index in toString) and enums |
| // can't change their structure during hot reload. |
| // If there are other sources of DirectPropertyGet in the future, this code |
| // have to be adjusted. |
| return instructions + StaticCall(position, target, 1, Array::null_array(), |
| ICData::kNoRebind, &result_type); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildDirectPropertySet(TokenPosition* p) { |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| Fragment instructions(MakeTemp()); |
| LocalVariable* value = MakeTemporary(); |
| |
| instructions += BuildExpression(); // read receiver. |
| instructions += PushArgument(); |
| |
| const NameIndex target_reference = |
| ReadCanonicalNameReference(); // read target_reference. |
| const String& method_name = H.DartSetterName(target_reference); |
| const Function& target = Function::ZoneHandle( |
| Z, LookupMethodByMember(target_reference, method_name)); |
| ASSERT(target.IsSetterFunction() || target.IsImplicitSetterFunction()); |
| |
| instructions += BuildExpression(); // read value. |
| instructions += StoreLocal(TokenPosition::kNoSource, value); |
| instructions += PushArgument(); |
| |
| // Static calls are marked as "no-rebind", which is currently safe because |
| // DirectPropertyGet are only used in enums (index in toString) and enums |
| // can't change their structure during hot reload. |
| // If there are other sources of DirectPropertyGet in the future, this code |
| // have to be adjusted. |
| instructions += |
| StaticCall(position, target, 2, Array::null_array(), ICData::kNoRebind, |
| /* result_type = */ NULL); |
| |
| return instructions + Drop(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStaticGet(TokenPosition* p) { |
| ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| NameIndex target = ReadCanonicalNameReference(); // read target_reference. |
| |
| if (H.IsField(target)) { |
| const Field& field = |
| Field::ZoneHandle(Z, H.LookupFieldByKernelField(target)); |
| if (field.is_const()) { |
| return Constant(Instance::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpression(offset))); |
| } else { |
| const Class& owner = Class::Handle(Z, field.Owner()); |
| const String& getter_name = H.DartGetterName(target); |
| const Function& getter = |
| Function::ZoneHandle(Z, owner.LookupStaticFunction(getter_name)); |
| if (getter.IsNull() || !field.has_initializer()) { |
| Fragment instructions = Constant(field); |
| return instructions + LoadStaticField(); |
| } else { |
| return StaticCall(position, getter, 0, Array::null_array(), |
| ICData::kStatic, &result_type); |
| } |
| } |
| } else { |
| const Function& function = |
| Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target)); |
| |
| if (H.IsGetter(target)) { |
| return StaticCall(position, function, 0, Array::null_array(), |
| ICData::kStatic, &result_type); |
| } else if (H.IsMethod(target)) { |
| return Constant(Instance::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpression(offset))); |
| } else { |
| UNIMPLEMENTED(); |
| } |
| } |
| |
| return Fragment(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStaticSet(TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| NameIndex target = ReadCanonicalNameReference(); // read target_reference. |
| |
| if (H.IsField(target)) { |
| const Field& field = |
| Field::ZoneHandle(Z, H.LookupFieldByKernelField(target)); |
| const AbstractType& dst_type = AbstractType::ZoneHandle(Z, field.type()); |
| Fragment instructions = BuildExpression(); // read expression. |
| if (NeedsDebugStepCheck(stack(), position)) { |
| instructions = DebugStepCheck(position) + instructions; |
| } |
| instructions += CheckAssignableInCheckedMode( |
| dst_type, String::ZoneHandle(Z, field.name())); |
| LocalVariable* variable = MakeTemporary(); |
| instructions += LoadLocal(variable); |
| return instructions + StoreStaticField(position, field); |
| } else { |
| ASSERT(H.IsProcedure(target)); |
| |
| // Evaluate the expression on the right hand side. |
| Fragment instructions = BuildExpression(); // read expression. |
| LocalVariable* variable = MakeTemporary(); |
| |
| // Prepare argument. |
| instructions += LoadLocal(variable); |
| instructions += PushArgument(); |
| |
| // Invoke the setter function. |
| const Function& function = |
| Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target)); |
| instructions += StaticCall(position, function, 1, ICData::kStatic); |
| |
| // Drop the unused result & leave the stored value on the stack. |
| return instructions + Drop(); |
| } |
| } |
| |
| static bool IsNumberLiteral(Tag tag) { |
| return tag == kNegativeIntLiteral || tag == kPositiveIntLiteral || |
| tag == kSpecializedIntLiteral || tag == kDoubleLiteral; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const DirectCallMetadata direct_call = |
| direct_call_metadata_helper_.GetDirectTargetForMethodInvocation(offset); |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| const Tag receiver_tag = PeekTag(); // peek tag for receiver. |
| if (IsNumberLiteral(receiver_tag) && |
| (!optimizing() || constant_evaluator_.IsCached(offset))) { |
| const intptr_t before_branch_offset = ReaderOffset(); |
| |
| SkipExpression(); // read receiver (it's just a number literal). |
| |
| const String& name = ReadNameAsMethodName(); // read name. |
| const Token::Kind token_kind = |
| MethodTokenRecognizer::RecognizeTokenKind(name); |
| intptr_t argument_count = PeekArgumentsCount() + 1; |
| |
| if ((argument_count == 1) && (token_kind == Token::kNEGATE)) { |
| const Object& result = Object::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpressionSafe(offset)); |
| if (!result.IsError()) { |
| SkipArguments(); // read arguments. |
| SkipCanonicalNameReference(); // read interface_target_reference. |
| return Constant(result); |
| } |
| } else if ((argument_count == 2) && |
| Token::IsBinaryArithmeticOperator(token_kind) && |
| IsNumberLiteral(PeekArgumentsFirstPositionalTag())) { |
| const Object& result = Object::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpressionSafe(offset)); |
| if (!result.IsError()) { |
| SkipArguments(); // read arguments. |
| SkipCanonicalNameReference(); // read interface_target_reference. |
| return Constant(result); |
| } |
| } |
| |
| SetOffset(before_branch_offset); |
| } |
| |
| Fragment instructions; |
| |
| intptr_t type_args_len = 0; |
| LocalVariable* type_arguments_temp = NULL; |
| if (I->reify_generic_functions()) { |
| AlternativeReadingScope alt(&reader_); |
| SkipExpression(); // skip receiver |
| SkipName(); // skip method name |
| ReadUInt(); // read argument count. |
| intptr_t list_length = ReadListLength(); // read types list length. |
| if (list_length > 0) { |
| const TypeArguments& type_arguments = |
| T.BuildTypeArguments(list_length); // read types. |
| instructions += TranslateInstantiatedTypeArguments(type_arguments); |
| if (direct_call.check_receiver_for_null_) { |
| // Don't yet push type arguments if we need to check receiver for null. |
| // In this case receiver will be duplicated so instead of pushing |
| // type arguments here we need to push it between receiver_temp |
| // and actual receiver. See the code below. |
| type_arguments_temp = MakeTemporary(); |
| } else { |
| instructions += PushArgument(); |
| } |
| } |
| type_args_len = list_length; |
| } |
| |
| instructions += BuildExpression(); // read receiver. |
| |
| const String& name = ReadNameAsMethodName(); // read name. |
| const Token::Kind token_kind = |
| MethodTokenRecognizer::RecognizeTokenKind(name); |
| |
| // Detect comparison with null. |
| if ((token_kind == Token::kEQ || token_kind == Token::kNE) && |
| PeekArgumentsCount() == 1 && |
| (receiver_tag == kNullLiteral || |
| PeekArgumentsFirstPositionalTag() == kNullLiteral)) { |
| ASSERT(type_args_len == 0); |
| // "==" or "!=" with null on either side. |
| instructions += BuildArguments(NULL /* named */, NULL /* arg count */, |
| NULL /* positional arg count */, |
| true); // read arguments. |
| SkipCanonicalNameReference(); // read interface_target_reference. |
| Token::Kind strict_cmp_kind = |
| token_kind == Token::kEQ ? Token::kEQ_STRICT : Token::kNE_STRICT; |
| return instructions + |
| StrictCompare(strict_cmp_kind, /*number_check = */ true); |
| } |
| |
| LocalVariable* receiver_temp = NULL; |
| if (direct_call.check_receiver_for_null_) { |
| // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
| receiver_temp = MakeTemporary(); |
| if (type_arguments_temp != NULL) { |
| // If call has type arguments then push them before pushing the receiver. |
| // The stack will contain: |
| // |
| // [type_arguments_temp][receiver_temp][type_arguments][receiver] ... |
| // |
| instructions += LoadLocal(type_arguments_temp); |
| instructions += PushArgument(); |
| } |
| instructions += LoadLocal(receiver_temp); |
| } |
| |
| instructions += PushArgument(); // push receiver as argument. |
| |
| intptr_t argument_count; |
| intptr_t positional_argument_count; |
| Array& argument_names = Array::ZoneHandle(Z); |
| instructions += |
| BuildArguments(&argument_names, &argument_count, |
| &positional_argument_count); // read arguments. |
| ++argument_count; // include receiver |
| |
| intptr_t checked_argument_count = 1; |
| // If we have a special operation (e.g. +/-/==) we mark both arguments as |
| // to be checked. |
| if (token_kind != Token::kILLEGAL) { |
| ASSERT(argument_count <= 2); |
| checked_argument_count = argument_count; |
| } |
| |
| const Function* interface_target = &Function::null_function(); |
| const NameIndex itarget_name = |
| ReadCanonicalNameReference(); // read interface_target_reference. |
| if (I->strong() && !H.IsRoot(itarget_name) && !H.IsField(itarget_name)) { |
| interface_target = &Function::ZoneHandle( |
| Z, |
| LookupMethodByMember(itarget_name, H.DartProcedureName(itarget_name))); |
| ASSERT((name.raw() == interface_target->name()) || |
| (interface_target->IsGetterFunction() && |
| Field::GetterSymbol(name) == interface_target->name())); |
| } |
| |
| if (direct_call.check_receiver_for_null_) { |
| instructions += CheckNull(position, receiver_temp, name); |
| } |
| |
| if (!direct_call.target_.IsNull()) { |
| ASSERT(FLAG_precompiled_mode); |
| instructions += StaticCall(position, direct_call.target_, argument_count, |
| argument_names, ICData::kNoRebind, &result_type, |
| type_args_len); |
| } else { |
| instructions += |
| InstanceCall(position, name, token_kind, type_args_len, argument_count, |
| argument_names, checked_argument_count, *interface_target, |
| &result_type); |
| } |
| |
| // Drop temporaries preserving result on the top of the stack. |
| ASSERT((receiver_temp != NULL) || (type_arguments_temp == NULL)); |
| if (receiver_temp != NULL) { |
| const intptr_t num_temps = |
| (receiver_temp != NULL ? 1 : 0) + (type_arguments_temp != NULL ? 1 : 0); |
| instructions += DropTempsPreserveTop(num_temps); |
| } |
| |
| // Later optimization passes assume that result of a x.[]=(...) call is not |
| // used. We must guarantee this invariant because violation will lead to an |
| // illegal IL once we replace x.[]=(...) with a sequence that does not |
| // actually produce any value. See http://dartbug.com/29135 for more details. |
| if (name.raw() == Symbols::AssignIndexToken().raw()) { |
| instructions += Drop(); |
| instructions += NullConstant(); |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildDirectMethodInvocation( |
| TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| TokenPosition position = ReadPosition(); // read offset. |
| if (p != NULL) *p = position; |
| |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| Tag receiver_tag = PeekTag(); // peek tag for receiver. |
| |
| Fragment instructions; |
| intptr_t type_args_len = 0; |
| if (I->reify_generic_functions()) { |
| AlternativeReadingScope alt(&reader_); |
| SkipExpression(); // skip receiver |
| ReadCanonicalNameReference(); // skip target reference |
| ReadUInt(); // read argument count. |
| intptr_t list_length = ReadListLength(); // read types list length. |
| if (list_length > 0) { |
| const TypeArguments& type_arguments = |
| T.BuildTypeArguments(list_length); // read types. |
| instructions += TranslateInstantiatedTypeArguments(type_arguments); |
| instructions += PushArgument(); |
| } |
| type_args_len = list_length; |
| } |
| |
| instructions += BuildExpression(); // read receiver. |
| |
| NameIndex kernel_name = |
| ReadCanonicalNameReference(); // read target_reference. |
| const String& method_name = H.DartProcedureName(kernel_name); |
| const Token::Kind token_kind = |
| MethodTokenRecognizer::RecognizeTokenKind(method_name); |
| |
| // Detect comparison with null. |
| if ((token_kind == Token::kEQ || token_kind == Token::kNE) && |
| PeekArgumentsCount() == 1 && |
| (receiver_tag == kNullLiteral || |
| PeekArgumentsFirstPositionalTag() == kNullLiteral)) { |
| ASSERT(type_args_len == 0); |
| // "==" or "!=" with null on either side. |
| instructions += BuildArguments(NULL /* names */, NULL /* arg count */, |
| NULL /* positional arg count */, |
| true); // read arguments. |
| Token::Kind strict_cmp_kind = |
| token_kind == Token::kEQ ? Token::kEQ_STRICT : Token::kNE_STRICT; |
| return instructions + |
| StrictCompare(strict_cmp_kind, /*number_check = */ true); |
| } |
| |
| instructions += PushArgument(); // push receiver as argument. |
| |
| const Function& target = |
| Function::ZoneHandle(Z, LookupMethodByMember(kernel_name, method_name)); |
| |
| Array& argument_names = Array::ZoneHandle(Z); |
| intptr_t argument_count, positional_argument_count; |
| instructions += |
| BuildArguments(&argument_names, &argument_count, |
| &positional_argument_count); // read arguments. |
| ++argument_count; |
| |
| return instructions + StaticCall(position, target, argument_count, |
| argument_names, ICData::kNoRebind, |
| &result_type, type_args_len); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildSuperMethodInvocation( |
| TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| const TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| intptr_t type_args_len = 0; |
| if (I->reify_generic_functions()) { |
| AlternativeReadingScope alt(&reader_); |
| SkipName(); // skip method name |
| ReadUInt(); // read argument count. |
| type_args_len = ReadListLength(); // read types list length. |
| } |
| |
| Class& klass = GetSuperOrDie(); |
| |
| // Search the superclass chain for the selector. |
| const String& method_name = ReadNameAsMethodName(); // read name. |
| |
| // Figure out selector signature. |
| intptr_t argument_count; |
| Array& argument_names = Array::Handle(Z); |
| { |
| AlternativeReadingScope alt(&reader_); |
| argument_count = ReadUInt(); |
| SkipListOfDartTypes(); |
| |
| SkipListOfExpressions(); |
| intptr_t named_list_length = ReadListLength(); |
| argument_names ^= Array::New(named_list_length, H.allocation_space()); |
| for (intptr_t i = 0; i < named_list_length; i++) { |
| const String& arg_name = H.DartSymbolObfuscate(ReadStringReference()); |
| argument_names.SetAt(i, arg_name); |
| SkipExpression(); |
| } |
| } |
| |
| Function& function = FindMatchingFunction( |
| klass, method_name, type_args_len, |
| argument_count + 1 /* account for 'this' */, argument_names); |
| |
| if (function.IsNull()) { |
| ReadUInt(); // argument count |
| intptr_t type_list_length = ReadListLength(); |
| |
| Fragment instructions; |
| instructions += |
| Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null())); |
| instructions += IntConstant(argument_count + 1 /* this */ + |
| (type_list_length == 0 ? 0 : 1)); // array size |
| instructions += CreateArray(); |
| LocalVariable* actuals_array = MakeTemporary(); |
| |
| // Call allocationInvocationMirror to get instance of Invocation. |
| Fragment build_rest_of_actuals; |
| intptr_t actuals_array_index = 0; |
| if (type_list_length > 0) { |
| const TypeArguments& type_arguments = |
| T.BuildTypeArguments(type_list_length); |
| build_rest_of_actuals += LoadLocal(actuals_array); |
| build_rest_of_actuals += IntConstant(actuals_array_index); |
| build_rest_of_actuals += |
| TranslateInstantiatedTypeArguments(type_arguments); |
| build_rest_of_actuals += StoreIndexed(kArrayCid); |
| build_rest_of_actuals += Drop(); // dispose of stored value |
| ++actuals_array_index; |
| } |
| |
| ++actuals_array_index; // account for 'this'. |
| // Read arguments |
| intptr_t list_length = ReadListLength(); |
| intptr_t i = 0; |
| while (i < list_length) { |
| build_rest_of_actuals += LoadLocal(actuals_array); // array |
| build_rest_of_actuals += IntConstant(actuals_array_index + i); // index |
| build_rest_of_actuals += BuildExpression(); // value. |
| build_rest_of_actuals += StoreIndexed(kArrayCid); |
| build_rest_of_actuals += Drop(); // dispose of stored value |
| ++i; |
| } |
| // Read named arguments |
| intptr_t named_list_length = ReadListLength(); |
| if (named_list_length > 0) { |
| ASSERT(argument_count == list_length + named_list_length); |
| while ((i - list_length) < named_list_length) { |
| SkipStringReference(); |
| build_rest_of_actuals += LoadLocal(actuals_array); // array |
| build_rest_of_actuals += IntConstant(i + actuals_array_index); // index |
| build_rest_of_actuals += BuildExpression(); // value. |
| build_rest_of_actuals += StoreIndexed(kArrayCid); |
| build_rest_of_actuals += Drop(); // dispose of stored value |
| ++i; |
| } |
| } |
| instructions += BuildAllocateInvocationMirrorCall( |
| position, method_name, type_list_length, |
| /* num_arguments = */ argument_count + 1, argument_names, actuals_array, |
| build_rest_of_actuals); |
| instructions += PushArgument(); // second argument - invocation mirror |
| |
| SkipCanonicalNameReference(); // skip target_reference. |
| |
| Function& nsm_function = GetNoSuchMethodOrDie(Z, klass); |
| instructions += StaticCall(TokenPosition::kNoSource, |
| Function::ZoneHandle(Z, nsm_function.raw()), |
| /* argument_count = */ 2, ICData::kNSMDispatch); |
| instructions += DropTempsPreserveTop(1); // Drop actuals_array temp. |
| return instructions; |
| } else { |
| Fragment instructions; |
| |
| if (I->reify_generic_functions()) { |
| AlternativeReadingScope alt(&reader_); |
| ReadUInt(); // read argument count. |
| intptr_t list_length = ReadListLength(); // read types list length. |
| if (list_length > 0) { |
| const TypeArguments& type_arguments = |
| T.BuildTypeArguments(list_length); // read types. |
| instructions += TranslateInstantiatedTypeArguments(type_arguments); |
| instructions += PushArgument(); |
| } |
| } |
| |
| // receiver |
| instructions += LoadLocal(scopes()->this_variable); |
| instructions += PushArgument(); |
| |
| Array& argument_names = Array::ZoneHandle(Z); |
| intptr_t argument_count; |
| instructions += BuildArguments( |
| &argument_names, &argument_count, |
| /* positional_argument_count = */ NULL); // read arguments. |
| ++argument_count; // include receiver |
| SkipCanonicalNameReference(); // interfaceTargetReference |
| return instructions + |
| StaticCall(position, Function::ZoneHandle(Z, function.raw()), |
| argument_count, argument_names, ICData::kSuper, |
| &result_type, type_args_len); |
| } |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(bool is_const, |
| TokenPosition* p) { |
| const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const InferredTypeMetadata result_type = |
| inferred_type_metadata_helper_.GetInferredType(offset); |
| |
| NameIndex procedure_reference = |
| ReadCanonicalNameReference(); // read procedure reference. |
| intptr_t argument_count = PeekArgumentsCount(); |
| const Function& target = Function::ZoneHandle( |
| Z, H.LookupStaticMethodByKernelProcedure(procedure_reference)); |
| const Class& klass = Class::ZoneHandle(Z, target.Owner()); |
| if (target.IsGenerativeConstructor() || target.IsFactory()) { |
| // The VM requires a TypeArguments object as first parameter for |
| // every factory constructor. |
| ++argument_count; |
| } |
| |
| Fragment instructions; |
| LocalVariable* instance_variable = NULL; |
| |
| bool special_case_identical = klass.IsTopLevel() && |
| (klass.library() == Library::CoreLibrary()) && |
| (target.name() == Symbols::Identical().raw()); |
| |
| // If we cross the Kernel -> VM core library boundary, a [StaticInvocation] |
| // can appear, but the thing we're calling is not a static method, but a |
| // factory constructor. |
| // The `H.LookupStaticmethodByKernelProcedure` will potentially resolve to the |
| // forwarded constructor. |
| // In that case we'll make an instance and pass it as first argument. |
| // |
| // TODO(27590): Get rid of this after we're using core libraries compiled |
| // into Kernel. |
| intptr_t type_args_len = 0; |
| if (target.IsGenerativeConstructor()) { |
| if (klass.NumTypeArguments() > 0) { |
| const TypeArguments& type_arguments = |
| PeekArgumentsInstantiatedType(klass); |
| instructions += TranslateInstantiatedTypeArguments(type_arguments); |
| instructions += PushArgument(); |
| instructions += AllocateObject(position, klass, 1); |
| } else { |
| instructions += AllocateObject(position, klass, 0); |
| } |
| |
| instance_variable = MakeTemporary(); |
| |
| instructions += LoadLocal(instance_variable); |
| instructions += PushArgument(); |
| } else if (target.IsFactory()) { |
| // The VM requires currently a TypeArguments object as first parameter for |
| // every factory constructor :-/ ! |
| // |
| // TODO(27590): Get rid of this after we're using core libraries compiled |
| // into Kernel. |
| const TypeArguments& type_arguments = PeekArgumentsInstantiatedType(klass); |
| instructions += TranslateInstantiatedTypeArguments(type_arguments); |
| instructions += PushArgument(); |
| } else if (!special_case_identical && I->reify_generic_functions()) { |
| AlternativeReadingScope alt(&reader_); |
| ReadUInt(); // read argument count. |
| intptr_t list_length = ReadListLength(); // read types list length. |
| if (list_length > 0) { |
| const TypeArguments& type_arguments = |
| T.BuildTypeArguments(list_length); // read types. |
| instructions += TranslateInstantiatedTypeArguments(type_arguments); |
| instructions += PushArgument(); |
| } |
| type_args_len = list_length; |
| } |
| |
| Array& argument_names = Array::ZoneHandle(Z); |
| instructions += BuildArguments(&argument_names, NULL /* arg count */, |
| NULL /* positional arg count */, |
| special_case_identical); // read arguments. |
| ASSERT(target.AreValidArguments(type_args_len, argument_count, argument_names, |
| NULL)); |
| |
| // Special case identical(x, y) call. |
| // TODO(27590) consider moving this into the inliner and force inline it |
| // there. |
| if (special_case_identical) { |
| ASSERT(argument_count == 2); |
| instructions += StrictCompare(Token::kEQ_STRICT, /*number_check=*/true); |
| } else { |
| instructions += StaticCall(position, target, argument_count, argument_names, |
| ICData::kStatic, &result_type, type_args_len); |
| if (target.IsGenerativeConstructor()) { |
| // Drop the result of the constructor call and leave [instance_variable] |
| // on top-of-stack. |
| instructions += Drop(); |
| } |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildConstructorInvocation( |
| bool is_const, |
| TokenPosition* p) { |
| if (is_const) { |
| intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| (p != NULL) ? * p = ReadPosition() : ReadPosition(); // read position. |
| |
| SetOffset(offset); |
| SkipExpression(); // read past this ConstructorInvocation. |
| return Constant(constant_evaluator_.EvaluateConstructorInvocation(offset)); |
| } |
| |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| NameIndex kernel_name = |
| ReadCanonicalNameReference(); // read target_reference. |
| |
| Class& klass = Class::ZoneHandle( |
| Z, H.LookupClassByKernelClass(H.EnclosingName(kernel_name))); |
| |
| Fragment instructions; |
| |
| // Check for malbounded-ness of type. |
| if (I->type_checks()) { |
| intptr_t offset = ReaderOffset(); |
| |
| const TypeArguments& type_arguments = BuildTypeArguments(); |
| |
| AbstractType& type = AbstractType::Handle( |
| Z, Type::New(klass, type_arguments, TokenPosition::kNoSource)); |
| type = ClassFinalizer::FinalizeType(klass, type); |
| |
| if (type.IsMalbounded()) { |
| // Evaluate expressions for correctness. |
| instructions += |
| BuildArgumentsFromActualArguments(NULL, false, /*do_drop*/ true); |
| |
| // Throw an error & keep the [Value] on the stack. |
| instructions += ThrowTypeError(); |
| |
| // Bail out early. |
| return instructions; |
| } |
| |
| SetOffset(offset); |
| } |
| |
| if (klass.NumTypeArguments() > 0) { |
| if (!klass.IsGeneric()) { |
| Type& type = Type::ZoneHandle(Z, T.ReceiverType(klass).raw()); |
| |
| // TODO(27590): Can we move this code into [ReceiverType]? |
| type ^= ClassFinalizer::FinalizeType(*active_class()->klass, type, |
| ClassFinalizer::kFinalize); |
| ASSERT(!type.IsMalformedOrMalbounded()); |
| |
| TypeArguments& canonicalized_type_arguments = |
| TypeArguments::ZoneHandle(Z, type.arguments()); |
| canonicalized_type_arguments = |
| canonicalized_type_arguments.Canonicalize(); |
| instructions += Constant(canonicalized_type_arguments); |
| } else { |
| const TypeArguments& type_arguments = |
| PeekArgumentsInstantiatedType(klass); |
| instructions += TranslateInstantiatedTypeArguments(type_arguments); |
| } |
| |
| instructions += PushArgument(); |
| instructions += AllocateObject(position, klass, 1); |
| } else { |
| instructions += AllocateObject(position, klass, 0); |
| } |
| LocalVariable* variable = MakeTemporary(); |
| |
| instructions += LoadLocal(variable); |
| instructions += PushArgument(); |
| |
| Array& argument_names = Array::ZoneHandle(Z); |
| intptr_t argument_count; |
| instructions += BuildArguments( |
| &argument_names, &argument_count, |
| /* positional_argument_count = */ NULL); // read arguments. |
| |
| const Function& target = Function::ZoneHandle( |
| Z, H.LookupConstructorByKernelConstructor(klass, kernel_name)); |
| ++argument_count; |
| instructions += StaticCall(position, target, argument_count, argument_names, |
| ICData::kStatic, /* result_type = */ NULL); |
| return instructions + Drop(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildNot(TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| Fragment instructions = BuildExpression(); // read expression. |
| instructions += CheckBoolean(); |
| instructions += BooleanNegate(); |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildLogicalExpression( |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| bool negate; |
| Fragment instructions = TranslateCondition(&negate); // read left. |
| |
| TargetEntryInstr* right_entry; |
| TargetEntryInstr* constant_entry; |
| LogicalOperator op = static_cast<LogicalOperator>(ReadByte()); |
| |
| if (op == kAnd) { |
| instructions += BranchIfTrue(&right_entry, &constant_entry, negate); |
| } else { |
| instructions += BranchIfTrue(&constant_entry, &right_entry, negate); |
| } |
| |
| Value* top = stack(); |
| Fragment right_fragment(right_entry); |
| right_fragment += TranslateCondition(&negate); // read right. |
| |
| right_fragment += Constant(Bool::True()); |
| right_fragment += |
| StrictCompare(negate ? Token::kNE_STRICT : Token::kEQ_STRICT); |
| right_fragment += StoreLocal(TokenPosition::kNoSource, |
| parsed_function()->expression_temp_var()); |
| right_fragment += Drop(); |
| |
| ASSERT(top == stack()); |
| Fragment constant_fragment(constant_entry); |
| constant_fragment += Constant(Bool::Get(op == kOr)); |
| constant_fragment += StoreLocal(TokenPosition::kNoSource, |
| parsed_function()->expression_temp_var()); |
| constant_fragment += Drop(); |
| |
| JoinEntryInstr* join = BuildJoinEntry(); |
| right_fragment += Goto(join); |
| constant_fragment += Goto(join); |
| |
| return Fragment(instructions.entry, join) + |
| LoadLocal(parsed_function()->expression_temp_var()); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildConditionalExpression( |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| bool negate; |
| Fragment instructions = TranslateCondition(&negate); // read condition. |
| |
| TargetEntryInstr* then_entry; |
| TargetEntryInstr* otherwise_entry; |
| instructions += BranchIfTrue(&then_entry, &otherwise_entry, negate); |
| |
| Value* top = stack(); |
| Fragment then_fragment(then_entry); |
| then_fragment += BuildExpression(); // read then. |
| then_fragment += StoreLocal(TokenPosition::kNoSource, |
| parsed_function()->expression_temp_var()); |
| then_fragment += Drop(); |
| ASSERT(stack() == top); |
| |
| Fragment otherwise_fragment(otherwise_entry); |
| otherwise_fragment += BuildExpression(); // read otherwise. |
| otherwise_fragment += StoreLocal(TokenPosition::kNoSource, |
| parsed_function()->expression_temp_var()); |
| otherwise_fragment += Drop(); |
| ASSERT(stack() == top); |
| |
| JoinEntryInstr* join = BuildJoinEntry(); |
| then_fragment += Goto(join); |
| otherwise_fragment += Goto(join); |
| |
| SkipOptionalDartType(); // read unused static type. |
| |
| return Fragment(instructions.entry, join) + |
| LoadLocal(parsed_function()->expression_temp_var()); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStringConcatenation(TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| intptr_t length = ReadListLength(); // read list length. |
| // Note: there will be "length" expressions. |
| |
| Fragment instructions; |
| if (length == 1) { |
| instructions += BuildExpression(); // read expression. |
| instructions += StringInterpolateSingle(position); |
| } else { |
| // The type arguments for CreateArray. |
| instructions += Constant(TypeArguments::ZoneHandle(Z)); |
| instructions += IntConstant(length); |
| instructions += CreateArray(); |
| LocalVariable* array = MakeTemporary(); |
| |
| for (intptr_t i = 0; i < length; ++i) { |
| instructions += LoadLocal(array); |
| instructions += IntConstant(i); |
| instructions += BuildExpression(); // read ith expression. |
| instructions += StoreIndexed(kArrayCid); |
| instructions += Drop(); |
| } |
| |
| instructions += StringInterpolate(position); |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildIsExpression(TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| Fragment instructions = BuildExpression(); // read operand. |
| |
| const AbstractType& type = T.BuildType(); // read type. |
| |
| // The VM does not like an instanceOf call with a dynamic type. We need to |
| // special case this situation. |
| const Type& object_type = Type::Handle(Z, Type::ObjectType()); |
| |
| if (type.IsMalformed()) { |
| instructions += Drop(); |
| instructions += ThrowTypeError(); |
| return instructions; |
| } |
| |
| if (type.IsInstantiated() && |
| object_type.IsSubtypeOf(type, NULL, NULL, Heap::kOld)) { |
| // Evaluate the expression on the left but ignore it's result. |
| instructions += Drop(); |
| |
| // Let condition be always true. |
| instructions += Constant(Bool::True()); |
| } else { |
| instructions += PushArgument(); |
| |
| // See if simple instanceOf is applicable. |
| if (dart::FlowGraphBuilder::SimpleInstanceOfType(type)) { |
| instructions += Constant(type); |
| instructions += PushArgument(); // Type. |
| instructions += InstanceCall( |
| position, Library::PrivateCoreLibName(Symbols::_simpleInstanceOf()), |
| Token::kIS, 2, 2); // 2 checked arguments. |
| return instructions; |
| } |
| |
| if (!type.IsInstantiated(kCurrentClass)) { |
| instructions += LoadInstantiatorTypeArguments(); |
| } else { |
| instructions += NullConstant(); |
| } |
| instructions += PushArgument(); // Instantiator type arguments. |
| |
| if (!type.IsInstantiated(kFunctions)) { |
| instructions += LoadFunctionTypeArguments(); |
| } else { |
| instructions += NullConstant(); |
| } |
| instructions += PushArgument(); // Function type arguments. |
| |
| instructions += Constant(type); |
| instructions += PushArgument(); // Type. |
| |
| instructions += InstanceCall( |
| position, Library::PrivateCoreLibName(Symbols::_instanceOf()), |
| Token::kIS, 4); |
| } |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildAsExpression(TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| uint8_t flags = ReadFlags(); // read flags. |
| const bool is_type_error = (flags & (1 << 0)) != 0; |
| |
| TokenPosition value_position = TokenPosition::kNoSource; |
| Fragment instructions = BuildExpression(&value_position); // read operand. |
| |
| const AbstractType& type = T.BuildType(); // read type. |
| |
| // The VM does not like an Object_as call with a dynamic type. We need to |
| // special case this situation. |
| const Type& object_type = Type::Handle(Z, Type::ObjectType()); |
| |
| if (type.IsMalformed()) { |
| instructions += Drop(); |
| instructions += ThrowTypeError(); |
| return instructions; |
| } |
| |
| if (type.IsInstantiated() && |
| object_type.IsSubtypeOf(type, NULL, NULL, Heap::kOld)) { |
| // We already evaluated the operand on the left and just leave it there as |
| // the result of the `obj as dynamic` expression. |
| } else if (is_type_error) { |
| instructions += LoadLocal(MakeTemporary()); |
| instructions += flow_graph_builder_->AssertAssignable( |
| value_position, type, Symbols::Empty(), |
| AssertAssignableInstr::kInsertedByFrontend); |
| instructions += Drop(); |
| } else { |
| instructions += PushArgument(); |
| |
| if (!type.IsInstantiated(kCurrentClass)) { |
| instructions += LoadInstantiatorTypeArguments(); |
| } else { |
| instructions += NullConstant(); |
| } |
| instructions += PushArgument(); // Instantiator type arguments. |
| |
| if (!type.IsInstantiated(kFunctions)) { |
| instructions += LoadFunctionTypeArguments(); |
| } else { |
| instructions += NullConstant(); |
| } |
| instructions += PushArgument(); // Function type arguments. |
| |
| instructions += Constant(type); |
| instructions += PushArgument(); // Type. |
| |
| instructions += InstanceCall( |
| position, Library::PrivateCoreLibName(Symbols::_as()), Token::kAS, 4); |
| } |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildSymbolLiteral( |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| intptr_t offset = ReaderOffset() - 1; // EvaluateExpression needs the tag. |
| SkipStringReference(); // read index into string table. |
| return Constant( |
| Instance::ZoneHandle(Z, constant_evaluator_.EvaluateExpression(offset))); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildTypeLiteral(TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| const AbstractType& type = T.BuildType(); // read type. |
| if (type.IsMalformed()) { |
| H.ReportError(script_, TokenPosition::kNoSource, "Malformed type literal"); |
| } |
| |
| Fragment instructions; |
| if (type.IsInstantiated()) { |
| instructions += Constant(type); |
| } else { |
| if (!type.IsInstantiated(kCurrentClass)) { |
| instructions += LoadInstantiatorTypeArguments(); |
| } else { |
| instructions += NullConstant(); |
| } |
| if (!type.IsInstantiated(kFunctions)) { |
| instructions += LoadFunctionTypeArguments(); |
| } else { |
| instructions += NullConstant(); |
| } |
| instructions += InstantiateType(type); |
| } |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildThisExpression( |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| return LoadLocal(scopes()->this_variable); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildRethrow(TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| Fragment instructions = DebugStepCheck(position); |
| instructions += LoadLocal(catch_block()->exception_var()); |
| instructions += PushArgument(); |
| instructions += LoadLocal(catch_block()->stack_trace_var()); |
| instructions += PushArgument(); |
| instructions += RethrowException(position, catch_block()->catch_try_index()); |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildThrow(TokenPosition* p) { |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| Fragment instructions; |
| |
| instructions += BuildExpression(); // read expression. |
| |
| if (NeedsDebugStepCheck(stack(), position)) { |
| instructions = DebugStepCheck(position) + instructions; |
| } |
| instructions += PushArgument(); |
| instructions += ThrowException(position); |
| ASSERT(instructions.is_closed()); |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildListLiteral(bool is_const, |
| TokenPosition* p) { |
| if (is_const) { |
| intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| (p != NULL) ? * p = ReadPosition() : ReadPosition(); // read position. |
| |
| SetOffset(offset); |
| SkipExpression(); // read past the ListLiteral. |
| return Constant(constant_evaluator_.EvaluateListLiteral(offset)); |
| } |
| |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const TypeArguments& type_arguments = T.BuildTypeArguments(1); // read type. |
| intptr_t length = ReadListLength(); // read list length. |
| // Note: there will be "length" expressions. |
| |
| // The type argument for the factory call. |
| Fragment instructions = TranslateInstantiatedTypeArguments(type_arguments); |
| LocalVariable* type = MakeTemporary(); |
| |
| instructions += LoadLocal(type); |
| instructions += PushArgument(); |
| if (length == 0) { |
| instructions += Constant(Object::empty_array()); |
| } else { |
| // The type arguments for CreateArray. |
| instructions += LoadLocal(type); |
| instructions += IntConstant(length); |
| instructions += CreateArray(); |
| AbstractType& list_type = AbstractType::ZoneHandle(Z); |
| if (I->type_checks()) { |
| if (type_arguments.IsNull()) { |
| // It was dynamic. |
| list_type = Object::dynamic_type().raw(); |
| } else { |
| list_type = type_arguments.TypeAt(0); |
| } |
| } |
| |
| LocalVariable* array = MakeTemporary(); |
| for (intptr_t i = 0; i < length; ++i) { |
| instructions += LoadLocal(array); |
| instructions += IntConstant(i); |
| instructions += BuildExpression(); // read ith expression. |
| instructions += CheckAssignableInCheckedMode( |
| list_type, Symbols::ListLiteralElement()); |
| instructions += StoreIndexed(kArrayCid); |
| instructions += Drop(); |
| } |
| } |
| instructions += PushArgument(); // The array. |
| |
| const Class& factory_class = |
| Class::Handle(Z, Library::LookupCoreClass(Symbols::List())); |
| const Function& factory_method = Function::ZoneHandle( |
| Z, factory_class.LookupFactory( |
| Library::PrivateCoreLibName(Symbols::ListLiteralFactory()))); |
| |
| instructions += StaticCall(position, factory_method, 2, ICData::kStatic); |
| instructions += DropTempsPreserveTop(1); // Instantiated type_arguments. |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildMapLiteral(bool is_const, |
| TokenPosition* p) { |
| if (is_const) { |
| intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| (p != NULL) ? * p = ReadPosition() : ReadPosition(); |
| |
| SetOffset(offset); |
| SkipExpression(); // Read past the MapLiteral. |
| return Constant(constant_evaluator_.EvaluateMapLiteral(offset)); |
| } |
| |
| TokenPosition position = ReadPosition(); // read position. |
| if (p != NULL) *p = position; |
| |
| const TypeArguments& type_arguments = |
| T.BuildTypeArguments(2); // read key_type and value_type. |
| |
| // The type argument for the factory call `new Map<K, V>._fromLiteral(List)`. |
| Fragment instructions = TranslateInstantiatedTypeArguments(type_arguments); |
| instructions += PushArgument(); |
| |
| intptr_t length = ReadListLength(); // read list length. |
| // Note: there will be "length" map entries (i.e. key and value expressions). |
| |
| if (length == 0) { |
| instructions += Constant(Object::empty_array()); |
| } else { |
| // The type arguments for `new List<X>(int len)`. |
| instructions += Constant(TypeArguments::ZoneHandle(Z)); |
| |
| // We generate a list of tuples, i.e. [key1, value1, ..., keyN, valueN]. |
| instructions += IntConstant(2 * length); |
| instructions += CreateArray(); |
| |
| LocalVariable* array = MakeTemporary(); |
| for (intptr_t i = 0; i < length; ++i) { |
| instructions += LoadLocal(array); |
| instructions += IntConstant(2 * i); |
| instructions += BuildExpression(); // read ith key. |
| instructions += StoreIndexed(kArrayCid); |
| instructions += Drop(); |
| |
| instructions += LoadLocal(array); |
| instructions += IntConstant(2 * i + 1); |
| instructions += BuildExpression(); // read ith value. |
| instructions += StoreIndexed(kArrayCid); |
| instructions += Drop(); |
| } |
| } |
| instructions += PushArgument(); // The array. |
| |
| const Class& map_class = |
| Class::Handle(Z, Library::LookupCoreClass(Symbols::Map())); |
| const Function& factory_method = Function::ZoneHandle( |
| Z, map_class.LookupFactory( |
| Library::PrivateCoreLibName(Symbols::MapLiteralFactory()))); |
| |
| return instructions + |
| StaticCall(position, factory_method, 2, ICData::kStatic); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildFunctionExpression() { |
| ReadPosition(); // read position. |
| return BuildFunctionNode(TokenPosition::kNoSource, StringIndex()); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildLet(TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| Fragment instructions = BuildVariableDeclaration(); // read variable. |
| instructions += BuildExpression(); // read body. |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildBigIntLiteral( |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| const String& value = |
| H.DartString(ReadStringReference()); // read index into string table. |
| const Integer& integer = |
| Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld)); |
| if (integer.IsNull()) { |
| H.ReportError(script_, TokenPosition::kNoSource, |
| "Integer literal %s is out of range", value.ToCString()); |
| UNREACHABLE(); |
| } |
| return Constant(integer); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildStringLiteral( |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| return Constant(H.DartSymbolPlain( |
| ReadStringReference())); // read index into string table. |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildIntLiteral(uint8_t payload, |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| int64_t value = static_cast<int32_t>(payload) - SpecializedIntLiteralBias; |
| return IntConstant(value); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildIntLiteral(bool is_negative, |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| int64_t value = is_negative ? -static_cast<int64_t>(ReadUInt()) |
| : ReadUInt(); // read value. |
| return IntConstant(value); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildDoubleLiteral( |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| Double& constant = Double::ZoneHandle( |
| Z, Double::NewCanonical(ReadDouble())); // read double. |
| return Constant(constant); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildBoolLiteral(bool value, |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| return Constant(Bool::Get(value)); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildNullLiteral(TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| return Constant(Instance::ZoneHandle(Z, Instance::null())); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildFutureNullValue( |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| const Class& future = Class::Handle(Z, I->object_store()->future_class()); |
| ASSERT(!future.IsNull()); |
| const Function& constructor = |
| Function::ZoneHandle(Z, future.LookupFunction(Symbols::FutureValue())); |
| ASSERT(!constructor.IsNull()); |
| |
| Fragment instructions; |
| instructions += BuildNullLiteral(position); |
| instructions += PushArgument(); |
| instructions += StaticCall(TokenPosition::kNoSource, constructor, |
| /* argument_count = */ 1, ICData::kStatic); |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildConstantExpression( |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| const intptr_t constant_offset = ReadUInt(); |
| KernelConstantsMap constant_map(H.constants().raw()); |
| Fragment result = |
| Constant(Object::ZoneHandle(Z, constant_map.GetOrDie(constant_offset))); |
| ASSERT(constant_map.Release().raw() == H.constants().raw()); |
| return result; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildPartialTearoffInstantiation( |
| TokenPosition* position) { |
| if (position != NULL) *position = TokenPosition::kNoSource; |
| |
| // Create a copy of the closure. |
| |
| Fragment instructions = BuildExpression(); |
| LocalVariable* original_closure = MakeTemporary(); |
| |
| instructions += AllocateObject( |
| TokenPosition::kNoSource, |
| Class::ZoneHandle(Z, I->object_store()->closure_class()), 0); |
| LocalVariable* new_closure = MakeTemporary(); |
| |
| instructions += LoadLocal(new_closure); |
| |
| intptr_t num_type_args = ReadListLength(); |
| const TypeArguments& type_args = T.BuildTypeArguments(num_type_args); |
| instructions += TranslateInstantiatedTypeArguments(type_args); |
| instructions += StoreInstanceField(TokenPosition::kNoSource, |
| Closure::delayed_type_arguments_offset()); |
| |
| // Copy over the target function. |
| instructions += LoadLocal(new_closure); |
| instructions += LoadLocal(original_closure); |
| instructions += LoadField(Closure::function_offset()); |
| instructions += |
| StoreInstanceField(TokenPosition::kNoSource, Closure::function_offset()); |
| |
| // Copy over the instantiator type arguments. |
| instructions += LoadLocal(new_closure); |
| instructions += LoadLocal(original_closure); |
| instructions += LoadField(Closure::instantiator_type_arguments_offset()); |
| instructions += StoreInstanceField( |
| TokenPosition::kNoSource, Closure::instantiator_type_arguments_offset()); |
| |
| // Copy over the function type arguments. |
| instructions += LoadLocal(new_closure); |
| instructions += LoadLocal(original_closure); |
| instructions += LoadField(Closure::function_type_arguments_offset()); |
| instructions += StoreInstanceField(TokenPosition::kNoSource, |
| Closure::function_type_arguments_offset()); |
| |
| // Copy over the context. |
| instructions += LoadLocal(new_closure); |
| instructions += LoadLocal(original_closure); |
| instructions += LoadField(Closure::context_offset()); |
| instructions += |
| StoreInstanceField(TokenPosition::kNoSource, Closure::context_offset()); |
| |
| instructions += DropTempsPreserveTop(1); // drop old closure |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildExpressionStatement() { |
| Fragment instructions = BuildExpression(); // read expression. |
| instructions += Drop(); |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildBlock() { |
| intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| |
| Fragment instructions; |
| |
| instructions += EnterScope(offset); |
| intptr_t list_length = ReadListLength(); // read number of statements. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| if (instructions.is_open()) { |
| instructions += BuildStatement(); // read ith statement. |
| } else { |
| SkipStatement(); // read ith statement. |
| } |
| } |
| instructions += ExitScope(offset); |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildEmptyStatement() { |
| return Fragment(); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildAssertBlock() { |
| if (!I->asserts()) { |
| SkipStatementList(); |
| return Fragment(); |
| } |
| |
| intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| |
| Fragment instructions; |
| |
| instructions += EnterScope(offset); |
| intptr_t list_length = ReadListLength(); // read number of statements. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| if (instructions.is_open()) { |
| instructions += BuildStatement(); // read ith statement. |
| } else { |
| SkipStatement(); // read ith statement. |
| } |
| } |
| instructions += ExitScope(offset); |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildAssertStatement() { |
| if (!I->asserts()) { |
| SetOffset(ReaderOffset() - 1); // Include the tag. |
| SkipStatement(); // read this statement. |
| return Fragment(); |
| } |
| |
| TargetEntryInstr* then; |
| TargetEntryInstr* otherwise; |
| |
| Fragment instructions; |
| // Asserts can be of the following two kinds: |
| // |
| // * `assert(expr)` |
| // * `assert(() { ... })` |
| // |
| // The call to `_AssertionError._evaluateAssertion()` will take care of both |
| // and returns a boolean. |
| instructions += BuildExpression(); // read condition. |
| instructions += PushArgument(); |
| instructions += EvaluateAssertion(); |
| instructions += CheckBoolean(); |
| instructions += Constant(Bool::True()); |
| instructions += BranchIfEqual(&then, &otherwise, false); |
| |
| TokenPosition condition_start_offset = |
| ReadPosition(); // read condition start offset. |
| TokenPosition condition_end_offset = |
| ReadPosition(); // read condition end offset. |
| |
| const Class& klass = |
| Class::ZoneHandle(Z, Library::LookupCoreClass(Symbols::AssertionError())); |
| ASSERT(!klass.IsNull()); |
| const Function& target = Function::ZoneHandle( |
| Z, klass.LookupStaticFunctionAllowPrivate(Symbols::ThrowNew())); |
| ASSERT(!target.IsNull()); |
| |
| // Build equivalent of `throw _AssertionError._throwNew(start, end, message)` |
| // expression. We build throw (even through _throwNew already throws) because |
| // call is not a valid last instruction for the block. Blocks can only |
| // terminate with explicit control flow instructions (Branch, Goto, Return |
| // or Throw). |
| Fragment otherwise_fragment(otherwise); |
| otherwise_fragment += IntConstant(condition_start_offset.Pos()); |
| otherwise_fragment += PushArgument(); // start |
| otherwise_fragment += IntConstant(condition_end_offset.Pos()); |
| otherwise_fragment += PushArgument(); // end |
| Tag tag = ReadTag(); // read (first part of) message. |
| if (tag == kSomething) { |
| otherwise_fragment += BuildExpression(); // read (rest of) message. |
| } else { |
| otherwise_fragment += Constant(Instance::ZoneHandle(Z)); // null. |
| } |
| otherwise_fragment += PushArgument(); // message |
| |
| otherwise_fragment += |
| StaticCall(TokenPosition::kNoSource, target, 3, ICData::kStatic); |
| otherwise_fragment += PushArgument(); |
| otherwise_fragment += ThrowException(TokenPosition::kNoSource); |
| otherwise_fragment += Drop(); |
| |
| return Fragment(instructions.entry, then); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildLabeledStatement() { |
| // There can be serveral cases: |
| // |
| // * the body contains a break |
| // * the body doesn't contain a break |
| // |
| // * translating the body results in a closed fragment |
| // * translating the body results in a open fragment |
| // |
| // => We will only know which case we are in after the body has been |
| // traversed. |
| |
| BreakableBlock block(flow_graph_builder_); |
| Fragment instructions = BuildStatement(); // read body. |
| if (block.HadJumper()) { |
| if (instructions.is_open()) { |
| instructions += Goto(block.destination()); |
| } |
| return Fragment(instructions.entry, block.destination()); |
| } else { |
| return instructions; |
| } |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildBreakStatement() { |
| TokenPosition position = ReadPosition(); // read position. |
| intptr_t target_index = ReadUInt(); // read target index. |
| |
| TryFinallyBlock* outer_finally = NULL; |
| intptr_t target_context_depth = -1; |
| JoinEntryInstr* destination = breakable_block()->BreakDestination( |
| target_index, &outer_finally, &target_context_depth); |
| |
| Fragment instructions; |
| instructions += |
| TranslateFinallyFinalizers(outer_finally, target_context_depth); |
| if (instructions.is_open()) { |
| if (NeedsDebugStepCheck(parsed_function()->function(), position)) { |
| instructions += DebugStepCheck(position); |
| } |
| instructions += Goto(destination); |
| } |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildWhileStatement() { |
| loop_depth_inc(); |
| const TokenPosition position = ReadPosition(); // read position. |
| |
| bool negate; |
| Fragment condition = TranslateCondition(&negate); // read condition. |
| TargetEntryInstr* body_entry; |
| TargetEntryInstr* loop_exit; |
| condition += BranchIfTrue(&body_entry, &loop_exit, negate); |
| |
| Fragment body(body_entry); |
| body += BuildStatement(); // read body. |
| |
| Instruction* entry; |
| if (body.is_open()) { |
| JoinEntryInstr* join = BuildJoinEntry(); |
| body += Goto(join); |
| |
| Fragment loop(join); |
| loop += CheckStackOverflow(position); |
| loop += condition; |
| entry = new (Z) GotoInstr(join, Thread::Current()->GetNextDeoptId()); |
| } else { |
| entry = condition.entry; |
| } |
| |
| loop_depth_dec(); |
| return Fragment(entry, loop_exit); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildDoStatement() { |
| loop_depth_inc(); |
| const TokenPosition position = ReadPosition(); // read position. |
| Fragment body = BuildStatement(); // read body. |
| |
| if (body.is_closed()) { |
| SkipExpression(); // read condition. |
| loop_depth_dec(); |
| return body; |
| } |
| |
| bool negate; |
| JoinEntryInstr* join = BuildJoinEntry(); |
| Fragment loop(join); |
| loop += CheckStackOverflow(position); |
| loop += body; |
| loop += TranslateCondition(&negate); // read condition. |
| TargetEntryInstr* loop_repeat; |
| TargetEntryInstr* loop_exit; |
| loop += BranchIfTrue(&loop_repeat, &loop_exit, negate); |
| |
| Fragment repeat(loop_repeat); |
| repeat += Goto(join); |
| |
| loop_depth_dec(); |
| return Fragment(new (Z) GotoInstr(join, Thread::Current()->GetNextDeoptId()), |
| loop_exit); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildForStatement() { |
| intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| |
| const TokenPosition position = ReadPosition(); // read position. |
| |
| Fragment declarations; |
| |
| loop_depth_inc(); |
| |
| intptr_t num_context_variables = 0; |
| declarations += EnterScope(offset, &num_context_variables); |
| |
| intptr_t list_length = ReadListLength(); // read number of variables. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| declarations += BuildVariableDeclaration(); // read ith variable. |
| } |
| |
| bool negate = false; |
| Tag tag = ReadTag(); // Read first part of condition. |
| Fragment condition = |
| tag == kNothing ? Constant(Bool::True()) |
| : TranslateCondition(&negate); // read rest of condition. |
| TargetEntryInstr* body_entry; |
| TargetEntryInstr* loop_exit; |
| condition += BranchIfTrue(&body_entry, &loop_exit, negate); |
| |
| Fragment updates; |
| list_length = ReadListLength(); // read number of updates. |
| for (intptr_t i = 0; i < list_length; ++i) { |
| updates += BuildExpression(); // read ith update. |
| updates += Drop(); |
| } |
| |
| Fragment body(body_entry); |
| body += BuildStatement(); // read body. |
| |
| if (body.is_open()) { |
| // We allocated a fresh context before the loop which contains captured |
| // [ForStatement] variables. Before jumping back to the loop entry we clone |
| // the context object (at same depth) which ensures the next iteration of |
| // the body gets a fresh set of [ForStatement] variables (with the old |
| // (possibly updated) values). |
| if (num_context_variables > 0) body += CloneContext(num_context_variables); |
| |
| body += updates; |
| JoinEntryInstr* join = BuildJoinEntry(); |
| declarations += Goto(join); |
| body += Goto(join); |
| |
| Fragment loop(join); |
| loop += CheckStackOverflow(position); |
| loop += condition; |
| } else { |
| declarations += condition; |
| } |
| |
| Fragment loop(declarations.entry, loop_exit); |
| |
| loop += ExitScope(offset); |
| |
| loop_depth_dec(); |
| |
| return loop; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildForInStatement(bool async) { |
| intptr_t offset = ReaderOffset() - 1; // Include the tag. |
| |
| const TokenPosition position = ReadPosition(); // read position. |
| TokenPosition body_position = ReadPosition(); // read body position. |
| intptr_t variable_kernel_position = ReaderOffset() + data_program_offset_; |
| SkipVariableDeclaration(); // read variable. |
| |
| TokenPosition iterable_position = TokenPosition::kNoSource; |
| Fragment instructions = |
| BuildExpression(&iterable_position); // read iterable. |
| instructions += PushArgument(); |
| |
| const String& iterator_getter = |
| String::ZoneHandle(Z, Field::GetterSymbol(Symbols::Iterator())); |
| instructions += |
| InstanceCall(iterable_position, iterator_getter, Token::kGET, 1); |
| LocalVariable* iterator = scopes()->iterator_variables[for_in_depth()]; |
| instructions += StoreLocal(TokenPosition::kNoSource, iterator); |
| instructions += Drop(); |
| |
| for_in_depth_inc(); |
| loop_depth_inc(); |
| Fragment condition = LoadLocal(iterator); |
| condition += PushArgument(); |
| condition += |
| InstanceCall(iterable_position, Symbols::MoveNext(), Token::kILLEGAL, 1); |
| TargetEntryInstr* body_entry; |
| TargetEntryInstr* loop_exit; |
| condition += BranchIfTrue(&body_entry, &loop_exit, false); |
| |
| Fragment body(body_entry); |
| body += EnterScope(offset); |
| body += LoadLocal(iterator); |
| body += PushArgument(); |
| const String& current_getter = |
| String::ZoneHandle(Z, Field::GetterSymbol(Symbols::Current())); |
| body += InstanceCall(body_position, current_getter, Token::kGET, 1); |
| body += StoreLocal(TokenPosition::kNoSource, |
| LookupVariable(variable_kernel_position)); |
| body += Drop(); |
| body += BuildStatement(); // read body. |
| body += ExitScope(offset); |
| |
| if (body.is_open()) { |
| JoinEntryInstr* join = BuildJoinEntry(); |
| instructions += Goto(join); |
| body += Goto(join); |
| |
| Fragment loop(join); |
| loop += CheckStackOverflow(position); |
| loop += condition; |
| } else { |
| instructions += condition; |
| } |
| |
| loop_depth_dec(); |
| for_in_depth_dec(); |
| return Fragment(instructions.entry, loop_exit); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildSwitchStatement() { |
| ReadPosition(); // read position. |
| // We need the number of cases. So start by getting that, then go back. |
| intptr_t offset = ReaderOffset(); |
| SkipExpression(); // temporarily skip condition |
| int case_count = ReadListLength(); // read number of cases. |
| SetOffset(offset); |
| |
| SwitchBlock block(flow_graph_builder_, case_count); |
| |
| // Instead of using a variable we should reuse the expression on the stack, |
| // since it won't be assigned again, we don't need phi nodes. |
| Fragment head_instructions = BuildExpression(); // read condition. |
| head_instructions += |
| StoreLocal(TokenPosition::kNoSource, scopes()->switch_variable); |
| head_instructions += Drop(); |
| |
| case_count = ReadListLength(); // read number of cases. |
| |
| // Phase 1: Generate bodies and try to find out whether a body will be target |
| // of a jump due to: |
| // * `continue case_label` |
| // * `case e1: case e2: body` |
| Fragment* body_fragments = new Fragment[case_count]; |
| intptr_t* case_expression_offsets = new intptr_t[case_count]; |
| int default_case = -1; |
| |
| for (intptr_t i = 0; i < case_count; ++i) { |
| case_expression_offsets[i] = ReaderOffset(); |
| int expression_count = ReadListLength(); // read number of expressions. |
| for (intptr_t j = 0; j < expression_count; ++j) { |
| ReadPosition(); // read jth position. |
| SkipExpression(); // read jth expression. |
| } |
| bool is_default = ReadBool(); // read is_default. |
| if (is_default) default_case = i; |
| Fragment& body_fragment = body_fragments[i] = |
| BuildStatement(); // read body. |
| |
| if (body_fragment.entry == NULL) { |
| // Make a NOP in order to ensure linking works properly. |
| body_fragment = NullConstant(); |
| body_fragment += Drop(); |
| } |
| |
| // The Dart language specification mandates fall-throughs in [SwitchCase]es |
| // to be runtime errors. |
| if (!is_default && body_fragment.is_open() && (i < (case_count - 1))) { |
| const Class& klass = Class::ZoneHandle( |
| Z, Library::LookupCoreClass(Symbols::FallThroughError())); |
| ASSERT(!klass.IsNull()); |
| |
| GrowableHandlePtrArray<const String> pieces(Z, 3); |
| pieces.Add(Symbols::FallThroughError()); |
| pieces.Add(Symbols::Dot()); |
| pieces.Add(H.DartSymbolObfuscate("_create")); |
| |
| const Function& constructor = Function::ZoneHandle( |
| Z, klass.LookupConstructorAllowPrivate(String::ZoneHandle( |
| Z, Symbols::FromConcatAll(H.thread(), pieces)))); |
| ASSERT(!constructor.IsNull()); |
| const String& url = H.DartString( |
| parsed_function()->function().ToLibNamePrefixedQualifiedCString(), |
| Heap::kOld); |
| |
| // Create instance of _FallThroughError |
| body_fragment += AllocateObject(TokenPosition::kNoSource, klass, 0); |
| LocalVariable* instance = MakeTemporary(); |
| |
| // Call _FallThroughError._create constructor. |
| body_fragment += LoadLocal(instance); |
| body_fragment += PushArgument(); // this |
| |
| body_fragment += Constant(url); |
| body_fragment += PushArgument(); // url |
| |
| body_fragment += NullConstant(); |
| body_fragment += PushArgument(); // line |
| |
| body_fragment += |
| StaticCall(TokenPosition::kNoSource, constructor, 3, ICData::kStatic); |
| body_fragment += Drop(); |
| |
| // Throw the exception |
| body_fragment += PushArgument(); |
| body_fragment += ThrowException(TokenPosition::kNoSource); |
| body_fragment += Drop(); |
| } |
| |
| // If there is an implicit fall-through we have one [SwitchCase] and |
| // multiple expressions, e.g. |
| // |
| // switch(expr) { |
| // case a: |
| // case b: |
| // <stmt-body> |
| // } |
| // |
| // This means that the <stmt-body> will have more than 1 incoming edge (one |
| // from `a == expr` and one from `a != expr && b == expr`). The |
| // `block.Destination()` records the additional jump. |
| if (expression_count > 1) { |
| block.DestinationDirect(i); |
| } |
| } |
| |
| intptr_t end_offset = ReaderOffset(); |
| |
| // Phase 2: Generate everything except the real bodies: |
| // * jump directly to a body (if there is no jumper) |
| // * jump to a wrapper block which jumps to the body (if there is a jumper) |
| Fragment current_instructions = head_instructions; |
| for (intptr_t i = 0; i < case_count; ++i) { |
| SetOffset(case_expression_offsets[i]); |
| int expression_count = ReadListLength(); // read length of expressions. |
| |
| if (i == default_case) { |
| ASSERT(i == (case_count - 1)); |
| |
| // Evaluate the conditions for the default [SwitchCase] just for the |
| // purpose of potentially triggering a compile-time error. |
| |
| for (intptr_t j = 0; j < expression_count; ++j) { |
| ReadPosition(); // read jth position. |
| // this reads the expression, but doesn't skip past it. |
| constant_evaluator_.EvaluateExpression(ReaderOffset()); |
| SkipExpression(); // read jth expression. |
| } |
| |
| if (block.HadJumper(i)) { |
| // There are several branches to the body, so we will make a goto to |
| // the join block (and prepend a join instruction to the real body). |
| JoinEntryInstr* join = block.DestinationDirect(i); |
| current_instructions += Goto(join); |
| |
| current_instructions = Fragment(current_instructions.entry, join); |
| current_instructions += body_fragments[i]; |
| } else { |
| current_instructions += body_fragments[i]; |
| } |
| } else { |
| JoinEntryInstr* body_join = NULL; |
| if (block.HadJumper(i)) { |
| body_join = block.DestinationDirect(i); |
| body_fragments[i] = Fragment(body_join) + body_fragments[i]; |
| } |
| |
| for (intptr_t j = 0; j < expression_count; ++j) { |
| TargetEntryInstr* then; |
| TargetEntryInstr* otherwise; |
| |
| TokenPosition position = ReadPosition(); // read jth position. |
| current_instructions += Constant(Instance::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpression(ReaderOffset()))); |
| SkipExpression(); // read jth expression. |
| current_instructions += PushArgument(); |
| current_instructions += LoadLocal(scopes()->switch_variable); |
| current_instructions += PushArgument(); |
| current_instructions += |
| InstanceCall(position, Symbols::EqualOperator(), Token::kEQ, |
| /*argument_count=*/2, |
| /*checked_argument_count=*/2); |
| current_instructions += BranchIfTrue(&then, &otherwise, false); |
| |
| Fragment then_fragment(then); |
| |
| if (body_join != NULL) { |
| // There are several branches to the body, so we will make a goto to |
| // the join block (the real body has already been prepended with a |
| // join instruction). |
| then_fragment += Goto(body_join); |
| } else { |
| // There is only a signle branch to the body, so we will just append |
| // the body fragment. |
| then_fragment += body_fragments[i]; |
| } |
| |
| current_instructions = Fragment(otherwise); |
| } |
| } |
| } |
| |
| if (case_count > 0 && default_case < 0) { |
| // There is no default, which means we have an open [current_instructions] |
| // (which is a [TargetEntryInstruction] for the last "otherwise" branch). |
| // |
| // Furthermore the last [SwitchCase] can be open as well. If so, we need |
| // to join these two. |
| Fragment& last_body = body_fragments[case_count - 1]; |
| if (last_body.is_open()) { |
| ASSERT(current_instructions.is_open()); |
| ASSERT(current_instructions.current->IsTargetEntry()); |
| |
| // Join the last "otherwise" branch and the last [SwitchCase] fragment. |
| JoinEntryInstr* join = BuildJoinEntry(); |
| current_instructions += Goto(join); |
| last_body += Goto(join); |
| |
| current_instructions = Fragment(join); |
| } |
| } else { |
| // All non-default cases will be closed (i.e. break/continue/throw/return) |
| // So it is fine to just let more statements after the switch append to the |
| // default case. |
| } |
| |
| delete[] body_fragments; |
| delete[] case_expression_offsets; |
| |
| SetOffset(end_offset); |
| return Fragment(head_instructions.entry, current_instructions.current); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildContinueSwitchStatement() { |
| TokenPosition position = ReadPosition(); // read position. |
| intptr_t target_index = ReadUInt(); // read target index. |
| |
| TryFinallyBlock* outer_finally = NULL; |
| intptr_t target_context_depth = -1; |
| JoinEntryInstr* entry = switch_block()->Destination( |
| target_index, &outer_finally, &target_context_depth); |
| |
| Fragment instructions; |
| instructions += |
| TranslateFinallyFinalizers(outer_finally, target_context_depth); |
| if (instructions.is_open()) { |
| if (NeedsDebugStepCheck(parsed_function()->function(), position)) { |
| instructions += DebugStepCheck(position); |
| } |
| instructions += Goto(entry); |
| } |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildIfStatement() { |
| bool negate; |
| ReadPosition(); // read position. |
| Fragment instructions = TranslateCondition(&negate); // read condition. |
| TargetEntryInstr* then_entry; |
| TargetEntryInstr* otherwise_entry; |
| instructions += BranchIfTrue(&then_entry, &otherwise_entry, negate); |
| |
| Fragment then_fragment(then_entry); |
| then_fragment += BuildStatement(); // read then. |
| |
| Fragment otherwise_fragment(otherwise_entry); |
| otherwise_fragment += BuildStatement(); // read otherwise. |
| |
| if (then_fragment.is_open()) { |
| if (otherwise_fragment.is_open()) { |
| JoinEntryInstr* join = BuildJoinEntry(); |
| then_fragment += Goto(join); |
| otherwise_fragment += Goto(join); |
| return Fragment(instructions.entry, join); |
| } else { |
| return Fragment(instructions.entry, then_fragment.current); |
| } |
| } else if (otherwise_fragment.is_open()) { |
| return Fragment(instructions.entry, otherwise_fragment.current); |
| } else { |
| return instructions.closed(); |
| } |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildReturnStatement() { |
| TokenPosition position = ReadPosition(); // read position. |
| Tag tag = ReadTag(); // read first part of expression. |
| |
| bool inside_try_finally = try_finally_block() != NULL; |
| |
| Fragment instructions = tag == kNothing |
| ? NullConstant() |
| : BuildExpression(); // read rest of expression. |
| |
| if (instructions.is_open()) { |
| if (inside_try_finally) { |
| ASSERT(scopes()->finally_return_variable != NULL); |
| const Function& function = parsed_function()->function(); |
| if (NeedsDebugStepCheck(function, position)) { |
| instructions += DebugStepCheck(position); |
| } |
| instructions += StoreLocal(position, scopes()->finally_return_variable); |
| instructions += Drop(); |
| instructions += TranslateFinallyFinalizers(NULL, -1); |
| if (instructions.is_open()) { |
| instructions += LoadLocal(scopes()->finally_return_variable); |
| instructions += Return(TokenPosition::kNoSource); |
| } |
| } else { |
| instructions += Return(position); |
| } |
| } else { |
| Pop(); |
| } |
| |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildTryCatch() { |
| InlineBailout("kernel::FlowgraphBuilder::VisitTryCatch"); |
| |
| intptr_t try_handler_index = AllocateTryIndex(); |
| Fragment try_body = TryCatch(try_handler_index); |
| JoinEntryInstr* after_try = BuildJoinEntry(); |
| |
| // Fill in the body of the try. |
| try_depth_inc(); |
| { |
| TryCatchBlock block(flow_graph_builder_, try_handler_index); |
| try_body += BuildStatement(); // read body. |
| try_body += Goto(after_try); |
| } |
| try_depth_dec(); |
| |
| const int kNeedsStracktraceBit = 1 << 0; |
| const int kIsSyntheticBit = 1 << 1; |
| |
| uint8_t flags = ReadByte(); |
| bool needs_stacktrace = |
| (flags & kNeedsStracktraceBit) == kNeedsStracktraceBit; |
| bool is_synthetic = (flags & kIsSyntheticBit) == kIsSyntheticBit; |
| |
| catch_depth_inc(); |
| intptr_t catch_count = ReadListLength(); // read number of catches. |
| const Array& handler_types = |
| Array::ZoneHandle(Z, Array::New(catch_count, Heap::kOld)); |
| |
| Fragment catch_body = CatchBlockEntry(handler_types, try_handler_index, |
| needs_stacktrace, is_synthetic); |
| // Fill in the body of the catch. |
| for (intptr_t i = 0; i < catch_count; ++i) { |
| intptr_t catch_offset = ReaderOffset(); // Catch has no tag. |
| TokenPosition position = ReadPosition(); // read position. |
| Tag tag = PeekTag(); // peek guard type. |
| AbstractType* type_guard = NULL; |
| if (tag != kDynamicType) { |
| type_guard = &T.BuildType(); // read guard. |
| handler_types.SetAt(i, *type_guard); |
| } else { |
| SkipDartType(); // read guard. |
| handler_types.SetAt(i, Object::dynamic_type()); |
| } |
| |
| Fragment catch_handler_body = EnterScope(catch_offset); |
| |
| tag = ReadTag(); // read first part of exception. |
| if (tag == kSomething) { |
| catch_handler_body += LoadLocal(CurrentException()); |
| catch_handler_body += |
| StoreLocal(TokenPosition::kNoSource, |
| LookupVariable(ReaderOffset() + data_program_offset_)); |
| catch_handler_body += Drop(); |
| SkipVariableDeclaration(); // read exception. |
| } |
| |
| tag = ReadTag(); // read first part of stack trace. |
| if (tag == kSomething) { |
| catch_handler_body += LoadLocal(CurrentStackTrace()); |
| catch_handler_body += |
| StoreLocal(TokenPosition::kNoSource, |
| LookupVariable(ReaderOffset() + data_program_offset_)); |
| catch_handler_body += Drop(); |
| SkipVariableDeclaration(); // read stack trace. |
| } |
| |
| { |
| CatchBlock block(flow_graph_builder_, CurrentException(), |
| CurrentStackTrace(), try_handler_index); |
| |
| catch_handler_body += BuildStatement(); // read body. |
| |
| // Note: ExitScope adjusts context_depth_ so even if catch_handler_body |
| // is closed we still need to execute ExitScope for its side effect. |
| catch_handler_body += ExitScope(catch_offset); |
| if (catch_handler_body.is_open()) { |
| catch_handler_body += Goto(after_try); |
| } |
| } |
| |
| if (type_guard != NULL) { |
| if (type_guard->IsMalformed()) { |
| catch_body += ThrowTypeError(); |
| catch_body += Drop(); |
| } else { |
| catch_body += LoadLocal(CurrentException()); |
| catch_body += PushArgument(); // exception |
| if (!type_guard->IsInstantiated(kCurrentClass)) { |
| catch_body += LoadInstantiatorTypeArguments(); |
| } else { |
| catch_body += NullConstant(); |
| } |
| catch_body += PushArgument(); // instantiator type arguments |
| if (!type_guard->IsInstantiated(kFunctions)) { |
| catch_body += LoadFunctionTypeArguments(); |
| } else { |
| catch_body += NullConstant(); |
| } |
| catch_body += PushArgument(); // function type arguments |
| catch_body += Constant(*type_guard); |
| catch_body += PushArgument(); // guard type |
| catch_body += InstanceCall( |
| position, Library::PrivateCoreLibName(Symbols::_instanceOf()), |
| Token::kIS, 4); |
| |
| TargetEntryInstr* catch_entry; |
| TargetEntryInstr* next_catch_entry; |
| catch_body += BranchIfTrue(&catch_entry, &next_catch_entry, false); |
| |
| Fragment(catch_entry) + catch_handler_body; |
| catch_body = Fragment(next_catch_entry); |
| } |
| } else { |
| catch_body += catch_handler_body; |
| } |
| } |
| |
| // In case the last catch body was not handling the exception and branching to |
| // after the try block, we will rethrow the exception (i.e. no default catch |
| // handler). |
| if (catch_body.is_open()) { |
| catch_body += LoadLocal(CurrentException()); |
| catch_body += PushArgument(); |
| catch_body += LoadLocal(CurrentStackTrace()); |
| catch_body += PushArgument(); |
| catch_body += RethrowException(TokenPosition::kNoSource, try_handler_index); |
| Drop(); |
| } |
| catch_depth_dec(); |
| |
| return Fragment(try_body.entry, after_try); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildTryFinally() { |
| // Note on streaming: |
| // We only stream this TryFinally if we can stream everything inside it, |
| // so creating a "TryFinallyBlock" with a kernel binary offset instead of an |
| // AST node isn't a problem. |
| |
| InlineBailout("kernel::FlowgraphBuilder::VisitTryFinally"); |
| |
| // There are 5 different cases where we need to execute the finally block: |
| // |
| // a) 1/2/3th case: Special control flow going out of `node->body()`: |
| // |
| // * [BreakStatement] transfers control to a [LabledStatement] |
| // * [ContinueSwitchStatement] transfers control to a [SwitchCase] |
| // * [ReturnStatement] returns a value |
| // |
| // => All three cases will automatically append all finally blocks |
| // between the branching point and the destination (so we don't need to |
| // do anything here). |
| // |
| // b) 4th case: Translating the body resulted in an open fragment (i.e. body |
| // executes without any control flow out of it) |
| // |
| // => We are responsible for jumping out of the body to a new block (with |
| // different try index) and execute the finalizer. |
| // |
| // c) 5th case: An exception occurred inside the body. |
| // |
| // => We are responsible for catching it, executing the finally block and |
| // rethrowing the exception. |
| intptr_t try_handler_index = AllocateTryIndex(); |
| Fragment try_body = TryCatch(try_handler_index); |
| JoinEntryInstr* after_try = BuildJoinEntry(); |
| |
| intptr_t offset = ReaderOffset(); |
| SkipStatement(); // temporarily read body. |
| intptr_t finalizer_offset = ReaderOffset(); |
| SetOffset(offset); |
| |
| // Fill in the body of the try. |
| try_depth_inc(); |
| { |
| TryFinallyBlock tfb(flow_graph_builder_, finalizer_offset); |
| TryCatchBlock tcb(flow_graph_builder_, try_handler_index); |
| try_body += BuildStatement(); // read body. |
| } |
| try_depth_dec(); |
| |
| if (try_body.is_open()) { |
| // Please note: The try index will be on level out of this block, |
| // thereby ensuring if there's an exception in the finally block we |
| // won't run it twice. |
| JoinEntryInstr* finally_entry = BuildJoinEntry(); |
| |
| try_body += Goto(finally_entry); |
| |
| Fragment finally_body(finally_entry); |
| finally_body += BuildStatement(); // read finalizer. |
| finally_body += Goto(after_try); |
| } |
| |
| // Fill in the body of the catch. |
| catch_depth_inc(); |
| const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld)); |
| handler_types.SetAt(0, Object::dynamic_type()); |
| // Note: rethrow will actually force mark the handler as needing a stacktrace. |
| Fragment finally_body = CatchBlockEntry(handler_types, try_handler_index, |
| /* needs_stacktrace = */ false, |
| /* is_synthesized = */ true); |
| SetOffset(finalizer_offset); |
| finally_body += BuildStatement(); // read finalizer |
| if (finally_body.is_open()) { |
| finally_body += LoadLocal(CurrentException()); |
| finally_body += PushArgument(); |
| finally_body += LoadLocal(CurrentStackTrace()); |
| finally_body += PushArgument(); |
| finally_body += |
| RethrowException(TokenPosition::kNoSource, try_handler_index); |
| Drop(); |
| } |
| catch_depth_dec(); |
| |
| return Fragment(try_body.entry, after_try); |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildYieldStatement() { |
| TokenPosition position = ReadPosition(); // read position. |
| uint8_t flags = ReadByte(); // read flags. |
| |
| ASSERT(flags == kNativeYieldFlags); // Must have been desugared. |
| |
| // Setup yield/continue point: |
| // |
| // ... |
| // :await_jump_var = index; |
| // :await_ctx_var = :current_context_var |
| // return <expr> |
| // |
| // Continuation<index>: |
| // Drop(1) |
| // ... |
| // |
| // BuildGraphOfFunction will create a dispatch that jumps to |
| // Continuation<:await_jump_var> upon entry to the function. |
| // |
| Fragment instructions = IntConstant(yield_continuations().length() + 1); |
| instructions += |
| StoreLocal(TokenPosition::kNoSource, scopes()->yield_jump_variable); |
| instructions += Drop(); |
| instructions += LoadLocal(parsed_function()->current_context_var()); |
| instructions += |
| StoreLocal(TokenPosition::kNoSource, scopes()->yield_context_variable); |
| instructions += Drop(); |
| instructions += BuildExpression(); // read expression. |
| instructions += Return(TokenPosition::kNoSource); |
| |
| // Note: DropTempsInstr serves as an anchor instruction. It will not |
| // be linked into the resulting graph. |
| DropTempsInstr* anchor = new (Z) DropTempsInstr(0, NULL); |
| yield_continuations().Add(YieldContinuation(anchor, CurrentTryIndex())); |
| |
| Fragment continuation(instructions.entry, anchor); |
| |
| if (parsed_function()->function().IsAsyncClosure() || |
| parsed_function()->function().IsAsyncGenClosure()) { |
| // If function is async closure or async gen closure it takes three |
| // parameters where the second and the third are exception and stack_trace. |
| // Check if exception is non-null and rethrow it. |
| // |
| // :async_op([:result, :exception, :stack_trace]) { |
| // ... |
| // Continuation<index>: |
| // if (:exception != null) rethrow(:exception, :stack_trace); |
| // ... |
| // } |
| // |
| LocalScope* scope = parsed_function()->node_sequence()->scope(); |
| LocalVariable* exception_var = scope->VariableAt(2); |
| LocalVariable* stack_trace_var = scope->VariableAt(3); |
| ASSERT(exception_var->name().raw() == Symbols::ExceptionParameter().raw()); |
| ASSERT(stack_trace_var->name().raw() == |
| Symbols::StackTraceParameter().raw()); |
| |
| TargetEntryInstr* no_error; |
| TargetEntryInstr* error; |
| |
| continuation += LoadLocal(exception_var); |
| continuation += BranchIfNull(&no_error, &error); |
| |
| Fragment rethrow(error); |
| rethrow += LoadLocal(exception_var); |
| rethrow += PushArgument(); |
| rethrow += LoadLocal(stack_trace_var); |
| rethrow += PushArgument(); |
| rethrow += RethrowException(position, CatchClauseNode::kInvalidTryIndex); |
| Drop(); |
| |
| continuation = Fragment(continuation.entry, no_error); |
| } |
| |
| return continuation; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildVariableDeclaration() { |
| intptr_t kernel_position_no_tag = ReaderOffset() + data_program_offset_; |
| LocalVariable* variable = LookupVariable(kernel_position_no_tag); |
| |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
| String& name = H.DartSymbolObfuscate(helper.name_index_); |
| AbstractType& type = T.BuildType(); // read type. |
| Tag tag = ReadTag(); // read (first part of) initializer. |
| |
| Fragment instructions; |
| if (tag == kNothing) { |
| instructions += NullConstant(); |
| } else { |
| if (helper.IsConst()) { |
| const Instance& constant_value = Instance::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpression( |
| ReaderOffset())); // read initializer form current position. |
| variable->SetConstValue(constant_value); |
| instructions += Constant(constant_value); |
| SkipExpression(); // skip initializer. |
| } else { |
| // Initializer |
| instructions += BuildExpression(); // read (actual) initializer. |
| instructions += CheckVariableTypeInCheckedMode(type, name); |
| } |
| } |
| |
| // Use position of equal sign if it exists. If the equal sign does not exist |
| // use the position of the identifier. |
| TokenPosition debug_position = |
| Utils::Maximum(helper.position_, helper.equals_position_); |
| if (NeedsDebugStepCheck(stack(), debug_position)) { |
| instructions = DebugStepCheck(debug_position) + instructions; |
| } |
| instructions += StoreLocal(helper.position_, variable); |
| instructions += Drop(); |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildFunctionDeclaration() { |
| TokenPosition position = ReadPosition(); // read position. |
| intptr_t variable_offset = ReaderOffset() + data_program_offset_; |
| |
| // read variable declaration. |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd); |
| |
| Fragment instructions = DebugStepCheck(position); |
| instructions += BuildFunctionNode(position, helper.name_index_); |
| instructions += StoreLocal(position, LookupVariable(variable_offset)); |
| instructions += Drop(); |
| return instructions; |
| } |
| |
| Fragment StreamingFlowGraphBuilder::BuildFunctionNode( |
| TokenPosition parent_position, |
| StringIndex name_index) { |
| intptr_t offset = ReaderOffset(); |
| |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
| TokenPosition position = function_node_helper.position_; |
| |
| bool declaration = name_index >= 0; |
| |
| if (declaration) { |
| position = parent_position; |
| } |
| if (!position.IsReal()) { |
| // Positions has to be unique in regards to the parent. |
| // A non-real at this point is probably -1, we cannot blindly use that |
| // as others might use it too. Create a new dummy non-real TokenPosition. |
| position = TokenPosition(offset).ToSynthetic(); |
| } |
| |
| // The VM has a per-isolate table of functions indexed by the enclosing |
| // function and token position. |
| Function& function = Function::ZoneHandle(Z); |
| |
| // NOTE: This is not TokenPosition in the general sense! |
| function = I->LookupClosureFunction(parsed_function()->function(), position); |
| if (function.IsNull()) { |
| for (intptr_t i = 0; i < scopes()->function_scopes.length(); ++i) { |
| if (scopes()->function_scopes[i].kernel_offset != offset) { |
| continue; |
| } |
| |
| const String* name; |
| if (declaration) { |
| name = &H.DartSymbolObfuscate(name_index); |
| } else { |
| name = &Symbols::AnonymousClosure(); |
| } |
| // NOTE: This is not TokenPosition in the general sense! |
| function = Function::NewClosureFunction( |
| *name, parsed_function()->function(), position); |
| |
| function.set_is_debuggable(function_node_helper.dart_async_marker_ == |
| FunctionNodeHelper::kSync); |
| switch (function_node_helper.dart_async_marker_) { |
| case FunctionNodeHelper::kSyncStar: |
| function.set_modifier(RawFunction::kSyncGen); |
| break; |
| case FunctionNodeHelper::kAsync: |
| function.set_modifier(RawFunction::kAsync); |
| function.set_is_inlinable(!FLAG_causal_async_stacks); |
| break; |
| case FunctionNodeHelper::kAsyncStar: |
| function.set_modifier(RawFunction::kAsyncGen); |
| function.set_is_inlinable(!FLAG_causal_async_stacks); |
| break; |
| default: |
| // no special modifier |
| break; |
| } |
| function.set_is_generated_body(function_node_helper.async_marker_ == |
| FunctionNodeHelper::kSyncYielding); |
| if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) { |
| function.set_is_inlinable(!FLAG_causal_async_stacks); |
| } |
| |
| function.set_end_token_pos(function_node_helper.end_position_); |
| LocalScope* scope = scopes()->function_scopes[i].scope; |
| const ContextScope& context_scope = ContextScope::Handle( |
| Z, scope->PreserveOuterScope(flow_graph_builder_->context_depth_)); |
| function.set_context_scope(context_scope); |
| function.set_kernel_offset(offset); |
| SetupFunctionParameters(active_class(), Class::Handle(Z), function, |
| false, // is_method |
| true, // is_closure |
| &function_node_helper); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd); |
| |
| // Finalize function type. |
| Type& signature_type = Type::Handle(Z, function.SignatureType()); |
| signature_type ^= |
| ClassFinalizer::FinalizeType(*active_class()->klass, signature_type); |
| function.SetSignatureType(signature_type); |
| |
| I->AddClosureFunction(function); |
| break; |
| } |
| } |
| |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd); |
| |
| const Class& closure_class = |
| Class::ZoneHandle(Z, I->object_store()->closure_class()); |
| ASSERT(!closure_class.IsNull()); |
| Fragment instructions = |
| flow_graph_builder_->AllocateObject(closure_class, function); |
| LocalVariable* closure = MakeTemporary(); |
| |
| // The function signature can have uninstantiated class type parameters. |
| if (!function.HasInstantiatedSignature(kCurrentClass)) { |
| instructions += LoadLocal(closure); |
| instructions += LoadInstantiatorTypeArguments(); |
| instructions += flow_graph_builder_->StoreInstanceField( |
| TokenPosition::kNoSource, |
| Closure::instantiator_type_arguments_offset()); |
| } |
| |
| // TODO(30455): We only need to save these if the closure uses any captured |
| // type parameters. |
| instructions += LoadLocal(closure); |
| instructions += LoadFunctionTypeArguments(); |
| instructions += flow_graph_builder_->StoreInstanceField( |
| TokenPosition::kNoSource, Closure::function_type_arguments_offset()); |
| |
| instructions += LoadLocal(closure); |
| instructions += Constant(Object::empty_type_arguments()); |
| instructions += flow_graph_builder_->StoreInstanceField( |
| TokenPosition::kNoSource, Closure::delayed_type_arguments_offset()); |
| |
| // Store the function and the context in the closure. |
| instructions += LoadLocal(closure); |
| instructions += Constant(function); |
| instructions += flow_graph_builder_->StoreInstanceField( |
| TokenPosition::kNoSource, Closure::function_offset()); |
| |
| instructions += LoadLocal(closure); |
| instructions += LoadLocal(parsed_function()->current_context_var()); |
| instructions += flow_graph_builder_->StoreInstanceField( |
| TokenPosition::kNoSource, Closure::context_offset()); |
| |
| return instructions; |
| } |
| |
| void StreamingFlowGraphBuilder::LoadAndSetupTypeParameters( |
| ActiveClass* active_class, |
| const Object& set_on, |
| intptr_t type_parameter_count, |
| const Function& parameterized_function) { |
| ASSERT(type_parameter_count >= 0); |
| if (type_parameter_count == 0) { |
| return; |
| } |
| ASSERT(set_on.IsClass() || set_on.IsFunction()); |
| bool set_on_class = set_on.IsClass(); |
| ASSERT(set_on_class == parameterized_function.IsNull()); |
| |
| // First setup the type parameters, so if any of the following code uses it |
| // (in a recursive way) we're fine. |
| TypeArguments& type_parameters = TypeArguments::Handle(Z); |
| TypeParameter& parameter = TypeParameter::Handle(Z); |
| const Type& null_bound = Type::Handle(Z); |
| |
| // Step a) Create array of [TypeParameter] objects (without bound). |
| type_parameters = TypeArguments::New(type_parameter_count); |
| { |
| AlternativeReadingScope alt(&reader_); |
| for (intptr_t i = 0; i < type_parameter_count; i++) { |
| TypeParameterHelper helper(this); |
| helper.Finish(); |
| parameter = TypeParameter::New( |
| set_on_class ? *active_class->klass : Class::Handle(Z), |
| parameterized_function, i, |
| H.DartSymbolObfuscate(helper.name_index_), // read ith name index. |
| null_bound, TokenPosition::kNoSource); |
| type_parameters.SetTypeAt(i, parameter); |
| } |
| } |
| |
| if (set_on.IsClass()) { |
| Class::Cast(set_on).set_type_parameters(type_parameters); |
| } else { |
| Function::Cast(set_on).set_type_parameters(type_parameters); |
| } |
| |
| const Function* enclosing = NULL; |
| if (!parameterized_function.IsNull()) { |
| enclosing = ¶meterized_function; |
| } |
| ActiveTypeParametersScope scope(active_class, enclosing, type_parameters, Z); |
| |
| // Step b) Fill in the bounds of all [TypeParameter]s. |
| for (intptr_t i = 0; i < type_parameter_count; i++) { |
| TypeParameterHelper helper(this); |
| helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound); |
| |
| // TODO(github.com/dart-lang/kernel/issues/42): This should be handled |
| // by the frontend. |
| parameter ^= type_parameters.TypeAt(i); |
| const Tag tag = PeekTag(); // peek ith bound type. |
| if (tag == kDynamicType) { |
| SkipDartType(); // read ith bound. |
| parameter.set_bound(Type::Handle(Z, I->object_store()->object_type())); |
| } else { |
| AbstractType& bound = |
| T.BuildTypeWithoutFinalization(); // read ith bound. |
| if (bound.IsMalformedOrMalbounded()) { |
| bound = I->object_store()->object_type(); |
| } |
| parameter.set_bound(bound); |
| } |
| |
| helper.Finish(); |
| } |
| } |
| |
| void StreamingFlowGraphBuilder::SetupFunctionParameters( |
| ActiveClass* active_class, |
| const Class& klass, |
| const Function& function, |
| bool is_method, |
| bool is_closure, |
| FunctionNodeHelper* function_node_helper) { |
| ASSERT(!(is_method && is_closure)); |
| bool is_factory = function.IsFactory(); |
| intptr_t extra_parameters = (is_method || is_closure || is_factory) ? 1 : 0; |
| |
| if (!is_factory) { |
| LoadAndSetupTypeParameters(active_class, function, ReadListLength(), |
| function); |
| function_node_helper->SetJustRead(FunctionNodeHelper::kTypeParameters); |
| } |
| |
| ActiveTypeParametersScope scope( |
| active_class, &function, |
| TypeArguments::Handle(Z, function.type_parameters()), Z); |
| |
| 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 = ReadListLength(); // read list length. |
| |
| intptr_t named_parameter_count = |
| total_parameter_count - positional_parameter_count; |
| |
| function.set_num_fixed_parameters(extra_parameters + |
| required_parameter_count); |
| if (named_parameter_count > 0) { |
| function.SetNumOptionalParameters(named_parameter_count, false); |
| } else { |
| function.SetNumOptionalParameters( |
| positional_parameter_count - required_parameter_count, true); |
| } |
| intptr_t parameter_count = extra_parameters + total_parameter_count; |
| function.set_parameter_types( |
| Array::Handle(Z, Array::New(parameter_count, Heap::kOld))); |
| function.set_parameter_names( |
| Array::Handle(Z, Array::New(parameter_count, Heap::kOld))); |
| intptr_t pos = 0; |
| if (is_method) { |
| ASSERT(!klass.IsNull()); |
| function.SetParameterTypeAt(pos, H.GetCanonicalType(klass)); |
| function.SetParameterNameAt(pos, Symbols::This()); |
| pos++; |
| } else if (is_closure) { |
| function.SetParameterTypeAt(pos, AbstractType::dynamic_type()); |
| function.SetParameterNameAt(pos, Symbols::ClosureParameter()); |
| pos++; |
| } else if (is_factory) { |
| function.SetParameterTypeAt(pos, AbstractType::dynamic_type()); |
| function.SetParameterNameAt(pos, Symbols::TypeArgumentsParameter()); |
| pos++; |
| } |
| |
| for (intptr_t i = 0; i < positional_parameter_count; ++i, ++pos) { |
| // Read ith variable declaration. |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
| const AbstractType& type = T.BuildTypeWithoutFinalization(); // read type. |
| Tag tag = ReadTag(); // read (first part of) initializer. |
| if (tag == kSomething) { |
| SkipExpression(); // read (actual) initializer. |
| } |
| |
| function.SetParameterTypeAt( |
| pos, type.IsMalformed() ? Type::dynamic_type() : type); |
| function.SetParameterNameAt(pos, H.DartSymbolObfuscate(helper.name_index_)); |
| } |
| |
| intptr_t named_parameter_count_check = ReadListLength(); // read list length. |
| ASSERT(named_parameter_count_check == named_parameter_count); |
| for (intptr_t i = 0; i < named_parameter_count; ++i, ++pos) { |
| // Read ith variable declaration. |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
| const AbstractType& type = T.BuildTypeWithoutFinalization(); // read type. |
| Tag tag = ReadTag(); // read (first part of) initializer. |
| if (tag == kSomething) { |
| SkipExpression(); // read (actual) initializer. |
| } |
| |
| function.SetParameterTypeAt( |
| pos, type.IsMalformed() ? Type::dynamic_type() : type); |
| function.SetParameterNameAt(pos, H.DartSymbolObfuscate(helper.name_index_)); |
| } |
| |
| function_node_helper->SetJustRead(FunctionNodeHelper::kNamedParameters); |
| |
| // The result type for generative constructors has already been set. |
| if (!function.IsGenerativeConstructor()) { |
| const AbstractType& return_type = |
| T.BuildTypeWithoutFinalization(); // read return type. |
| function.set_result_type(return_type.IsMalformed() ? Type::dynamic_type() |
| : return_type); |
| function_node_helper->SetJustRead(FunctionNodeHelper::kReturnType); |
| } |
| } |
| |
| RawObject* StreamingFlowGraphBuilder::BuildParameterDescriptor( |
| intptr_t kernel_offset) { |
| SetOffset(kernel_offset); |
| ReadUntilFunctionNode(); // read until function node. |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding( |
| FunctionNodeHelper::kPositionalParameters); |
| intptr_t param_count = function_node_helper.total_parameter_count_; |
| intptr_t positional_count = ReadListLength(); // read list length. |
| intptr_t named_parameter_count = param_count - positional_count; |
| |
| const Array& param_descriptor = Array::Handle( |
| Array::New(param_count * Parser::kParameterEntrySize, Heap::kOld)); |
| for (intptr_t i = 0; i < param_count; ++i) { |
| const intptr_t entry_start = i * Parser::kParameterEntrySize; |
| |
| if (i == positional_count) { |
| intptr_t named_parameter_count_check = |
| ReadListLength(); // read list length. |
| ASSERT(named_parameter_count_check == named_parameter_count); |
| } |
| |
| // Read ith variable declaration. |
| VariableDeclarationHelper helper(this); |
| helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
| param_descriptor.SetAt(entry_start + Parser::kParameterIsFinalOffset, |
| helper.IsFinal() ? Bool::True() : Bool::False()); |
| |
| Tag tag = ReadTag(); // read (first part of) initializer. |
| if (tag == kSomething) { |
| // this will (potentially) read the initializer, but reset the position. |
| Instance& constant = Instance::ZoneHandle( |
| Z, constant_evaluator_.EvaluateExpression(ReaderOffset())); |
| SkipExpression(); // read (actual) initializer. |
| param_descriptor.SetAt(entry_start + Parser::kParameterDefaultValueOffset, |
| constant); |
| } else { |
| param_descriptor.SetAt(entry_start + Parser::kParameterDefaultValueOffset, |
| Object::null_instance()); |
| } |
| |
| param_descriptor.SetAt(entry_start + Parser::kParameterMetadataOffset, |
| /* Issue(28434): Missing parameter metadata. */ |
| Object::null_instance()); |
| } |
| return param_descriptor.raw(); |
| } |
| |
| RawObject* StreamingFlowGraphBuilder::EvaluateMetadata( |
| intptr_t kernel_offset, |
| const Class& owner_class) { |
| SetOffset(kernel_offset); |
| const Tag tag = PeekTag(); |
| |
| // Setup active_class in type translator for type finalization. |
| ActiveClass active_class; |
| active_class.klass = &owner_class; |
| type_translator_.set_active_class(&active_class); |
| |
| if (tag == kClass) { |
| ClassHelper class_helper(this); |
| class_helper.ReadUntilExcluding(ClassHelper::kAnnotations); |
| } else if (tag == kProcedure) { |
| ProcedureHelper procedure_helper(this); |
| procedure_helper.ReadUntilExcluding(ProcedureHelper::kAnnotations); |
| } else if (tag == kField) { |
| FieldHelper field_helper(this); |
| field_helper.ReadUntilExcluding(FieldHelper::kAnnotations); |
| } else if (tag == kConstructor) { |
| ConstructorHelper constructor_helper(this); |
| constructor_helper.ReadUntilExcluding(ConstructorHelper::kAnnotations); |
| } else { |
| FATAL("No support for metadata on this type of kernel node\n"); |
| } |
| |
| intptr_t list_length = ReadListLength(); // read list length. |
| const Array& metadata_values = |
| Array::Handle(Z, Array::New(list_length, H.allocation_space())); |
| Instance& value = Instance::Handle(Z); |
| for (intptr_t i = 0; i < list_length; ++i) { |
| // this will (potentially) read the expression, but reset the position. |
| value = constant_evaluator_.EvaluateExpression(ReaderOffset()); |
| SkipExpression(); // read (actual) initializer. |
| metadata_values.SetAt(i, value); |
| } |
| |
| type_translator_.set_active_class(NULL); |
| |
| return metadata_values.raw(); |
| } |
| |
| void StreamingFlowGraphBuilder::CollectTokenPositionsFor( |
| intptr_t script_index, |
| intptr_t initial_script_index, |
| intptr_t kernel_offset, |
| GrowableArray<intptr_t>* record_token_positions_in, |
| GrowableArray<intptr_t>* record_yield_positions_in) { |
| record_token_positions_into_ = record_token_positions_in; |
| record_yield_positions_into_ = record_yield_positions_in; |
| record_for_script_id_ = script_index; |
| current_script_id_ = initial_script_index; |
| |
| SetOffset(kernel_offset); |
| |
| const Tag tag = PeekTag(); |
| if (tag == kProcedure) { |
| ProcedureHelper procedure_helper(this); |
| procedure_helper.ReadUntilExcluding(ProcedureHelper::kEnd); |
| } else if (tag == kConstructor) { |
| ConstructorHelper constructor_helper(this); |
| constructor_helper.ReadUntilExcluding(ConstructorHelper::kEnd); |
| } else if (tag == kFunctionNode) { |
| FunctionNodeHelper function_node_helper(this); |
| function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd); |
| } else if (tag == kField) { |
| FieldHelper field_helper(this); |
| field_helper.ReadUntilExcluding(FieldHelper::kEnd); |
| } else if (tag == kClass) { |
| ClassHelper class_helper(this); |
| class_helper.ReadUntilExcluding(ClassHelper::kEnd); |
| } else { |
| ReportUnexpectedTag("a class or a member", tag); |
| UNREACHABLE(); |
| } |
| |
| record_token_positions_into_ = NULL; |
| record_yield_positions_into_ = NULL; |
| record_for_script_id_ = -1; |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::SourceTableSize() { |
| AlternativeReadingScope alt(&reader_); |
| intptr_t library_count = reader_.ReadFromIndexNoReset( |
| reader_.size(), LibraryCountFieldCountFromEnd, 1, 0); |
| intptr_t source_table_offset = reader_.ReadFromIndexNoReset( |
| reader_.size(), |
| LibraryCountFieldCountFromEnd + 1 + library_count + 1 + |
| SourceTableFieldCountFromFirstLibraryOffset, |
| 1, 0); |
| SetOffset(source_table_offset); // read source table offset. |
| return reader_.ReadUInt32(); // read source table size. |
| } |
| |
| intptr_t StreamingFlowGraphBuilder::GetOffsetForSourceInfo(intptr_t index) { |
| AlternativeReadingScope alt(&reader_); |
| intptr_t library_count = reader_.ReadFromIndexNoReset( |
| reader_.size(), LibraryCountFieldCountFromEnd, 1, 0); |
| intptr_t source_table_offset = reader_.ReadFromIndexNoReset( |
| reader_.size(), |
| LibraryCountFieldCountFromEnd + 1 + library_count + 1 + |
| SourceTableFieldCountFromFirstLibraryOffset, |
| 1, 0); |
| intptr_t next_field_offset = reader_.ReadUInt32(); |
| SetOffset(source_table_offset); |
| intptr_t size = reader_.ReadUInt32(); // read source table size. |
| |
| return reader_.ReadFromIndexNoReset(next_field_offset, 0, size, index); |
| } |
| |
| String& StreamingFlowGraphBuilder::SourceTableUriFor(intptr_t index) { |
| AlternativeReadingScope alt(&reader_); |
| SetOffset(GetOffsetForSourceInfo(index)); |
| intptr_t size = ReadUInt(); // read uri List<byte> size. |
| return H.DartString(reader_.CopyDataIntoZone(Z, ReaderOffset(), size), size, |
| Heap::kOld); |
| } |
| |
| String& StreamingFlowGraphBuilder::GetSourceFor(intptr_t index) { |
| AlternativeReadingScope alt(&reader_); |
| SetOffset(GetOffsetForSourceInfo(index)); |
| SkipBytes(ReadUInt()); // skip uri. |
| intptr_t size = ReadUInt(); // read source List<byte> size. |
| return H.DartString(reader_.CopyDataIntoZone(Z, ReaderOffset(), size), size, |
| Heap::kOld); |
| } |
| |
| RawTypedData* StreamingFlowGraphBuilder::GetLineStartsFor(intptr_t index) { |
| // Line starts are delta encoded. So get the max delta first so that we |
| // can store them as tighly as possible. |
| AlternativeReadingScope alt(&reader_); |
| SetOffset(GetOffsetForSourceInfo(index)); |
| SkipBytes(ReadUInt()); // skip uri. |
| SkipBytes(ReadUInt()); // skip source. |
| const intptr_t line_start_count = ReadUInt(); // read number of line start |
| // entries. |
| MallocGrowableArray<int32_t> line_starts_array; |
| |
| intptr_t max_delta = 0; |
| for (intptr_t i = 0; i < line_start_count; ++i) { |
| int32_t delta = ReadUInt(); |
| line_starts_array.Add(delta); |
| if (delta > max_delta) { |
| max_delta = delta; |
| } |
| } |
| |
| intptr_t cid; |
| if (max_delta <= kMaxInt8) { |
| cid = kTypedDataInt8ArrayCid; |
| } else if (max_delta <= kMaxInt16) { |
| cid = kTypedDataInt16ArrayCid; |
| } else { |
| cid = kTypedDataInt32ArrayCid; |
| } |
| |
| TypedData& line_starts_data = |
| TypedData::Handle(Z, TypedData::New(cid, line_start_count, Heap::kOld)); |
| for (intptr_t j = 0; j < line_start_count; ++j) { |
| int32_t line_start = line_starts_array[j]; |
| switch (cid) { |
| case kTypedDataInt8ArrayCid: |
| line_starts_data.SetInt8(j, static_cast<int8_t>(line_start)); |
| break; |
| case kTypedDataInt16ArrayCid: |
| line_starts_data.SetInt16(j << 1, static_cast<int16_t>(line_start)); |
| break; |
| case kTypedDataInt32ArrayCid: |
| line_starts_data.SetInt32(j << 2, line_start); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| return line_starts_data.raw(); |
| } |
| |
| void StreamingFlowGraphBuilder::EnsureMetadataIsScanned() { |
| // Scan metadata mappings only once. |
| if (metadata_scanned_) { |
| return; |
| } |
| metadata_scanned_ = true; |
| |
| const intptr_t kUInt32Size = 4; |
| Reader reader(H.metadata_mappings()); |
| if (reader.size() == 0) { |
| return; |
| } |
| |
| // Scan through metadata mappings in reverse direction. |
| |
| // Read metadataMappings length. |
| intptr_t offset = reader.size() - kUInt32Size; |
| uint32_t metadata_num = reader.ReadUInt32At(offset); |
| |
| if (metadata_num == 0) { |
| ASSERT(H.metadata_mappings().LengthInBytes() == kUInt32Size); |
| return; |
| } |
| |
| // Read metadataMappings elements. |
| for (uint32_t i = 0; i < metadata_num; ++i) { |
| // Read nodeOffsetToMetadataOffset length. |
| offset -= kUInt32Size; |
| uint32_t mappings_num = reader.ReadUInt32At(offset); |
| |
| // Skip nodeOffsetToMetadataOffset and read tag. |
| offset -= mappings_num * 2 * kUInt32Size + kUInt32Size; |
| StringIndex tag = StringIndex(reader.ReadUInt32At(offset)); |
| |
| if (mappings_num == 0) { |
| continue; |
| } |
| |
| // Check recognized metadata |
| if (H.StringEquals(tag, DirectCallMetadataHelper::tag())) { |
| if (!FLAG_precompiled_mode) { |
| FATAL("DirectCallMetadata is allowed in precompiled mode only"); |
| } |
| direct_call_metadata_helper_.SetMetadataMappings(offset + kUInt32Size, |
| mappings_num); |
| } else if (H.StringEquals(tag, InferredTypeMetadataHelper::tag())) { |
| if (!FLAG_precompiled_mode) { |
| FATAL("InferredTypeMetadata is allowed in precompiled mode only"); |
| } |
| inferred_type_metadata_helper_.SetMetadataMappings(offset + kUInt32Size, |
| mappings_num); |
| } else if (H.StringEquals(tag, ProcedureAttributesMetadataHelper::tag())) { |
| if (!FLAG_precompiled_mode) { |
| FATAL( |
| "ProcedureAttributesMetadata is allowed in precompiled mode " |
| "only"); |
| } |
| procedure_attributes_metadata_helper_.SetMetadataMappings( |
| offset + kUInt32Size, mappings_num); |
| } else if (H.StringEquals(tag, BytecodeMetadataHelper::tag())) { |
| bytecode_metadata_helper_.SetMetadataMappings(offset + kUInt32Size, |
| mappings_num); |
| } |
| } |
| } |
| |
| const Array& ConstantHelper::ReadConstantTable() { |
| const intptr_t number_of_constants = builder_.ReadUInt(); |
| if (number_of_constants == 0) { |
| return Array::Handle(Z, Array::null()); |
| } |
| |
| const Library& corelib = Library::Handle(Z, Library::CoreLibrary()); |
| const Class& list_class = |
| Class::Handle(Z, corelib.LookupClassAllowPrivate(Symbols::_List())); |
| |
| // Eagerly finalize _ImmutableList (instead of doing it on every list |
| // constant). |
| temp_class_ = I->class_table()->At(kImmutableArrayCid); |
| temp_object_ = temp_class_.EnsureIsFinalized(H.thread()); |
| ASSERT(temp_object_.IsNull()); |
| |
| KernelConstantsMap constants( |
| HashTables::New<KernelConstantsMap>(number_of_constants, Heap::kOld)); |
| |
| const intptr_t start_offset = builder_.ReaderOffset(); |
| |
| for (intptr_t i = 0; i < number_of_constants; ++i) { |
| const intptr_t offset = builder_.ReaderOffset(); |
| const intptr_t constant_tag = builder_.ReadByte(); |
| switch (constant_tag) { |
| case kNullConstant: |
| temp_instance_ = Instance::null(); |
| break; |
| case kBoolConstant: |
| temp_instance_ = builder_.ReadByte() == 1 ? Object::bool_true().raw() |
| : Object::bool_false().raw(); |
| break; |
| case kIntConstant: { |
| temp_instance_ = const_evaluator_.EvaluateExpression( |
| builder_.ReaderOffset(), false /* reset position */); |
| break; |
| } |
| case kDoubleConstant: { |
| temp_instance_ = Double::New(builder_.ReadDouble(), Heap::kOld); |
| temp_instance_ = H.Canonicalize(temp_instance_); |
| break; |
| } |
| case kStringConstant: { |
| temp_instance_ = |
| H.Canonicalize(H.DartString(builder_.ReadStringReference())); |
| break; |
| } |
| case kListConstant: { |
| temp_type_arguments_ = TypeArguments::New(1, Heap::kOld); |
| const AbstractType& type = type_translator_.BuildType(); |
| temp_type_arguments_.SetTypeAt(0, type); |
| InstantiateTypeArguments(list_class, &temp_type_arguments_); |
| |
| const intptr_t length = builder_.ReadUInt(); |
| temp_array_ = ImmutableArray::New(length, Heap::kOld); |
| temp_array_.SetTypeArguments(temp_type_arguments_); |
| for (intptr_t j = 0; j < length; ++j) { |
| const intptr_t entry_offset = builder_.ReadUInt(); |
| ASSERT(entry_offset < offset); // We have a DAG! |
| temp_object_ = constants.GetOrDie(entry_offset); |
| temp_array_.SetAt(j, temp_object_); |
| } |
| |
| temp_instance_ = H.Canonicalize(temp_array_); |
| break; |
| } |
| case kInstanceConstant: { |
| const NameIndex index = builder_.ReadCanonicalNameReference(); |
| if (ShouldSkipConstant(index)) { |
| temp_instance_ = Instance::null(); |
| break; |
| } |
| |
| temp_class_ = H.LookupClassByKernelClass(index); |
| temp_object_ = temp_class_.EnsureIsFinalized(H.thread()); |
| ASSERT(temp_object_.IsNull()); |
| |
| temp_instance_ = Instance::New(temp_class_, Heap::kOld); |
| |
| const intptr_t number_of_type_arguments = builder_.ReadUInt(); |
| if (temp_class_.NumTypeArguments() > 0) { |
| temp_type_arguments_ = |
| TypeArguments::New(number_of_type_arguments, Heap::kOld); |
| for (intptr_t j = 0; j < number_of_type_arguments; ++j) { |
| temp_type_arguments_.SetTypeAt(j, type_translator_.BuildType()); |
| } |
| InstantiateTypeArguments(temp_class_, &temp_type_arguments_); |
| temp_instance_.SetTypeArguments(temp_type_arguments_); |
| } else { |
| ASSERT(number_of_type_arguments == 0); |
| } |
| |
| const intptr_t number_of_fields = builder_.ReadUInt(); |
| for (intptr_t j = 0; j < number_of_fields; ++j) { |
| temp_field_ = |
| H.LookupFieldByKernelField(builder_.ReadCanonicalNameReference()); |
| const intptr_t entry_offset = builder_.ReadUInt(); |
| ASSERT(entry_offset < offset); // We have a DAG! |
| temp_object_ = constants.GetOrDie(entry_offset); |
| temp_instance_.SetField(temp_field_, temp_object_); |
| } |
| |
| temp_instance_ = H.Canonicalize(temp_instance_); |
| break; |
| } |
| case kPartialInstantiationConstant: { |
| const intptr_t entry_offset = builder_.ReadUInt(); |
| temp_object_ = constants.GetOrDie(entry_offset); |
| |
| // Happens if the tearoff was in the vmservice library and we have |
| // [skip_vm_service_library] enabled. |
| if (temp_object_.IsNull()) { |
| temp_instance_ = Instance::null(); |
| break; |
| } |
| |
| const intptr_t number_of_type_arguments = builder_.ReadUInt(); |
| ASSERT(number_of_type_arguments > 0); |
| temp_type_arguments_ = |
| TypeArguments::New(number_of_type_arguments, Heap::kOld); |
| for (intptr_t j = 0; j < number_of_type_arguments; ++j) { |
| temp_type_arguments_.SetTypeAt(j, type_translator_.BuildType()); |
| } |
| |
| // Make a copy of the old closure, with the delayed type arguments |
| // set to [temp_type_arguments_]. |
| temp_closure_ = Closure::RawCast(temp_object_.raw()); |
| temp_function_ = temp_closure_.function(); |
| temp_type_arguments2_ = temp_closure_.instantiator_type_arguments(); |
| temp_type_arguments3_ = temp_closure_.function_type_arguments(); |
| temp_context_ = temp_closure_.context(); |
| temp_closure_ = Closure::New( |
| temp_type_arguments2_, Object::null_type_arguments(), |
| temp_type_arguments_, temp_function_, temp_context_, Heap::kOld); |
| temp_instance_ = H.Canonicalize(temp_closure_); |
| break; |
| } |
| case kTearOffConstant: { |
| const NameIndex index = builder_.ReadCanonicalNameReference(); |
| if (ShouldSkipConstant(index)) { |
| temp_instance_ = Instance::null(); |
| break; |
| } |
| |
| temp_function_ = H.LookupStaticMethodByKernelProcedure(index); |
| temp_function_ = temp_function_.ImplicitClosureFunction(); |
| temp_instance_ = temp_function_.ImplicitStaticClosure(); |
| temp_instance_ = H.Canonicalize(temp_instance_); |
| break; |
| } |
| case kTypeLiteralConstant: { |
| temp_instance_ = type_translator_.BuildType().raw(); |
| break; |
| } |
| case kMapConstant: |
| // Note: This is already lowered to InstanceConstant/ListConstant. |
| UNREACHABLE(); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| constants.InsertNewOrGetValue(offset - start_offset, temp_instance_); |
| } |
| return Array::Handle(Z, constants.Release().raw()); |
| } |
| |
| void ConstantHelper::InstantiateTypeArguments(const Class& receiver_class, |
| TypeArguments* type_arguments) { |
| // We make a temporary [Type] object and use `ClassFinalizer::FinalizeType` to |
| // finalize the argument types. |
| // (This can for example make the [type_arguments] vector larger) |
| temp_type_ = |
| Type::New(receiver_class, *type_arguments, TokenPosition::kNoSource); |
| temp_type_ = ClassFinalizer::FinalizeType(*active_class_->klass, temp_type_, |
| ClassFinalizer::kCanonicalize); |
| *type_arguments = temp_type_.arguments(); |
| } |
| |
| // If [index] has `dart:vm_service` as a parent and we are skipping the VM |
| // service library, this method returns `true`, otherwise `false`. |
| bool ConstantHelper::ShouldSkipConstant(NameIndex index) { |
| if (index == NameIndex::kInvalidName) { |
| return false; |
| } |
| while (!H.IsLibrary(index)) { |
| index = H.CanonicalNameParent(index); |
| } |
| ASSERT(H.IsLibrary(index)); |
| return index == skip_vmservice_library_; |
| } |
| |
| } // namespace kernel |
| } // namespace dart |
| |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |