// 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. #if !defined(DART_PRECOMPILED_RUNTIME) #include "vm/kernel_loader.h" #include #include #include "vm/closure_functions_cache.h" #include "vm/compiler/backend/flow_graph_compiler.h" #include "vm/compiler/frontend/constant_reader.h" #include "vm/compiler/frontend/kernel_translation_helper.h" #include "vm/dart_api_impl.h" #include "vm/flags.h" #include "vm/heap/heap.h" #include "vm/kernel_binary.h" #include "vm/longjump.h" #include "vm/object_store.h" #include "vm/parser.h" #include "vm/reusable_handles.h" #include "vm/service_isolate.h" #include "vm/symbols.h" #include "vm/thread.h" #include "vm/version.h" namespace dart { namespace kernel { #define Z (zone_) #define I (isolate_) #define IG (thread_->isolate_group()) #define T (type_translator_) #define H (translation_helper_) static const char* const kVMServiceIOLibraryUri = "dart:vmservice_io"; static bool IsMainOrDevChannel() { return strstr("|main|dev|", Version::Channel()) != nullptr; } static bool is_experimental_shared_data_enabled = false; static void EnableExperimentSharedData(bool value) { if (value && !IsMainOrDevChannel()) { FATAL( "Shared memory multithreading in only available for " "experimentation in dev or main"); } is_experimental_shared_data_enabled = value; } DEFINE_FLAG_HANDLER(EnableExperimentSharedData, experimental_shared_data, "Enable experiment to share data between isolates."); class SimpleExpressionConverter { public: SimpleExpressionConverter(TranslationHelper* translation_helper, KernelReaderHelper* reader_helper) : translation_helper_(*translation_helper), zone_(translation_helper_.zone()), simple_value_(nullptr), helper_(reader_helper) {} bool IsSimple(intptr_t kernel_offset) { AlternativeReadingScope alt(&helper_->reader_, kernel_offset); uint8_t payload = 0; Tag tag = helper_->ReadTag(&payload); // read tag. switch (tag) { case kBigIntLiteral: { helper_->ReadPosition(); const String& literal_str = H.DartString(helper_->ReadStringReference(), Heap::kOld); // read index into string table. simple_value_ = &Integer::ZoneHandle(Z, Integer::New(literal_str)); if (simple_value_->IsNull()) { H.ReportError("Integer literal %s is out of range", literal_str.ToCString()); UNREACHABLE(); } *simple_value_ = H.Canonicalize(*simple_value_); return true; } case kStringLiteral: helper_->ReadPosition(); simple_value_ = &H.DartSymbolPlain( helper_->ReadStringReference()); // read index into string table. return true; case kSpecializedIntLiteral: helper_->ReadPosition(); simple_value_ = &Integer::ZoneHandle(Z, Integer::New(static_cast(payload) - SpecializedIntLiteralBias, Heap::kOld)); *simple_value_ = H.Canonicalize(*simple_value_); return true; case kNegativeIntLiteral: helper_->ReadPosition(); simple_value_ = &Integer::ZoneHandle( Z, Integer::New(-static_cast(helper_->ReadUInt()), Heap::kOld)); // read value. *simple_value_ = H.Canonicalize(*simple_value_); return true; case kPositiveIntLiteral: helper_->ReadPosition(); simple_value_ = &Integer::ZoneHandle( Z, Integer::New(static_cast(helper_->ReadUInt()), Heap::kOld)); // read value. *simple_value_ = H.Canonicalize(*simple_value_); return true; case kDoubleLiteral: helper_->ReadPosition(); simple_value_ = &Double::ZoneHandle( Z, Double::New(helper_->ReadDouble(), Heap::kOld)); // read value. *simple_value_ = H.Canonicalize(*simple_value_); return true; case kTrueLiteral: helper_->ReadPosition(); simple_value_ = &Bool::Handle(Z, Bool::Get(true).ptr()); return true; case kFalseLiteral: helper_->ReadPosition(); simple_value_ = &Bool::Handle(Z, Bool::Get(false).ptr()); return true; case kNullLiteral: helper_->ReadPosition(); simple_value_ = &Instance::ZoneHandle(Z, Instance::null()); return true; default: return false; } } const Instance& SimpleValue() { return *simple_value_; } Zone* zone() const { return zone_; } private: TranslationHelper& translation_helper_; Zone* zone_; Instance* simple_value_; KernelReaderHelper* helper_; DISALLOW_COPY_AND_ASSIGN(SimpleExpressionConverter); }; ArrayPtr KernelLoader::MakeFieldsArray() { const intptr_t len = fields_.length(); const Array& res = Array::Handle(zone_, Array::New(len, Heap::kOld)); for (intptr_t i = 0; i < len; i++) { res.SetAt(i, *fields_[i]); } return res.ptr(); } ArrayPtr KernelLoader::MakeFunctionsArray() { const intptr_t len = functions_.length(); const Array& res = Array::Handle(zone_, Array::New(len, Heap::kOld)); for (intptr_t i = 0; i < len; i++) { res.SetAt(i, *functions_[i]); } return res.ptr(); } LibraryPtr BuildingTranslationHelper::LookupLibraryByKernelLibrary( NameIndex library, bool required) { return loader_->LookupLibrary(library); } ClassPtr BuildingTranslationHelper::LookupClassByKernelClass(NameIndex klass, bool required) { #if defined(DEBUG) LibraryLookupHandleScope library_lookup_handle_scope(library_lookup_handle_); #endif // defined(DEBUG) library_lookup_handle_ = loader_->LookupLibraryFromClass(klass); return loader_->LookupClass(library_lookup_handle_, klass); } LibraryIndex::LibraryIndex(const TypedDataView& kernel_data) : reader_(kernel_data) { intptr_t data_size = reader_.size(); procedure_count_ = reader_.ReadUInt32At(data_size - 4); procedure_index_offset_ = data_size - 4 - (procedure_count_ + 1) * 4; class_count_ = reader_.ReadUInt32At(procedure_index_offset_ - 4); class_index_offset_ = procedure_index_offset_ - 4 - (class_count_ + 1) * 4; source_references_offset_ = -1; source_references_offset_ = reader_.ReadUInt32At(class_index_offset_ - 4); } ClassIndex::ClassIndex(const TypedDataBase& library_kernel_data, intptr_t class_offset, intptr_t class_size) : reader_(library_kernel_data) { Init(class_offset, class_size); } void ClassIndex::Init(intptr_t class_offset, intptr_t class_size) { procedure_count_ = reader_.ReadUInt32At(class_offset + class_size - 4); procedure_index_offset_ = class_offset + class_size - 4 - (procedure_count_ + 1) * 4; } using UriToSourceTable = DirectChainedHashMap; KernelLoader::KernelLoader(Program* program, UriToSourceTable* uri_to_source_table) : program_(program), thread_(Thread::Current()), zone_(thread_->zone()), no_active_isolate_scope_(), patch_classes_(Array::Handle(zone_)), active_class_(), library_kernel_offset_(-1), // Set to the correct value in LoadLibrary correction_offset_(-1), // Set to the correct value in LoadLibrary loading_native_wrappers_library_(false), library_kernel_data_(TypedDataView::Handle(zone_)), kernel_program_info_(KernelProgramInfo::Handle(zone_)), translation_helper_(this, thread_, Heap::kOld), helper_(zone_, &translation_helper_, program_->binary(), /*data_program_offset=*/0), constant_reader_(&helper_, &active_class_), type_translator_(&helper_, &constant_reader_, &active_class_, /* finalize= */ false), inferred_type_metadata_helper_(&helper_, &constant_reader_), static_field_value_(Object::Handle(Z)), name_index_handle_(Smi::Handle(Z)), expression_evaluation_library_(Library::Handle(Z)) { if (!program->is_single_program()) { FATAL( "Trying to load a concatenated dill file at a time where that is " "not allowed"); } InitializeFields(uri_to_source_table); } void KernelLoader::ReadObfuscationProhibitions() { ObfuscationProhibitionsMetadataHelper helper(&helper_); helper.ReadProhibitions(); } void KernelLoader::ReadLoadingUnits() { LoadingUnitsMetadataHelper helper(&helper_); helper.ReadLoadingUnits(); } Object& KernelLoader::LoadEntireProgram(Program* program, bool process_pending_classes) { Thread* thread = Thread::Current(); TIMELINE_DURATION(thread, Isolate, "LoadKernel"); if (program->is_single_program()) { KernelLoader loader(program, /*uri_to_source_table=*/nullptr); return Object::Handle(loader.LoadProgram(process_pending_classes)); } GrowableArray subprogram_file_starts; { kernel::Reader reader(program->binary()); index_programs(&reader, &subprogram_file_starts); } Zone* zone = thread->zone(); Library& library = Library::Handle(zone); intptr_t subprogram_count = subprogram_file_starts.length() - 1; // First index all source tables. UriToSourceTable uri_to_source_table; UriToSourceTableEntry wrapper; Thread* thread_ = Thread::Current(); Zone* zone_ = thread_->zone(); for (intptr_t i = subprogram_count - 1; i >= 0; --i) { intptr_t subprogram_start = subprogram_file_starts.At(i); intptr_t subprogram_end = subprogram_file_starts.At(i + 1); const auto& component = TypedDataBase::Handle( program->binary().ViewFromTo(subprogram_start, subprogram_end)); TranslationHelper translation_helper(thread); KernelReaderHelper helper_(zone_, &translation_helper, component, 0); const intptr_t source_table_size = helper_.SourceTableSize(); for (intptr_t index = 0; index < source_table_size; ++index) { const String& uri_string = helper_.SourceTableUriFor(index); wrapper.uri = &uri_string; TypedData& line_starts = TypedData::Handle(Z, helper_.GetLineStartsFor(index)); if (line_starts.Length() == 0) continue; const String& script_source = helper_.GetSourceFor(index); wrapper.uri = &uri_string; UriToSourceTableEntry* pair = uri_to_source_table.LookupValue(&wrapper); if (pair != nullptr) { // At least two entries with content. Unless the content is the same // that's not valid. const bool src_differ = pair->sources->CompareTo(script_source) != 0; const bool line_starts_differ = !pair->line_starts->CanonicalizeEquals(line_starts); if (src_differ || line_starts_differ) { FATAL( "Invalid kernel binary: Contains at least two source entries " "that do not agree. URI '%s', difference: %s. Subprogram count: " "%" Pd ".", uri_string.ToCString(), src_differ && line_starts_differ ? "src and line starts" : (src_differ ? "src" : "line starts"), subprogram_count); } } else { UriToSourceTableEntry* tmp = new UriToSourceTableEntry(); tmp->uri = &uri_string; tmp->sources = &script_source; tmp->line_starts = &line_starts; uri_to_source_table.Insert(tmp); } } } // Create "fake programs" for each sub-program. for (intptr_t i = subprogram_count - 1; i >= 0; --i) { intptr_t subprogram_start = subprogram_file_starts.At(i); intptr_t subprogram_end = subprogram_file_starts.At(i + 1); const auto& component = TypedDataBase::Handle( program->binary().ViewFromTo(subprogram_start, subprogram_end)); Reader reader(component); const char* error = nullptr; std::unique_ptr subprogram = Program::ReadFrom(&reader, &error); if (subprogram == nullptr) { FATAL("Failed to load kernel file: %s", error); } ASSERT(subprogram->is_single_program()); KernelLoader loader(subprogram.get(), &uri_to_source_table); Object& load_result = Object::Handle(loader.LoadProgram(false)); if (load_result.IsError()) return load_result; if (load_result.IsLibrary()) { library ^= load_result.ptr(); } } if (process_pending_classes && !ClassFinalizer::ProcessPendingClasses()) { // Class finalization failed -> sticky error would be set. return Error::Handle(thread->StealStickyError()); } return library; } void KernelLoader::index_programs( kernel::Reader* reader, GrowableArray* subprogram_file_starts) { // Dill files can be concatenated (e.g. cat a.dill b.dill > c.dill), so we // need to first index the (possibly combined) file. // First entry becomes last entry. // Last entry is for ease of calculating size of last subprogram. subprogram_file_starts->Add(reader->size()); reader->set_offset(reader->size() - 4); while (reader->offset() > 0) { intptr_t size = reader->ReadUInt32(); intptr_t start = reader->offset() - size; if (start < 0) { FATAL("Invalid kernel binary: Indicated size is invalid."); } subprogram_file_starts->Add(start); reader->set_offset(start - 4); } subprogram_file_starts->Reverse(); } StringPtr KernelLoader::FindSourceForScript(const uint8_t* kernel_buffer, intptr_t kernel_buffer_length, const String& uri) { const auto& binary = ExternalTypedData::Handle(ExternalTypedData::New( kExternalTypedDataUint8ArrayCid, const_cast(kernel_buffer), kernel_buffer_length, Heap::kNew)); Thread* thread = Thread::Current(); Zone* zone = thread->zone(); TranslationHelper translation_helper(thread); KernelReaderHelper reader(zone, &translation_helper, binary, 0); intptr_t source_table_size = reader.SourceTableSize(); for (intptr_t i = 0; i < source_table_size; ++i) { const String& source_uri = reader.SourceTableUriFor(i); if (source_uri.EndsWith(uri)) { return reader.GetSourceFor(i).ptr(); } } return String::null(); } void KernelLoader::InitializeFields(UriToSourceTable* uri_to_source_table) { const intptr_t source_table_size = helper_.SourceTableSize(); const Array& scripts = Array::Handle(Z, Array::New(source_table_size, Heap::kOld)); const auto& binary = program_->binary(); // Copy the Kernel string offsets out of the binary and into the VM's heap. ASSERT(program_->string_table_offset() >= 0); Reader reader(binary); reader.set_offset(program_->string_table_offset()); intptr_t count = reader.ReadUInt() + 1; const auto& offsets = TypedData::Handle( Z, TypedData::New(kTypedDataUint32ArrayCid, count, Heap::kOld)); offsets.SetUint32(0, 0); intptr_t end_offset = 0; for (intptr_t i = 1; i < count; ++i) { end_offset = reader.ReadUInt(); offsets.SetUint32(i << 2, end_offset); } // Create view of the string data. const auto& string_data = TypedDataView::Handle( reader.ViewFromTo(reader.offset(), reader.offset() + end_offset)); // Create a view of the constants table. const auto& constants_table = TypedDataView::Handle(reader.ViewFromTo( program_->constant_table_offset(), program_->name_table_offset())); // Copy the canonical names into the VM's heap. Encode them as unsigned, so // the parent indexes are adjusted when extracted. reader.set_offset(program_->name_table_offset()); count = reader.ReadUInt() * 2; TypedData& names = TypedData::Handle( Z, TypedData::New(kTypedDataUint32ArrayCid, count, Heap::kOld)); for (intptr_t i = 0; i < count; ++i) { names.SetUint32(i << 2, reader.ReadUInt()); } // Create view of metadata payloads. const auto& metadata_payloads = TypedDataView::Handle( reader.ViewFromTo(program_->metadata_payloads_offset(), program_->metadata_mappings_offset())); ASSERT(Utils::IsAligned(metadata_payloads.DataAddr(0), kWordSize)); // Create view of metadata mappings. const auto& metadata_mappings = TypedDataView::Handle(reader.ViewFromTo( program_->metadata_mappings_offset(), program_->string_table_offset())); #if defined(DEBUG) MetadataHelper::VerifyMetadataMappings(metadata_mappings); #endif const Array& libraries_cache = Array::Handle(Z, HashTables::New>( program_->library_count(), Heap::kOld)); const intptr_t kClassesPerLibraryGuess = 5; const Array& classes_cache = Array::Handle( Z, HashTables::New>( kClassesPerLibraryGuess * program_->library_count(), Heap::kOld)); kernel_program_info_ = KernelProgramInfo::New( binary, string_data, metadata_payloads, metadata_mappings, constants_table, offsets, names, scripts, libraries_cache, classes_cache); H.InitFromKernelProgramInfo(kernel_program_info_); Script& script = Script::Handle(Z); for (intptr_t index = 0; index < source_table_size; ++index) { script = LoadScriptAt(index, uri_to_source_table); scripts.SetAt(index, script); } } KernelLoader::KernelLoader(const KernelProgramInfo& kernel_program_info, const TypedDataBase& kernel_data, intptr_t data_program_offset) : program_(nullptr), thread_(Thread::Current()), zone_(thread_->zone()), no_active_isolate_scope_(), patch_classes_(Array::Handle(zone_)), library_kernel_offset_(data_program_offset), correction_offset_(0), loading_native_wrappers_library_(false), library_kernel_data_(TypedDataView::Handle(zone_)), kernel_program_info_( KernelProgramInfo::Handle(zone_, kernel_program_info.ptr())), translation_helper_(this, thread_, Heap::kOld), helper_(zone_, &translation_helper_, kernel_data, 0), constant_reader_(&helper_, &active_class_), type_translator_(&helper_, &constant_reader_, &active_class_, /* finalize= */ false), inferred_type_metadata_helper_(&helper_, &constant_reader_), static_field_value_(Object::Handle(Z)), name_index_handle_(Smi::Handle(Z)), expression_evaluation_library_(Library::Handle(Z)) { ASSERT(T.active_class_ == &active_class_); T.finalize_ = false; H.InitFromKernelProgramInfo(kernel_program_info_); } bool KernelLoader::IsClassName(NameIndex name, const String& library, const String& klass) { ASSERT(H.IsClass(name)); StringIndex class_name_index = H.CanonicalNameString(name); if (!H.StringEquals(class_name_index, klass.ToCString())) { return false; } ASSERT(H.IsLibrary(H.CanonicalNameParent(name))); StringIndex library_name_index = H.CanonicalNameString(H.CanonicalNameParent(name)); return H.StringEquals(library_name_index, library.ToCString()); } ObjectPtr KernelLoader::LoadProgram(bool process_pending_classes) { SafepointWriteRwLocker ml(thread_, thread_->isolate_group()->program_lock()); ASSERT(kernel_program_info_.constants() == Array::null()); if (!program_->is_single_program()) { FATAL( "Trying to load a concatenated dill file at a time where that is " "not allowed"); } LongJumpScope jump; if (setjmp(*jump.Set()) == 0) { // Note that `problemsAsJson` on Component is implicitly skipped. const intptr_t length = program_->library_count(); for (intptr_t i = 0; i < length; i++) { LoadLibrary(i); } // Finalize still pending classes if requested. if (process_pending_classes) { if (!ClassFinalizer::ProcessPendingClasses()) { // Class finalization failed -> sticky error would be set. return H.thread()->StealStickyError(); } } // Sets the constants array to an empty array with the length equal to // the number of constants. The array gets filled lazily while reading // constants. ASSERT(kernel_program_info_.constants_table() != ExternalTypedData::null()); ConstantReader constant_reader(&helper_, &active_class_); const intptr_t num_consts = constant_reader.NumConstants(); const Array& array = Array::Handle(Z, Array::New(num_consts, Heap::kOld)); for (intptr_t i = 0; i < num_consts; i++) { array.SetAt(i, Object::sentinel()); } kernel_program_info_.set_constants(array); H.SetConstants(array); // for caching NameIndex main = program_->main_method(); if (main != -1) { NameIndex main_library = H.EnclosingName(main); return LookupLibrary(main_library); } return Library::null(); } // Either class finalization failed or we caught a compile error. // In both cases sticky error would be set. return Thread::Current()->StealStickyError(); } void KernelLoader::LoadLibrary(const Library& library) { // This will be invoked by VM bootstrapping code. SafepointWriteRwLocker ml(thread_, thread_->isolate_group()->program_lock()); ASSERT(!library.Loaded()); const auto& uri = String::Handle(Z, library.url()); const intptr_t num_libraries = program_->library_count(); for (intptr_t i = 0; i < num_libraries; ++i) { const String& library_uri = LibraryUri(i); if (library_uri.Equals(uri)) { LoadLibrary(i); return; } } } ObjectPtr KernelLoader::LoadExpressionEvaluationFunction( const String& library_url, const String& klass) { // Find the original context, i.e. library/class, in which the evaluation will // happen. const Library& real_library = Library::Handle(Z, Library::LookupLibrary(thread_, library_url)); ASSERT(!real_library.IsNull()); const Class& real_class = Class::Handle( Z, klass.IsNull() ? real_library.toplevel_class() : real_library.LookupClassAllowPrivate(klass)); ASSERT(!real_class.IsNull()); const intptr_t num_cids = IG->class_table()->NumCids(); const intptr_t num_libs = GrowableObjectArray::Handle(IG->object_store()->libraries()).Length(); // Load the "evaluate:source" expression evaluation library. ASSERT(expression_evaluation_library_.IsNull()); ASSERT(H.GetExpressionEvaluationClass().IsNull()); ASSERT(H.GetExpressionEvaluationFunction().IsNull()); H.SetExpressionEvaluationRealClass(real_class); const Object& result = Object::Handle(Z, LoadProgram(true)); if (result.IsError()) { return result.ptr(); } const Function& function = H.GetExpressionEvaluationFunction(); ASSERT(!function.IsNull()); ASSERT( GrowableObjectArray::Handle(IG->object_store()->libraries()).Length() == num_libs); ASSERT(IG->class_table()->NumCids() == num_cids); // Make the expression evaluation function have the right script, // kernel data and parent. const auto& eval_script = Script::Handle(Z, function.script()); ASSERT(!expression_evaluation_library_.IsNull()); function.SetKernelLibraryAndEvalScript( eval_script, kernel_program_info_, expression_evaluation_library_.kernel_library_index()); function.set_owner(real_class); ASSERT(real_class.is_finalized()); // The owner class has already been marked as finalized so the signature of // this added function must be finalized here, since finalization of member // types will not be called anymore. FunctionType& signature = FunctionType::Handle(Z, function.signature()); if (!function.is_static()) { // Patch the illegal receiver type (type class with kIllegalCid) to dynamic. signature.SetParameterTypeAt(0, Object::dynamic_type()); } signature ^= ClassFinalizer::FinalizeType(signature); function.SetSignature(signature); return function.ptr(); } void KernelLoader::FindModifiedLibraries(Program* program, IsolateGroup* isolate_group, BitVector* modified_libs, bool force_reload, bool* is_empty_program, intptr_t* p_num_classes, intptr_t* p_num_procedures) { LongJumpScope jump; Zone* zone = Thread::Current()->zone(); if (setjmp(*jump.Set()) == 0) { if (force_reload) { // If a reload is being forced we mark all libraries as having // been modified. const auto& libs = GrowableObjectArray::Handle( isolate_group->object_store()->libraries()); intptr_t num_libs = libs.Length(); Library& lib = dart::Library::Handle(zone); for (intptr_t i = 0; i < num_libs; i++) { lib ^= libs.At(i); if (!lib.is_dart_scheme()) { modified_libs->Add(lib.index()); } } return; } if (p_num_classes != nullptr) { *p_num_classes = 0; } if (p_num_procedures != nullptr) { *p_num_procedures = 0; } // Now go through all the libraries that are present in the incremental // kernel files, these will constitute the modified libraries. *is_empty_program = true; if (program->is_single_program()) { KernelLoader loader(program, /*uri_to_source_table=*/nullptr); loader.walk_incremental_kernel(modified_libs, is_empty_program, p_num_classes, p_num_procedures); } GrowableArray subprogram_file_starts; { kernel::Reader reader(program->binary()); index_programs(&reader, &subprogram_file_starts); } // Create "fake programs" for each sub-program. intptr_t subprogram_count = subprogram_file_starts.length() - 1; for (intptr_t i = 0; i < subprogram_count; ++i) { intptr_t subprogram_start = subprogram_file_starts.At(i); intptr_t subprogram_end = subprogram_file_starts.At(i + 1); const auto& component = TypedDataBase::Handle( program->binary().ViewFromTo(subprogram_start, subprogram_end)); Reader reader(component); const char* error = nullptr; std::unique_ptr subprogram = Program::ReadFrom(&reader, &error); if (subprogram == nullptr) { FATAL("Failed to load kernel file: %s", error); } ASSERT(subprogram->is_single_program()); KernelLoader loader(subprogram.get(), /*uri_to_source_table=*/nullptr); loader.walk_incremental_kernel(modified_libs, is_empty_program, p_num_classes, p_num_procedures); } } } void KernelLoader::walk_incremental_kernel(BitVector* modified_libs, bool* is_empty_program, intptr_t* p_num_classes, intptr_t* p_num_procedures) { intptr_t length = program_->library_count(); *is_empty_program = *is_empty_program && (length == 0); bool collect_library_stats = p_num_classes != nullptr || p_num_procedures != nullptr; intptr_t num_classes = 0; intptr_t num_procedures = 0; Library& lib = Library::Handle(Z); for (intptr_t i = 0; i < length; i++) { intptr_t kernel_offset = library_offset(i); helper_.SetOffset(kernel_offset); LibraryHelper library_helper(&helper_); library_helper.ReadUntilIncluding(LibraryHelper::kCanonicalName); lib = LookupLibraryOrNull(library_helper.canonical_name_); if (!lib.IsNull() && !lib.is_dart_scheme()) { // This is a library that already exists so mark it as being modified. modified_libs->Add(lib.index()); } if (collect_library_stats) { intptr_t library_end = library_offset(i + 1); library_kernel_data_ = helper_.reader_.ViewFromTo(kernel_offset, library_end); LibraryIndex library_index(library_kernel_data_); num_classes += library_index.class_count(); num_procedures += library_index.procedure_count(); } } if (p_num_classes != nullptr) { *p_num_classes += num_classes; } if (p_num_procedures != nullptr) { *p_num_procedures += num_procedures; } } void KernelLoader::ReadInferredType(const Field& field, intptr_t kernel_offset) { const InferredTypeMetadata type = inferred_type_metadata_helper_.GetInferredType(kernel_offset, /*read_constant=*/false); if (type.IsTrivial()) { return; } field.set_guarded_cid(type.cid); field.set_is_nullable(type.IsNullable()); field.set_guarded_list_length(Field::kNoFixedLength); if (FLAG_precompiled_mode) { field.set_is_unboxed(!field.is_late() && !field.is_static() && !field.is_nullable() && ((field.guarded_cid() == kDoubleCid) || (field.guarded_cid() == kFloat32x4Cid && FlowGraphCompiler::SupportsUnboxedSimd128()) || (field.guarded_cid() == kFloat64x2Cid && FlowGraphCompiler::SupportsUnboxedSimd128()) || type.IsInt())); } } void KernelLoader::CheckForInitializer(const Field& field) { if (helper_.PeekTag() == kSomething) { field.set_has_initializer(true); SimpleExpressionConverter converter(&H, &helper_); const bool has_simple_initializer = converter.IsSimple(helper_.ReaderOffset() + 1); if (!has_simple_initializer || (!field.is_static() && !converter.SimpleValue().IsNull())) { field.set_has_nontrivial_initializer(true); } return; } field.set_has_initializer(false); field.set_has_nontrivial_initializer(false); } LibraryPtr KernelLoader::LoadLibrary(intptr_t index) { if (!program_->is_single_program()) { FATAL( "Trying to load a concatenated dill file at a time where that is " "not allowed"); } // Read library index. library_kernel_offset_ = library_offset(index); correction_offset_ = library_kernel_offset_; intptr_t library_end = library_offset(index + 1); intptr_t library_size = library_end - library_kernel_offset_; // NOTE: Since |helper_| is used to load the overall kernel program, // it's reader's offset is an offset into the overall kernel program. // Hence, when setting the kernel offsets of field and functions, one // has to subtract the library's kernel offset from the reader's // offset. helper_.SetOffset(library_kernel_offset_); LibraryHelper library_helper(&helper_); library_helper.ReadUntilIncluding(LibraryHelper::kCanonicalName); if (!FLAG_precompiled_mode && !IG->should_load_vmservice()) { StringIndex lib_name_index = H.CanonicalNameString(library_helper.canonical_name_); if (H.StringEquals(lib_name_index, kVMServiceIOLibraryUri)) { // We are not the service isolate and we are not generating an AOT // snapshot so we skip loading 'dart:vmservice_io'. skip_vmservice_library_ = library_helper.canonical_name_; ASSERT(H.IsLibrary(skip_vmservice_library_)); return Library::null(); } } Library& library = Library::Handle(Z, LookupLibrary(library_helper.canonical_name_)); if (library.Loaded()) return library.ptr(); const NNBDCompiledMode mode = library_helper.GetNonNullableByDefaultCompiledMode(); if (mode == NNBDCompiledMode::kInvalid) { H.ReportError( "Library '%s' was compiled in an unsupported mixed mode between sound " "null safety and not sound null safety.", String::Handle(library.url()).ToCString()); } if (mode == NNBDCompiledMode::kWeak) { H.ReportError( "Library '%s' was compiled without sound null safety (in weak mode) " "and it cannot be used at runtime", String::Handle(library.url()).ToCString()); } library_kernel_data_ = helper_.reader_.ViewFromTo( library_kernel_offset_, library_kernel_offset_ + library_size); library.set_kernel_library_index(index); library.set_kernel_program_info(kernel_program_info_); const intptr_t start_offset = kernel_program_info_.KernelLibraryStartOffset(index); const intptr_t end_offset = kernel_program_info_.KernelLibraryEndOffset(index); library_kernel_data_ = helper_.reader_.ViewFromTo(start_offset, end_offset); LibraryIndex library_index(library_kernel_data_); intptr_t class_count = library_index.class_count(); library_helper.ReadUntilIncluding(LibraryHelper::kName); library.SetName(H.DartSymbolObfuscate(library_helper.name_index_)); // The bootstrapper will take care of creating the native wrapper classes, but // we will add the synthetic constructors to them here. if (library.name() == Symbols::Symbol(Symbols::kDartNativeWrappersLibNameId).ptr()) { ASSERT(library.LoadInProgress()); loading_native_wrappers_library_ = true; } else { loading_native_wrappers_library_ = false; library.SetLoadInProgress(); } if (library.url() == Symbols::vm_ffi_native_assets().ptr()) { // Hot reload replaces an old native assets library. // TODO(https://github.com/dart-lang/sdk/issues/55519): If we start // supporting caching of asset lookup, we should empty the caches derived // from the native assets library. IG->object_store()->set_native_assets_library(library); } library_helper.ReadUntilIncluding(LibraryHelper::kSourceUriIndex); const Script& script = Script::Handle(Z, ScriptAt(library_helper.source_uri_index_)); library_helper.ReadUntilExcluding(LibraryHelper::kAnnotations); intptr_t annotations_kernel_offset = helper_.ReaderOffset() - correction_offset_; const intptr_t annotation_count = helper_.ReadListLength(); // read list length. for (intptr_t i = 0; i < annotation_count; ++i) { helper_.SkipExpression(); // read ith annotation. } library_helper.SetJustRead(LibraryHelper::kAnnotations); // Setup toplevel class (which contains library fields/procedures). // We do not register expression evaluation classes with the VM: // The expression evaluation functions should be GC-able as soon as // they are not reachable anymore and we never look them up by name. const bool register_class = library.ptr() != expression_evaluation_library_.ptr(); Class& toplevel_class = Class::Handle(Z, Class::New(library, Symbols::TopLevel(), script, TokenPosition::kNoSource, register_class)); toplevel_class.set_is_abstract(); toplevel_class.set_is_declaration_loaded(); toplevel_class.set_is_type_finalized(); toplevel_class.set_num_type_arguments_unsafe(0); library.set_toplevel_class(toplevel_class); library_helper.ReadUntilExcluding(LibraryHelper::kDependencies); LoadLibraryImportsAndExports(&library, toplevel_class); library_helper.SetJustRead(LibraryHelper::kDependencies); // Everything up til the classes are skipped implicitly, and library_helper // is no longer used. const GrowableObjectArray& classes = GrowableObjectArray::Handle(Z, IG->object_store()->pending_classes()); // Load all classes. intptr_t next_class_offset = library_index.ClassOffset(0); Class& klass = Class::Handle(Z); for (intptr_t i = 0; i < class_count; ++i) { helper_.SetOffset(next_class_offset); next_class_offset = library_index.ClassOffset(i + 1); LoadClass(library, toplevel_class, next_class_offset, &klass); if (register_class) { classes.Add(klass, Heap::kOld); } } if (loading_native_wrappers_library_ || !register_class) { FinishTopLevelClassLoading(toplevel_class, library, library_index); } // Used for mirrors and allows VM to recognize @pragma annotations on // libraries. if (annotation_count > 0) { ASSERT(annotations_kernel_offset > 0); library.AddMetadata(library, annotations_kernel_offset); } if (register_class) { helper_.SetOffset(library_index.SourceReferencesOffset()); intptr_t count = helper_.ReadUInt(); const GrowableObjectArray& used_scripts = GrowableObjectArray::Handle(library.used_scripts()); Script& script = Script::Handle(Z); for (intptr_t i = 0; i < count; i++) { intptr_t uri_index = helper_.ReadUInt(); script = ScriptAt(uri_index); used_scripts.Add(script); } } if (!library.Loaded()) library.SetLoaded(); return library.ptr(); } void KernelLoader::FinishTopLevelClassLoading( const Class& toplevel_class, const Library& library, const LibraryIndex& library_index) { if (toplevel_class.is_loaded()) { return; } TIMELINE_DURATION(Thread::Current(), Isolate, "FinishTopLevelClassLoading"); ActiveClassScope active_class_scope(&active_class_, &toplevel_class); // Offsets within library index are whole program offsets and not // relative to the library. const intptr_t correction = correction_offset_ - library_kernel_offset_; helper_.SetOffset(library_index.ClassOffset(library_index.class_count()) + correction); const intptr_t extension_count = helper_.ReadListLength(); for (intptr_t i = 0; i < extension_count; ++i) { helper_.ReadTag(); // read tag. helper_.SkipCanonicalNameReference(); // skip canonical name. helper_.SkipStringReference(); // skip name. helper_.SkipListOfExpressions(); // skip annotations. helper_.ReadUInt(); // read source uri index. helper_.ReadPosition(); // read file offset. helper_.ReadByte(); // skip flags. helper_.SkipTypeParametersList(); // skip type parameter list. helper_.SkipDartType(); // skip on-type. const intptr_t extension_member_count = helper_.ReadListLength(); for (intptr_t j = 0; j < extension_member_count; ++j) { helper_.SkipName(); // skip name. helper_.ReadByte(); // read kind. helper_.ReadByte(); // read flags. helper_.SkipCanonicalNameReference(); // skip member reference helper_.SkipCanonicalNameReference(); // skip tear-off reference } } const intptr_t extension_type_declaration_count = helper_.ReadListLength(); for (intptr_t i = 0; i < extension_type_declaration_count; ++i) { helper_.ReadTag(); // read tag. helper_.SkipCanonicalNameReference(); // skip canonical name. helper_.SkipStringReference(); // skip name. helper_.SkipListOfExpressions(); // skip annotations. helper_.ReadUInt(); // read source uri index. helper_.ReadPosition(); // read file offset. helper_.ReadByte(); // skip flags. helper_.SkipTypeParametersList(); // skip type parameter list. helper_.SkipDartType(); // skip declared representation type. helper_.SkipStringReference(); // skip representation name. helper_.SkipListOfDartTypes(); // skip implements types. // Skip extension type procedures. const intptr_t extension_type_procedure_count = helper_.ReadListLength(); // read list length. for (intptr_t i = 0; i < extension_type_procedure_count; ++i) { ProcedureHelper procedure_helper(&helper_); procedure_helper.ReadUntilExcluding(ProcedureHelper::kEnd); } const intptr_t extension_type_member_count = helper_.ReadListLength(); for (intptr_t j = 0; j < extension_type_member_count; ++j) { helper_.SkipName(); // skip name. helper_.ReadByte(); // read kind. helper_.ReadByte(); // read flags. helper_.SkipCanonicalNameReference(); // skip member reference helper_.SkipCanonicalNameReference(); // skip tear-off reference } } fields_.Clear(); functions_.Clear(); // Load toplevel fields. const intptr_t field_count = helper_.ReadListLength(); // read list length. for (intptr_t i = 0; i < field_count; ++i) { intptr_t field_offset = helper_.ReaderOffset() - correction_offset_; ActiveMemberScope active_member_scope(&active_class_, nullptr); FieldHelper field_helper(&helper_); field_helper.ReadUntilExcluding(FieldHelper::kName); const String& name = helper_.ReadNameAsFieldName(); field_helper.SetJustRead(FieldHelper::kName); field_helper.ReadUntilExcluding(FieldHelper::kAnnotations); intptr_t annotation_count = helper_.ReadListLength(); uint32_t pragma_bits = 0; ReadVMAnnotations(annotation_count, &pragma_bits); field_helper.SetJustRead(FieldHelper::kAnnotations); field_helper.ReadUntilExcluding(FieldHelper::kType); const Object& script_class = ClassForScriptAt(toplevel_class, field_helper.source_uri_index_); // In the VM all const fields are implicitly final whereas in Kernel they // are not final because they are not explicitly declared that way. const bool is_final = field_helper.IsConst() || field_helper.IsFinal(); // Only instance fields could be covariant. ASSERT(!field_helper.IsCovariant() && !field_helper.IsGenericCovariantImpl()); const bool is_late = field_helper.IsLate(); const bool is_extension_member = field_helper.IsExtensionMember(); const bool is_extension_type_member = field_helper.IsExtensionTypeMember(); const Field& field = Field::Handle( Z, Field::NewTopLevel(name, is_final, field_helper.IsConst(), is_late, script_class, field_helper.position_, field_helper.end_position_)); field.set_kernel_offset(field_offset); field.set_has_pragma(HasPragma::decode(pragma_bits)); field.set_is_extension_member(is_extension_member); field.set_is_extension_type_member(is_extension_type_member); field.set_is_shared(SharedPragma::decode(pragma_bits)); const AbstractType& type = T.BuildType(); // read type. field.SetFieldType(type); ReadInferredType(field, field_offset + library_kernel_offset_); CheckForInitializer(field); // Static fields with initializers are implicitly late. if (field.has_initializer()) { field.set_is_late(true); } field_helper.SetJustRead(FieldHelper::kType); field_helper.ReadUntilExcluding(FieldHelper::kInitializer); intptr_t field_initializer_offset = helper_.ReaderOffset(); field_helper.ReadUntilExcluding(FieldHelper::kEnd); { AlternativeReadingScope alt(&helper_.reader_, field_initializer_offset); static_field_value_ = ReadInitialFieldValue(field, &field_helper); } GenerateFieldAccessors(toplevel_class, field, &field_helper); IG->RegisterStaticField(field, static_field_value_); if ((FLAG_enable_mirrors || HasPragma::decode(pragma_bits)) && annotation_count > 0) { library.AddMetadata(field, field_offset); } fields_.Add(&field); } ASSERT(!toplevel_class.is_loaded()); // Load toplevel procedures. intptr_t next_procedure_offset = library_index.ProcedureOffset(0) + correction; const intptr_t procedure_count = library_index.procedure_count(); for (intptr_t i = 0; i < procedure_count; ++i) { helper_.SetOffset(next_procedure_offset); next_procedure_offset = library_index.ProcedureOffset(i + 1) + correction; LoadProcedure(library, toplevel_class, false, next_procedure_offset); // LoadProcedure calls Library::GetMetadata which invokes Dart code // which may recursively trigger class finalization and // FinishTopLevelClassLoading. // In such case, return immediately and avoid overwriting already finalized // functions with freshly loaded and not yet finalized. if (toplevel_class.is_loaded()) { return; } } toplevel_class.SetFields(Array::Handle(MakeFieldsArray())); toplevel_class.SetFunctions(Array::Handle(MakeFunctionsArray())); String& name = String::Handle(Z); for (intptr_t i = 0, n = fields_.length(); i < n; ++i) { const Field* field = fields_.At(i); name = field->name(); library.AddObject(*field, name); } for (intptr_t i = 0, n = functions_.length(); i < n; ++i) { const Function* function = functions_.At(i); name = function->name(); library.AddObject(*function, name); } ASSERT(!toplevel_class.is_loaded()); toplevel_class.set_is_loaded(true); } void KernelLoader::LoadLibraryImportsAndExports(Library* library, const Class& toplevel_class) { GrowableObjectArray& show_list = GrowableObjectArray::Handle(Z); GrowableObjectArray& hide_list = GrowableObjectArray::Handle(Z); Array& show_names = Array::Handle(Z); Array& hide_names = Array::Handle(Z); Namespace& ns = Namespace::Handle(Z); LibraryPrefix& library_prefix = LibraryPrefix::Handle(Z); const intptr_t deps_count = helper_.ReadListLength(); const Array& deps = Array::Handle(Array::New(deps_count)); for (intptr_t dep = 0; dep < deps_count; ++dep) { LibraryDependencyHelper dependency_helper(&helper_); dependency_helper.ReadUntilExcluding(LibraryDependencyHelper::kAnnotations); intptr_t annotations_kernel_offset = helper_.ReaderOffset() - correction_offset_; dependency_helper.ReadUntilExcluding(LibraryDependencyHelper::kCombinators); // Ignore the dependency if the target library is invalid. // The error will be caught during compilation. if (dependency_helper.target_library_canonical_name_ < 0) { const intptr_t combinator_count = helper_.ReadListLength(); for (intptr_t c = 0; c < combinator_count; ++c) { helper_.SkipLibraryCombinator(); } continue; } // Prepare show and hide lists. show_list = GrowableObjectArray::New(Heap::kOld); hide_list = GrowableObjectArray::New(Heap::kOld); const intptr_t combinator_count = helper_.ReadListLength(); for (intptr_t c = 0; c < combinator_count; ++c) { uint8_t flags = helper_.ReadFlags(); intptr_t name_count = helper_.ReadListLength(); for (intptr_t n = 0; n < name_count; ++n) { String& show_hide_name = H.DartSymbolObfuscate(helper_.ReadStringReference()); if ((flags & LibraryDependencyHelper::Show) != 0) { show_list.Add(show_hide_name, Heap::kOld); } else { hide_list.Add(show_hide_name, Heap::kOld); } } } if (show_list.Length() > 0) { show_names = Array::MakeFixedLength(show_list); } else { show_names = Array::null(); } if (hide_list.Length() > 0) { hide_names = Array::MakeFixedLength(hide_list); } else { hide_names = Array::null(); } Library& target_library = Library::Handle( Z, LookupLibrary(dependency_helper.target_library_canonical_name_)); if (!FLAG_enable_mirrors && target_library.url() == Symbols::DartMirrors().ptr()) { H.ReportError( "import of dart:mirrors is not supported in the current Dart " "runtime"); } if (!Api::IsFfiEnabled() && target_library.url() == Symbols::DartFfi().ptr() && library->url() != Symbols::DartCore().ptr() && library->url() != Symbols::DartConcurrent().ptr() && library->url() != Symbols::DartInternal().ptr() && library->url() != Symbols::DartFfi().ptr()) { H.ReportError( "import of dart:ffi is not supported in the current Dart runtime"); } if (target_library.url() == Symbols::DartConcurrent().ptr() && !is_experimental_shared_data_enabled) { FATAL( "Encountered dart:concurrent when functionality is disabled. " "Pass --experimental-shared-data"); } String& prefix = H.DartSymbolPlain(dependency_helper.name_index_); ns = Namespace::New(target_library, show_names, hide_names, *library); if ((dependency_helper.flags_ & LibraryDependencyHelper::Export) != 0) { library->AddExport(ns); } else { if (prefix.IsNull() || prefix.Length() == 0) { library->AddImport(ns); } else { library_prefix = library->LookupLocalLibraryPrefix(prefix); if (!library_prefix.IsNull()) { library_prefix.AddImport(ns); } else { library_prefix = LibraryPrefix::New( prefix, ns, (dependency_helper.flags_ & LibraryDependencyHelper::Deferred) != 0, *library); library->AddObject(library_prefix, prefix); } } } if (FLAG_enable_mirrors && dependency_helper.annotation_count_ > 0) { ASSERT(annotations_kernel_offset > 0); library->AddMetadata(ns, annotations_kernel_offset); } if (prefix.IsNull()) { deps.SetAt(dep, ns); } else { deps.SetAt(dep, library_prefix); } } library->set_dependencies(deps); } void KernelLoader::LoadPreliminaryClass(ClassHelper* class_helper, intptr_t type_parameter_count) { const Class* klass = active_class_.klass; // Enable access to type_parameters(). klass->set_is_declaration_loaded(); // Note: This assumes that ClassHelper is exactly at the position where // the length of the type parameters have been read, and that the order in // the binary is as follows: [...], kTypeParameters, kSuperClass, kMixinType, // kImplementedClasses, [...]. // Set type parameters. T.LoadAndSetupTypeParameters(&active_class_, Object::null_function(), *klass, Object::null_function_type(), type_parameter_count); ActiveTypeParametersScope scope(&active_class_, nullptr, Z); T.LoadAndSetupBounds(&active_class_, Object::null_function(), *klass, Object::null_function_type(), type_parameter_count); // Set super type. Some classes (e.g., Object) do not have one. Tag type_tag = helper_.ReadTag(); // read super class type (part 1). if (type_tag == kSomething) { AbstractType& super_type = T.BuildTypeWithoutFinalization(); // read super class type (part 2). klass->set_super_type(Type::Cast(super_type)); } class_helper->SetJustRead(ClassHelper::kSuperClass); class_helper->ReadUntilIncluding(ClassHelper::kMixinType); // Build implemented interface types intptr_t interface_count = helper_.ReadListLength(); if (interface_count == 0) { klass->set_interfaces(Object::empty_array()); } else { const Array& interfaces = Array::Handle(Z, Array::New(interface_count, Heap::kOld)); for (intptr_t i = 0; i < interface_count; i++) { const AbstractType& type = T.BuildTypeWithoutFinalization(); // read ith type. interfaces.SetAt(i, type); } klass->set_interfaces(interfaces); } class_helper->SetJustRead(ClassHelper::kImplementedClasses); if (class_helper->is_abstract()) { klass->set_is_abstract(); } if (class_helper->is_transformed_mixin_application()) { klass->set_is_transformed_mixin_application(); } if (class_helper->has_const_constructor()) { klass->set_is_const(); } if (class_helper->is_sealed()) { klass->set_is_sealed(); } if (class_helper->is_mixin_class()) { klass->set_is_mixin_class(); } if (class_helper->is_base()) { klass->set_is_base_class(); } if (class_helper->is_interface()) { klass->set_is_interface_class(); } if (class_helper->is_final()) { klass->set_is_final(); } } void KernelLoader::LoadClass(const Library& library, const Class& toplevel_class, intptr_t class_end, Class* out_class) { intptr_t class_offset = helper_.ReaderOffset(); ClassIndex class_index(program_->binary(), class_offset, class_end - class_offset); ClassHelper class_helper(&helper_); class_helper.ReadUntilIncluding(ClassHelper::kCanonicalName); *out_class = LookupClass(library, class_helper.canonical_name_); out_class->set_kernel_offset(class_offset - correction_offset_); // The class needs to have a script because all the functions in the class // will inherit it. The predicate Function::IsOptimizable uses the absence of // a script to detect test functions that should not be optimized. if (out_class->script() == Script::null()) { class_helper.ReadUntilIncluding(ClassHelper::kSourceUriIndex); const Script& script = Script::Handle(Z, ScriptAt(class_helper.source_uri_index_)); out_class->set_script(script); } if (out_class->token_pos() == TokenPosition::kNoSource) { class_helper.ReadUntilIncluding(ClassHelper::kEndPosition); out_class->set_token_pos(class_helper.start_position_); out_class->set_end_token_pos(class_helper.end_position_); } class_helper.ReadUntilIncluding(ClassHelper::kFlags); if (class_helper.is_enum_class()) { out_class->set_is_enum_class(); } class_helper.ReadUntilExcluding(ClassHelper::kAnnotations); intptr_t annotation_count = helper_.ReadListLength(); uint32_t pragma_bits = 0; ReadVMAnnotations(annotation_count, &pragma_bits); if (IsolateUnsendablePragma::decode(pragma_bits)) { out_class->set_is_isolate_unsendable_due_to_pragma(true); } if (DeeplyImmutablePragma::decode(pragma_bits)) { out_class->set_is_deeply_immutable(true); // Ensure that the pragma implies deeply immutability for VM recognized // classes. ASSERT(out_class->id() >= kNumPredefinedCids || IsDeeplyImmutableCid(out_class->id())); } if (HasPragma::decode(pragma_bits)) { out_class->set_has_pragma(true); } if (DynModuleExtendablePragma::decode(pragma_bits)) { out_class->set_is_dynamically_extendable(true); IG->set_has_dynamically_extendable_classes(true); } class_helper.SetJustRead(ClassHelper::kAnnotations); class_helper.ReadUntilExcluding(ClassHelper::kTypeParameters); intptr_t type_parameter_counts = helper_.ReadListLength(); // read type_parameters list length. ActiveClassScope active_class_scope(&active_class_, out_class); if (!out_class->is_declaration_loaded()) { LoadPreliminaryClass(&class_helper, type_parameter_counts); } else { ASSERT(type_parameter_counts == 0); class_helper.SetJustRead(ClassHelper::kTypeParameters); } if ((FLAG_enable_mirrors || HasPragma::decode(pragma_bits)) && annotation_count > 0) { library.AddMetadata(*out_class, class_offset - correction_offset_); } // We do not register expression evaluation classes with the VM: // The expression evaluation functions should be GC-able as soon as // they are not reachable anymore and we never look them up by name. const bool register_class = library.ptr() != expression_evaluation_library_.ptr(); if (!register_class) { H.SetExpressionEvaluationClass(*out_class); } if (loading_native_wrappers_library_ || !register_class) { FinishClassLoading(*out_class, library, toplevel_class, class_offset, class_index, &class_helper); } helper_.SetOffset(class_end); } void KernelLoader::FinishClassLoading(const Class& klass, const Library& library, const Class& toplevel_class, intptr_t class_offset, const ClassIndex& class_index, ClassHelper* class_helper) { if (klass.is_loaded()) { return; } TIMELINE_DURATION(Thread::Current(), Isolate, "FinishClassLoading"); ActiveClassScope active_class_scope(&active_class_, &klass); // If this is a dart:internal.ClassID class ignore field declarations // contained in the Kernel file and instead inject our own const // fields. const bool discard_fields = klass.InjectCIDFields(); fields_.Clear(); functions_.Clear(); if (!discard_fields) { class_helper->ReadUntilExcluding(ClassHelper::kFields); int field_count = helper_.ReadListLength(); // read list length. for (intptr_t i = 0; i < field_count; ++i) { intptr_t field_offset = helper_.ReaderOffset() - correction_offset_; ActiveMemberScope active_member(&active_class_, nullptr); FieldHelper field_helper(&helper_); field_helper.ReadUntilIncluding(FieldHelper::kSourceUriIndex); const Object& script_class = ClassForScriptAt(klass, field_helper.source_uri_index_); field_helper.ReadUntilExcluding(FieldHelper::kName); const String& name = helper_.ReadNameAsFieldName(); field_helper.SetJustRead(FieldHelper::kName); field_helper.ReadUntilExcluding(FieldHelper::kAnnotations); const intptr_t annotation_count = helper_.ReadListLength(); uint32_t pragma_bits = 0; ReadVMAnnotations(annotation_count, &pragma_bits); field_helper.SetJustRead(FieldHelper::kAnnotations); field_helper.ReadUntilExcluding(FieldHelper::kType); const AbstractType& type = T.BuildTypeWithoutFinalization(); // read type. field_helper.SetJustRead(FieldHelper::kType); const bool is_reflectable = field_helper.position_.IsReal() && !(library.is_dart_scheme() && library.IsPrivate(name)); // In the VM all const fields are implicitly final whereas in Kernel they // are not final because they are not explicitly declared that way. const bool is_final = field_helper.IsConst() || field_helper.IsFinal(); const bool is_late = field_helper.IsLate(); const bool is_extension_member = field_helper.IsExtensionMember(); const bool is_extension_type_member = field_helper.IsExtensionTypeMember(); Field& field = Field::Handle( Z, Field::New(name, field_helper.IsStatic(), is_final, field_helper.IsConst(), is_reflectable, is_late, script_class, type, field_helper.position_, field_helper.end_position_)); field.set_kernel_offset(field_offset); field.set_has_pragma(HasPragma::decode(pragma_bits)); field.set_is_covariant(field_helper.IsCovariant()); field.set_is_generic_covariant_impl( field_helper.IsGenericCovariantImpl()); field.set_is_extension_member(is_extension_member); field.set_is_extension_type_member(is_extension_type_member); field.set_is_shared(SharedPragma::decode(pragma_bits)); ReadInferredType(field, field_offset + library_kernel_offset_); CheckForInitializer(field); // Static fields with initializers are implicitly late. if (field_helper.IsStatic() && field.has_initializer()) { field.set_is_late(true); } field_helper.ReadUntilExcluding(FieldHelper::kInitializer); intptr_t field_initializer_offset = helper_.ReaderOffset(); field_helper.ReadUntilExcluding(FieldHelper::kEnd); { AlternativeReadingScope alt(&helper_.reader_, field_initializer_offset); static_field_value_ = ReadInitialFieldValue(field, &field_helper); } GenerateFieldAccessors(klass, field, &field_helper); if (field.is_static()) { IG->RegisterStaticField(field, static_field_value_); } if ((FLAG_enable_mirrors || HasPragma::decode(pragma_bits)) && annotation_count > 0) { library.AddMetadata(field, field_offset); } fields_.Add(&field); } class_helper->SetJustRead(ClassHelper::kFields); if (klass.is_enum_class()) { // Add static field 'const _deleted_enum_sentinel'. // This field does not need to be of type E. Field& deleted_enum_sentinel = Field::ZoneHandle(Z); deleted_enum_sentinel = Field::New(Symbols::_DeletedEnumSentinel(), /* is_static = */ true, /* is_final = */ true, /* is_const = */ true, /* is_reflectable = */ false, /* is_late = */ false, klass, Object::dynamic_type(), TokenPosition::kNoSource, TokenPosition::kNoSource); IG->RegisterStaticField(deleted_enum_sentinel, Object::Handle()); fields_.Add(&deleted_enum_sentinel); } // TODO(https://dartbug.com/44454): Make VM recognize the Struct class. // // The FfiTrampolines currently allocate subtypes of structs and store // TypedData in them, without using guards because they are force // optimized. We immediately set the guarded_cid_ to kDynamicCid, which // is effectively the same as calling this method first with Pointer and // subsequently with TypedData with field guards. We also set // guarded_list_length_ to kNoFixedLength for similar reasons. if (klass.UserVisibleName() == Symbols::Compound().ptr() && Library::Handle(Z, klass.library()).url() == Symbols::DartFfi().ptr()) { ASSERT_EQUAL(fields_.length(), 2); ASSERT(String::Handle(Z, fields_[0]->name()) .StartsWith(Symbols::_typedDataBase())); fields_[0]->set_guarded_cid(kDynamicCid); fields_[0]->set_is_nullable(true); fields_[0]->set_guarded_list_length(Field::kNoFixedLength); fields_[0]->set_guarded_list_length_in_object_offset( Field::kUnknownLengthOffset); } // Check that subclasses of AbiSpecificInteger have a mapping for the // current ABI. // // TODO(https://github.com/dart-lang/language/issues/1889): If we make // kernel know about the target platform, we can move this check to the // frontend. const auto& super_class = Class::Handle(Z, klass.SuperClass()); if (!super_class.IsNull() && super_class.UserVisibleName() == Symbols::AbiSpecificInteger().ptr() && Library::Handle(Z, super_class.library()).url() == Symbols::DartFfi().ptr()) { const char* error = nullptr; compiler::ffi::NativeType::FromAbstractType( Z, AbstractType::Handle(Z, klass.DeclarationType()), &error); if (error != nullptr) { H.ReportError("%s", error); } } // Due to ReadVMAnnotations(), the klass may have been loaded at this point // (loading the class while evaluating annotations). if (klass.is_loaded()) { return; } klass.SetFields(Array::Handle(Z, MakeFieldsArray())); } class_helper->ReadUntilExcluding(ClassHelper::kConstructors); int constructor_count = helper_.ReadListLength(); // read list length. for (intptr_t i = 0; i < constructor_count; ++i) { intptr_t constructor_offset = helper_.ReaderOffset() - correction_offset_; ActiveMemberScope active_member_scope(&active_class_, nullptr); ConstructorHelper constructor_helper(&helper_); constructor_helper.ReadUntilExcluding(ConstructorHelper::kAnnotations); const intptr_t annotation_count = helper_.ReadListLength(); uint32_t pragma_bits = 0; ReadVMAnnotations(annotation_count, &pragma_bits); constructor_helper.SetJustRead(ConstructorHelper::kAnnotations); constructor_helper.ReadUntilExcluding(ConstructorHelper::kFunction); const String& name = H.DartConstructorName(constructor_helper.canonical_name_); // We can have synthetic constructors, which will not have a source uri // attached to them (which means the index into the source uri table is 0, // see `package:kernel/binary/ast_to_binary::writeUriReference`. const Object* owner = &klass; const intptr_t source_uri_index = constructor_helper.source_uri_index_; if (source_uri_index != 0) { owner = &ClassForScriptAt(klass, source_uri_index); } FunctionType& signature = FunctionType::Handle(Z, FunctionType::New()); const Function& function = Function::ZoneHandle( Z, Function::New(signature, name, UntaggedFunction::kConstructor, false, // is_static constructor_helper.IsConst(), false, // is_abstract constructor_helper.IsExternal(), false, // is_native *owner, constructor_helper.start_position_)); function.set_end_token_pos(constructor_helper.end_position_); function.set_kernel_offset(constructor_offset); signature.set_result_type(T.ReceiverType(klass)); function.set_has_pragma(HasPragma::decode(pragma_bits)); function.set_is_visible(!InvisibleFunctionPragma::decode(pragma_bits)); function.SetIsDynamicallyOverridden( DynModuleCanBeOverriddenPragma::decode(pragma_bits)); FunctionNodeHelper function_node_helper(&helper_); function_node_helper.ReadUntilExcluding( FunctionNodeHelper::kTypeParameters); T.SetupFunctionParameters(klass, function, true, // is_method false, // is_closure &function_node_helper); T.SetupUnboxingInfoMetadata(function, library_kernel_offset_); if (library.is_dart_scheme() && H.IsPrivate(constructor_helper.canonical_name_)) { function.set_is_reflectable(false); } if (constructor_helper.IsSynthetic()) { function.set_is_debuggable(false); } function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd); constructor_helper.SetJustRead(ConstructorHelper::kFunction); constructor_helper.ReadUntilExcluding(ConstructorHelper::kEnd); if (klass.is_finalized()) { // The owner class has already been marked as finalized (e.g. class // 'NativeFieldWrapperClass1'), so the signature of this added constructor // must be finalized here, since finalization of member types will not be // called anymore. signature ^= ClassFinalizer::FinalizeType(signature); function.SetSignature(signature); } functions_.Add(&function); if ((FLAG_enable_mirrors || HasPragma::decode(pragma_bits)) && annotation_count > 0) { library.AddMetadata(function, constructor_offset); } } // Due to ReadVMAnnotations(), the klass may have been loaded at this point // (loading the class while evaluating annotations). if (klass.is_loaded()) { return; } // Everything up til the procedures are skipped implicitly, and class_helper // is no longer used. intptr_t procedure_count = class_index.procedure_count(); // Procedure offsets within a class index are whole program offsets and not // relative to the library of the class. Hence, we need a correction to get // the currect procedure offset within the current data. intptr_t correction = correction_offset_ - library_kernel_offset_; intptr_t next_procedure_offset = class_index.ProcedureOffset(0) + correction; for (intptr_t i = 0; i < procedure_count; ++i) { helper_.SetOffset(next_procedure_offset); next_procedure_offset = class_index.ProcedureOffset(i + 1) + correction; LoadProcedure(library, klass, true, next_procedure_offset); // LoadProcedure calls Library::GetMetadata which invokes Dart code // which may recursively trigger class finalization and FinishClassLoading. // In such case, return immediately and avoid overwriting already finalized // functions with freshly loaded and not yet finalized. if (klass.is_loaded()) { return; } } klass.SetFunctions(Array::Handle(MakeFunctionsArray())); ASSERT(!klass.is_loaded()); klass.set_is_loaded(true); } void KernelLoader::FinishLoading(const Class& klass) { ASSERT(klass.IsTopLevel() || (klass.kernel_offset() > 0)); Zone* zone = Thread::Current()->zone(); const Library& library = Library::Handle(zone, klass.library()); const Class& toplevel_class = Class::Handle(zone, library.toplevel_class()); const auto& library_kernel_data = TypedDataView::Handle(zone, library.KernelLibrary()); ASSERT(!library_kernel_data.IsNull()); const auto& kernel_info = KernelProgramInfo::Handle(zone, klass.KernelProgramInfo()); const intptr_t library_kernel_offset = kernel_info.KernelLibraryStartOffset(library.kernel_library_index()); KernelLoader kernel_loader(kernel_info, library_kernel_data, library_kernel_offset); LibraryIndex library_index(library_kernel_data); if (klass.IsTopLevel()) { ASSERT(klass.ptr() == toplevel_class.ptr()); kernel_loader.FinishTopLevelClassLoading(klass, library, library_index); return; } const intptr_t class_offset = klass.kernel_offset(); ClassIndex class_index( library_kernel_data, class_offset, // Class offsets in library index are whole program offsets. // Hence, we need to add |library_kernel_offset| to // |class_offset| to lookup the entry for the class in the library // index. library_index.SizeOfClassAtOffset(class_offset + library_kernel_offset)); kernel_loader.helper_.SetOffset(class_offset); ClassHelper class_helper(&kernel_loader.helper_); kernel_loader.FinishClassLoading(klass, library, toplevel_class, class_offset, class_index, &class_helper); } // Read annotations on a procedure or a class to identify potential VM-specific // directives. // // Output parameters: // // `native_name`: the native name if @pragma('vm:external-name)` was found. // // `pragma_bits`: any recognized pragma that was found // void KernelLoader::ReadVMAnnotations(intptr_t annotation_count, uint32_t* pragma_bits, String* native_name) { *pragma_bits = 0; if (annotation_count == 0) { return; } for (intptr_t i = 0; i < annotation_count; ++i) { const intptr_t tag = helper_.PeekTag(); if (tag == kConstantExpression || tag == kFileUriConstantExpression) { helper_.ReadByte(); // Skip the tag. helper_.ReadPosition(); // Skip fileOffset. if (tag == kFileUriConstantExpression) { helper_.ReadUInt(); // Skip uri. } helper_.SkipDartType(); // Skip type. const intptr_t index_in_constant_table = helper_.ReadUInt(); // Prepare lazy constant reading. ConstantReader constant_reader(&helper_, &active_class_); intptr_t name_index = -1; intptr_t options_index = -1; if (constant_reader.IsPragmaInstanceConstant( index_in_constant_table, &name_index, &options_index)) { *pragma_bits = HasPragma::update(true, *pragma_bits); if (constant_reader.IsStringConstant(name_index, "vm:invisible")) { *pragma_bits = InvisibleFunctionPragma::update(true, *pragma_bits); } if (constant_reader.IsStringConstant(name_index, "vm:external-name")) { *pragma_bits = ExternalNamePragma::update(true, *pragma_bits); constant_reader.GetStringConstant(options_index, native_name); } if (constant_reader.IsStringConstant(name_index, "vm:isolate-unsendable")) { *pragma_bits = IsolateUnsendablePragma::update(true, *pragma_bits); } if (constant_reader.IsStringConstant(name_index, "vm:deeply-immutable")) { *pragma_bits = DeeplyImmutablePragma::update(true, *pragma_bits); } if (constant_reader.IsStringConstant(name_index, "vm:ffi:native")) { *pragma_bits = FfiNativePragma::update(true, *pragma_bits); } if (constant_reader.IsStringConstant(name_index, "vm:shared")) { if (!is_experimental_shared_data_enabled) { FATAL( "Encountered vm:shared when functionality is disabled. " "Pass --experimental-shared-data"); } *pragma_bits = SharedPragma::update(true, *pragma_bits); } if (constant_reader.IsStringConstant(name_index, "dyn-module:extendable")) { *pragma_bits = DynModuleExtendablePragma::update(true, *pragma_bits); } if (constant_reader.IsStringConstant(name_index, "dyn-module:can-be-overridden")) { *pragma_bits = DynModuleCanBeOverriddenPragma::update(true, *pragma_bits); } } } else { helper_.SkipExpression(); continue; } } } void KernelLoader::LoadProcedure(const Library& library, const Class& owner, bool in_class, intptr_t procedure_end) { intptr_t procedure_offset = helper_.ReaderOffset() - correction_offset_; ProcedureHelper procedure_helper(&helper_); procedure_helper.ReadUntilExcluding(ProcedureHelper::kAnnotations); // CFE adds 'member signature' abstract functions to a legacy class deriving // or implementing an opted-in interface. The signature of these functions is // legacy erased and used as the target of interface calls. They are used for // static reasoning about the program by CFE, but not really needed by the VM. // In certain situations (e.g. issue 162073826), a large number of these // additional functions can cause strain on the VM. They are therefore skipped // in jit mode and their associated origin function is used instead as // interface call target. if (!FLAG_precompiled_mode && procedure_helper.IsMemberSignature()) { helper_.SetOffset(procedure_end); return; } const String& name = H.DartProcedureName(procedure_helper.canonical_name_); bool is_method = in_class && !procedure_helper.IsStatic(); bool is_abstract = procedure_helper.IsAbstract(); bool is_external = procedure_helper.IsExternal(); bool is_extension_member = procedure_helper.IsExtensionMember(); bool is_extension_type_member = procedure_helper.IsExtensionTypeMember(); bool is_synthetic = procedure_helper.IsSynthetic(); String& native_name = String::Handle(Z); uint32_t pragma_bits = 0; const intptr_t annotation_count = helper_.ReadListLength(); ReadVMAnnotations(annotation_count, &pragma_bits, &native_name); is_external = is_external && native_name.IsNull(); procedure_helper.SetJustRead(ProcedureHelper::kAnnotations); const Object& script_class = ClassForScriptAt(owner, procedure_helper.source_uri_index_); UntaggedFunction::Kind kind = GetFunctionType(procedure_helper.kind_); // We do not register expression evaluation libraries with the VM: // The expression evaluation functions should be GC-able as soon as // they are not reachable anymore and we never look them up by name. const bool register_function = !name.Equals(Symbols::DebugProcedureName()); const bool is_ffi_native = FfiNativePragma::decode(pragma_bits); const FunctionType& signature = FunctionType::Handle(Z, FunctionType::New()); const Function& function = Function::ZoneHandle( Z, Function::New(signature, name, kind, !is_method, // is_static false, // is_const is_abstract, is_external, !native_name.IsNull() || is_ffi_native, // is_native script_class, procedure_helper.start_position_)); function.set_has_pragma(HasPragma::decode(pragma_bits)); function.set_end_token_pos(procedure_helper.end_position_); function.set_is_synthetic(procedure_helper.IsNoSuchMethodForwarder() || procedure_helper.IsMemberSignature() || is_synthetic); function.set_is_visible(!InvisibleFunctionPragma::decode(pragma_bits)); function.SetIsDynamicallyOverridden( DynModuleCanBeOverriddenPragma::decode(pragma_bits)); if (register_function) { functions_.Add(&function); } else { H.SetExpressionEvaluationFunction(function); } function.set_kernel_offset(procedure_offset); function.set_is_extension_member(is_extension_member); function.set_is_extension_type_member(is_extension_type_member); if ((library.is_dart_scheme() && H.IsPrivate(procedure_helper.canonical_name_)) || (function.is_static() && (library.ptr() == Library::InternalLibrary()))) { function.set_is_reflectable(false); } if (procedure_helper.IsMemberSignature()) { function.set_is_reflectable(false); } ActiveMemberScope active_member(&active_class_, &function); procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction); FunctionNodeHelper function_node_helper(&helper_); function_node_helper.ReadUntilIncluding(FunctionNodeHelper::kDartAsyncMarker); if (function_node_helper.async_marker_ == FunctionNodeHelper::kAsync) { function.set_modifier(UntaggedFunction::kAsync); function.set_is_inlinable(false); ASSERT(function.IsAsyncFunction()); } else if (function_node_helper.async_marker_ == FunctionNodeHelper::kAsyncStar) { function.set_modifier(UntaggedFunction::kAsyncGen); function.set_is_inlinable(false); ASSERT(function.IsAsyncGenerator()); } else if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncStar) { function.set_modifier(UntaggedFunction::kSyncGen); function.set_is_inlinable(false); ASSERT(function.IsSyncGenerator()); } else { ASSERT(function_node_helper.async_marker_ == FunctionNodeHelper::kSync); ASSERT(!function.IsAsyncFunction()); ASSERT(!function.IsAsyncGenerator()); ASSERT(!function.IsSyncGenerator()); } if (!native_name.IsNull()) { function.set_native_name(native_name); } function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); T.SetupFunctionParameters(owner, function, is_method, false, // is_closure &function_node_helper); T.SetupUnboxingInfoMetadata(function, library_kernel_offset_); function_node_helper.ReadUntilExcluding( FunctionNodeHelper::kRedirectingFactoryTarget); function.set_is_redirecting_factory(helper_.ReadTag() == kSomething); // Everything else is skipped implicitly, and procedure_helper and // function_node_helper are no longer used. helper_.SetOffset(procedure_end); if (annotation_count > 0) { library.AddMetadata(function, procedure_offset); } } const Object& KernelLoader::ClassForScriptAt(const Class& klass, intptr_t source_uri_index) { const Script& correct_script = Script::Handle(Z, ScriptAt(source_uri_index)); if (klass.script() != correct_script.ptr()) { // Lazily create the [patch_classes_] array in case we need it. if (patch_classes_.IsNull()) { const Array& scripts = Array::Handle(Z, kernel_program_info_.scripts()); ASSERT(!scripts.IsNull()); patch_classes_ = Array::New(scripts.Length(), Heap::kOld); } // Use cache for patch classes. This works best for in-order usages. PatchClass& patch_class = PatchClass::Handle(Z); patch_class ^= patch_classes_.At(source_uri_index); if (patch_class.IsNull() || patch_class.wrapped_class() != klass.ptr()) { const auto& lib = Library::Handle(klass.library()); patch_class = PatchClass::New(klass, kernel_program_info_, correct_script); patch_class.set_kernel_library_index(lib.kernel_library_index()); patch_classes_.SetAt(source_uri_index, patch_class); } return patch_class; } return klass; } ScriptPtr KernelLoader::LoadScriptAt(intptr_t index, UriToSourceTable* uri_to_source_table) { const String& uri_string = helper_.SourceTableUriFor(index); const String& import_uri_string = helper_.SourceTableImportUriFor(index); auto& constant_coverage = TypedDataView::Handle(Z); NOT_IN_PRODUCT(constant_coverage = helper_.GetConstantCoverageFor(index)); String& sources = String::Handle(Z); TypedData& line_starts = TypedData::Handle(Z); if (uri_to_source_table != nullptr) { UriToSourceTableEntry wrapper; wrapper.uri = &uri_string; UriToSourceTableEntry* pair = uri_to_source_table->LookupValue(&wrapper); if (pair != nullptr) { sources = pair->sources->ptr(); line_starts = pair->line_starts->ptr(); } } if (sources.IsNull() || line_starts.IsNull()) { const String& script_source = helper_.GetSourceFor(index); line_starts = helper_.GetLineStartsFor(index); if (script_source.ptr() == Symbols::Empty().ptr() && line_starts.Length() == 0 && uri_string.Length() > 0) { // Entry included only to provide URI - actual source should already exist // in the VM, so try to find it. Library& lib = Library::Handle(Z); Script& script = Script::Handle(Z); const GrowableObjectArray& libs = GrowableObjectArray::Handle(IG->object_store()->libraries()); for (intptr_t i = 0; i < libs.Length(); i++) { lib ^= libs.At(i); script = lib.LookupScript(uri_string, /* useResolvedUri = */ true); if (!script.IsNull()) { sources = script.Source(); line_starts = script.line_starts(); break; } } } else { sources = script_source.ptr(); } } const Script& script = Script::Handle(Z, Script::New(import_uri_string, uri_string, sources)); script.InitializeFromKernel(kernel_program_info_, index, line_starts, constant_coverage); return script.ptr(); } ObjectPtr KernelLoader::ReadInitialFieldValue(const Field& field, FieldHelper* field_helper) { const Tag tag = helper_.PeekTag(); const bool has_initializer = (tag == kSomething); if (has_initializer) { SimpleExpressionConverter converter(&H, &helper_); const bool has_simple_initializer = converter.IsSimple(helper_.ReaderOffset() + 1); // ignore the tag. if (has_simple_initializer) { if (field_helper->IsStatic()) { return converter.SimpleValue().ptr(); } else { field.RecordStore(converter.SimpleValue()); } } } if (field_helper->IsStatic()) { if (!has_initializer && !field_helper->IsLate()) { // Static fields without an initializer are implicitly initialized to // null. return Instance::null(); } } ASSERT(field.NeedsGetter()); // If static, we do need a getter that evaluates the initializer if necessary. return field_helper->IsStatic() ? Object::sentinel().ptr() : Object::null(); } void KernelLoader::GenerateFieldAccessors(const Class& klass, const Field& field, FieldHelper* field_helper) { const bool needs_getter = field.NeedsGetter(); const bool needs_setter = field.NeedsSetter(); if (!needs_getter && !needs_setter) { return; } const Object& script_class = ClassForScriptAt(klass, field_helper->source_uri_index_); const AbstractType& field_type = AbstractType::Handle(Z, field.type()); if (needs_getter) { const String& getter_name = H.DartGetterName(field_helper->canonical_name_getter_); const FunctionType& signature = FunctionType::Handle(Z, FunctionType::New()); Function& getter = Function::ZoneHandle( Z, Function::New( signature, getter_name, field_helper->IsStatic() ? UntaggedFunction::kImplicitStaticGetter : UntaggedFunction::kImplicitGetter, field_helper->IsStatic(), // The functions created by the parser have is_const for static // fields that are const (not just final) and they have is_const // for non-static fields that are final. field_helper->IsStatic() ? field_helper->IsConst() : field_helper->IsFinal(), false, // is_abstract false, // is_external false, // is_native script_class, field_helper->position_)); functions_.Add(&getter); getter.set_end_token_pos(field_helper->end_position_); getter.set_kernel_offset(field.kernel_offset()); signature.set_result_type(field_type); getter.set_is_debuggable(false); getter.set_accessor_field(field); getter.set_is_extension_member(field.is_extension_member()); getter.set_is_extension_type_member(field.is_extension_type_member()); H.SetupFieldAccessorFunction(klass, getter, field_type); T.SetupUnboxingInfoMetadataForFieldAccessors(getter, library_kernel_offset_); } if (needs_setter) { // Only static fields can be const. ASSERT(!field_helper->IsConst()); const String& setter_name = H.DartSetterName(field_helper->canonical_name_setter_); const FunctionType& signature = FunctionType::Handle(Z, FunctionType::New()); Function& setter = Function::ZoneHandle( Z, Function::New(signature, setter_name, UntaggedFunction::kImplicitSetter, field_helper->IsStatic(), false, // is_const false, // is_abstract false, // is_external false, // is_native script_class, field_helper->position_)); functions_.Add(&setter); setter.set_end_token_pos(field_helper->end_position_); setter.set_kernel_offset(field.kernel_offset()); signature.set_result_type(Object::void_type()); setter.set_is_debuggable(false); setter.set_accessor_field(field); setter.set_is_extension_member(field.is_extension_member()); setter.set_is_extension_type_member(field.is_extension_type_member()); H.SetupFieldAccessorFunction(klass, setter, field_type); T.SetupUnboxingInfoMetadataForFieldAccessors(setter, library_kernel_offset_); } } LibraryPtr KernelLoader::LookupLibraryOrNull(NameIndex library) { LibraryPtr result; name_index_handle_ = Smi::New(library); { result = kernel_program_info_.LookupLibrary(thread_, name_index_handle_); NoSafepointScope no_safepoint_scope(thread_); if (result != Library::null()) { return result; } } const String& url = H.DartString(H.CanonicalNameString(library)); { result = Library::LookupLibrary(thread_, url); NoSafepointScope no_safepoint_scope(thread_); if (result == Library::null()) { return result; } } const Library& handle = Library::Handle(Z, result); name_index_handle_ = Smi::New(library); return kernel_program_info_.InsertLibrary(thread_, name_index_handle_, handle); } LibraryPtr KernelLoader::LookupLibrary(NameIndex library) { name_index_handle_ = Smi::New(library); { LibraryPtr result = kernel_program_info_.LookupLibrary(thread_, name_index_handle_); NoSafepointScope no_safepoint_scope(thread_); if (result != Library::null()) { return result; } } Library& handle = Library::Handle(Z); const String& url = H.DartSymbolPlain(H.CanonicalNameString(library)); // We do not register expression evaluation libraries with the VM: // The expression evaluation functions should be GC-able as soon as // they are not reachable anymore and we never look them up by name. if (url.Equals(Symbols::EvalSourceUri())) { if (expression_evaluation_library_.IsNull()) { handle = Library::New(url); expression_evaluation_library_ = handle.ptr(); } return expression_evaluation_library_.ptr(); } handle = Library::LookupLibrary(thread_, url); if (handle.IsNull()) { handle = Library::New(url); handle.Register(thread_); } ASSERT(!handle.IsNull()); name_index_handle_ = Smi::New(library); return kernel_program_info_.InsertLibrary(thread_, name_index_handle_, handle); } LibraryPtr KernelLoader::LookupLibraryFromClass(NameIndex klass) { return LookupLibrary(H.CanonicalNameParent(klass)); } ClassPtr KernelLoader::LookupClass(const Library& library, NameIndex klass) { name_index_handle_ = Smi::New(klass); { ClassPtr raw_class = kernel_program_info_.LookupClass(thread_, name_index_handle_); NoSafepointScope no_safepoint_scope(thread_); if (raw_class != Class::null()) { return raw_class; } } ASSERT(!library.IsNull()); const String& name = H.DartClassName(klass); Class& handle = Class::Handle(Z, library.LookupClass(name)); bool register_class = true; if (handle.IsNull()) { // We do not register expression evaluation classes with the VM: // The expression evaluation functions should be GC-able as soon as // they are not reachable anymore and we never look them up by name. register_class = library.ptr() != expression_evaluation_library_.ptr(); handle = Class::New(library, name, Script::Handle(Z), TokenPosition::kNoSource, register_class); if (register_class) { library.AddClass(handle); } } ASSERT(!handle.IsNull()); if (register_class) { name_index_handle_ = Smi::New(klass); kernel_program_info_.InsertClass(thread_, name_index_handle_, handle); } return handle.ptr(); } UntaggedFunction::Kind KernelLoader::GetFunctionType( ProcedureHelper::Kind procedure_kind) { intptr_t lookuptable[] = { UntaggedFunction::kRegularFunction, // Procedure::kMethod UntaggedFunction::kGetterFunction, // Procedure::kGetter UntaggedFunction::kSetterFunction, // Procedure::kSetter UntaggedFunction::kRegularFunction, // Procedure::kOperator UntaggedFunction::kConstructor, // Procedure::kFactory }; intptr_t kind = static_cast(procedure_kind); ASSERT(0 <= kind && kind <= ProcedureHelper::kFactory); return static_cast(lookuptable[kind]); } FunctionPtr KernelLoader::LoadClosureFunction(const Function& parent_function, const Object& closure_owner) { const intptr_t func_decl_offset = helper_.ReaderOffset(); const Tag tag = helper_.ReadTag(); ASSERT((tag == kFunctionExpression) || (tag == kFunctionDeclaration)); const bool is_declaration = (tag == kFunctionDeclaration); TokenPosition position = helper_.ReadPosition(); // read position. uint32_t pragma_bits = 0; intptr_t annotation_count = 0; const String* name; if (is_declaration) { // Read variable declaration. VariableDeclarationHelper variable_helper(&helper_); variable_helper.ReadUntilExcluding(VariableDeclarationHelper::kAnnotations); const intptr_t annotation_count = helper_.ReadListLength(); ReadVMAnnotations(annotation_count, &pragma_bits); variable_helper.SetJustRead(VariableDeclarationHelper::kAnnotations); variable_helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd); name = &H.DartSymbolObfuscate(variable_helper.name_index_); } else { name = &Symbols::AnonymousClosure(); } const intptr_t func_node_offset = helper_.ReaderOffset(); FunctionNodeHelper function_node_helper(&helper_); function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); Function& function = Function::Handle(Z); if (!closure_owner.IsNull()) { function = Function::NewClosureFunctionWithKind( UntaggedFunction::kClosureFunction, *name, parent_function, parent_function.is_static(), position, closure_owner); } else { function = Function::NewClosureFunction(*name, parent_function, position); } const bool has_pragma = HasPragma::decode(pragma_bits); function.set_has_pragma(has_pragma); function.set_is_visible(!InvisibleFunctionPragma::decode(pragma_bits)); if ((FLAG_enable_mirrors && (annotation_count > 0)) || has_pragma) { const auto& lib = Library::Handle(Z, Class::Handle(Z, function.Owner()).library()); lib.AddMetadata(function, func_decl_offset); } if (function_node_helper.async_marker_ == FunctionNodeHelper::kAsync) { function.set_modifier(UntaggedFunction::kAsync); function.set_is_inlinable(false); ASSERT(function.IsAsyncFunction()); } else if (function_node_helper.async_marker_ == FunctionNodeHelper::kAsyncStar) { function.set_modifier(UntaggedFunction::kAsyncGen); function.set_is_inlinable(false); ASSERT(function.IsAsyncGenerator()); } else if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncStar) { function.set_modifier(UntaggedFunction::kSyncGen); function.set_is_inlinable(false); ASSERT(function.IsSyncGenerator()); } else { ASSERT(function_node_helper.async_marker_ == FunctionNodeHelper::kSync); ASSERT(!function.IsAsyncFunction()); ASSERT(!function.IsAsyncGenerator()); ASSERT(!function.IsSyncGenerator()); } // If the start token position is synthetic, the end token position // should be as well. function.set_end_token_pos( position.IsReal() ? function_node_helper.end_position_ : position); function.set_kernel_offset(func_node_offset); T.SetupFunctionParameters(Class::Handle(Z), function, false, // is_method true, // is_closure &function_node_helper); // type_translator->SetupUnboxingInfoMetadata is not called here at the // moment because closures do not have unboxed parameters and return // value // Finalize function type. FunctionType& signature = FunctionType::Handle(Z, function.signature()); signature ^= ClassFinalizer::FinalizeType(signature); function.SetSignature(signature); ClosureFunctionsCache::AddClosureFunctionLocked(function); return function.ptr(); } FunctionPtr KernelLoader::GetClosureFunction(Thread* thread, intptr_t func_decl_offset, const Function& member_function, const Function& parent_function, const Object& closure_owner) { Zone* zone = thread->zone(); Function& function = Function::Handle(zone); intptr_t func_node_offset = -1; const auto& kernel_info = KernelProgramInfo::Handle(zone, member_function.KernelProgramInfo()); const auto& library_kernel_data = TypedDataView::Handle(zone, member_function.KernelLibrary()); ASSERT(!library_kernel_data.IsNull()); const intptr_t library_kernel_offset = member_function.KernelLibraryOffset(); KernelLoader kernel_loader(kernel_info, library_kernel_data, library_kernel_offset); { // TODO(alexmarkov): Use func_decl_offset as a key in ClosureFunctionsCache // instead of func_node_offset and avoid this reading. kernel_loader.helper_.SetOffset(func_decl_offset); kernel_loader.helper_.ReadUntilFunctionNode(); func_node_offset = kernel_loader.helper_.ReaderOffset(); { SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock()); function = ClosureFunctionsCache::LookupClosureFunctionLocked( member_function, func_node_offset); if (!function.IsNull()) { return function.ptr(); } } } SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock()); function = ClosureFunctionsCache::LookupClosureFunctionLocked( member_function, func_node_offset); if (function.IsNull()) { ActiveClassScope active_class_scope( &kernel_loader.active_class_, &Class::Handle(zone, member_function.Owner())); ActiveMemberScope active_member(&kernel_loader.active_class_, &member_function); ActiveTypeParametersScope active_type_params( &kernel_loader.active_class_, member_function, &FunctionType::Handle(zone, parent_function.signature()), zone); kernel_loader.helper_.SetOffset(func_decl_offset); function = kernel_loader.LoadClosureFunction(parent_function, closure_owner); } return function.ptr(); } } // namespace kernel } // namespace dart #endif // !defined(DART_PRECOMPILED_RUNTIME)