diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc index 2b20b44d08f5..da714737b430 100644 --- a/runtime/vm/app_snapshot.cc +++ b/runtime/vm/app_snapshot.cc @@ -1873,12 +1873,12 @@ class CodeSerializationCluster : public SerializationCluster { // in the current loading unit). ObjectPoolPtr pool = code->untag()->object_pool_; if (s->kind() == Snapshot::kFullAOT) { - TracePool(s, pool, /*only_code=*/is_deferred); + TracePool(s, pool, /*only_call_targets=*/is_deferred); } else { if (s->InCurrentLoadingUnitOrRoot(pool)) { s->Push(pool); } else { - TracePool(s, pool, /*only_code=*/true); + TracePool(s, pool, /*only_call_targets=*/true); } } @@ -1946,7 +1946,7 @@ class CodeSerializationCluster : public SerializationCluster { #endif } - void TracePool(Serializer* s, ObjectPoolPtr pool, bool only_code) { + void TracePool(Serializer* s, ObjectPoolPtr pool, bool only_call_targets) { if (pool == ObjectPool::null()) { return; } @@ -1957,8 +1957,18 @@ class CodeSerializationCluster : public SerializationCluster { auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]); if (entry_type == ObjectPool::EntryType::kTaggedObject) { const ObjectPtr target = pool->untag()->data()[i].raw_obj_; - if (!only_code || target->IsCode()) { + // A field is a call target because its initializer may be called + // indirectly by passing the field to the runtime. A const closure + // is a call target because its function may be called indirectly + // via a closure call. + if (!only_call_targets || target->IsCode() || target->IsFunction() || + target->IsField() || target->IsClosure()) { s->Push(target); + } else { + intptr_t cid = target->GetClassIdMayBeSmi(); + if (cid >= kNumPredefinedCids) { + s->Push(s->isolate_group()->class_table()->At(cid)); + } } } } diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc index ee450a677ced..6c6a0239d1ce 100644 --- a/runtime/vm/compiler/aot/precompiler.cc +++ b/runtime/vm/compiler/aot/precompiler.cc @@ -424,6 +424,7 @@ Precompiler::Precompiler(Thread* thread) typeparams_to_retain_(), consts_to_retain_(), seen_table_selectors_(), + api_uses_(), error_(Error::Handle()), get_runtime_type_is_unique_(false) { ASSERT(Precompiler::singleton_ == NULL); @@ -662,6 +663,8 @@ void Precompiler::DoCompileAll() { ProgramVisitor::Dedup(T); } + PruneDictionaries(); + if (retained_reasons_writer_ != nullptr) { reasons_writer.Write(); retained_reasons_writer_ = nullptr; @@ -752,6 +755,7 @@ void Precompiler::AddRoots() { } } if (!main.IsNull()) { + AddApiUse(main); if (lib.LookupLocalFunction(name) == Function::null()) { retain_root_library_caches_ = true; } @@ -1464,6 +1468,14 @@ bool Precompiler::IsHitByTableSelector(const Function& function) { return seen_table_selectors_.HasKey(selector_id); } +void Precompiler::AddApiUse(const Object& obj) { + api_uses_.Insert(&Object::ZoneHandle(Z, obj.ptr())); +} + +bool Precompiler::HasApiUse(const Object& obj) { + return api_uses_.HasKey(&obj); +} + void Precompiler::AddInstantiatedClass(const Class& cls) { if (is_tracing()) { tracer_->WriteClassInstantiationRef(cls); @@ -1523,6 +1535,7 @@ void Precompiler::AddAnnotatedRoots() { &reusable_object_handle) == EntryPointPragma::kAlways) { AddInstantiatedClass(cls); + AddApiUse(cls); } } @@ -1541,6 +1554,7 @@ void Precompiler::AddAnnotatedRoots() { if (pragma == EntryPointPragma::kNever) continue; AddField(field); + AddApiUse(field); if (!field.is_static()) { if (pragma != EntryPointPragma::kSetterOnly) { @@ -1569,6 +1583,7 @@ void Precompiler::AddAnnotatedRoots() { type == EntryPointPragma::kCallOnly) { functions_with_entry_point_pragmas_.Insert(function); AddFunction(function, RetainReasons::kEntryPointPragma); + AddApiUse(function); } if ((type == EntryPointPragma::kAlways || @@ -1578,10 +1593,16 @@ void Precompiler::AddAnnotatedRoots() { function2 = function.ImplicitClosureFunction(); functions_with_entry_point_pragmas_.Insert(function2); AddFunction(function2, RetainReasons::kEntryPointPragma); + + // Not `function2`: Dart_GetField will lookup the regular function + // and get the implicit closure function from that. + AddApiUse(function); } if (function.IsGenerativeConstructor()) { AddInstantiatedClass(cls); + AddApiUse(function); + AddApiUse(cls); } } if (function.kind() == UntaggedFunction::kImplicitGetter && @@ -1591,6 +1612,7 @@ void Precompiler::AddAnnotatedRoots() { if (function.accessor_field() == field.ptr()) { functions_with_entry_point_pragmas_.Insert(function); AddFunction(function, RetainReasons::kImplicitGetter); + AddApiUse(function); } } } @@ -1601,6 +1623,7 @@ void Precompiler::AddAnnotatedRoots() { if (function.accessor_field() == field.ptr()) { functions_with_entry_point_pragmas_.Insert(function); AddFunction(function, RetainReasons::kImplicitSetter); + AddApiUse(function); } } } @@ -1611,9 +1634,15 @@ void Precompiler::AddAnnotatedRoots() { if (function.accessor_field() == field.ptr()) { functions_with_entry_point_pragmas_.Insert(function); AddFunction(function, RetainReasons::kImplicitStaticGetter); + AddApiUse(function); } } } + if (function.is_native()) { + // The embedder will need to lookup this library to provide the native + // resolver, even if there are no embedder calls into the library. + AddApiUse(lib); + } } implicit_getters = GrowableObjectArray::null(); @@ -3069,6 +3098,166 @@ void Precompiler::DiscardCodeObjects() { } } +void Precompiler::PruneDictionaries() { + // PRODUCT-only: pruning interferes with various uses of the service protocol, + // including heap analysis tools. +#if defined(PRODUCT) + class PruneDictionariesVisitor { + public: + GrowableObjectArrayPtr PruneLibraries( + const GrowableObjectArray& libraries) { + for (intptr_t i = 0; i < libraries.Length(); i++) { + lib_ ^= libraries.At(i); + bool retain = PruneLibrary(lib_); + if (retain) { + lib_.set_index(retained_libraries_.Length()); + retained_libraries_.Add(lib_); + } else { + lib_.set_index(-1); + lib_.set_private_key(null_string_); + } + } + + Library::RegisterLibraries(Thread::Current(), retained_libraries_); + return retained_libraries_.ptr(); + } + + bool PruneLibrary(const Library& lib) { + dict_ = lib.dictionary(); + intptr_t dict_size = dict_.Length() - 1; + intptr_t used = 0; + for (intptr_t i = 0; i < dict_size; i++) { + entry_ = dict_.At(i); + if (entry_.IsNull()) continue; + + bool retain = false; + if (entry_.IsClass()) { + // dart:async: Fix async stack trace lookups in dart:async to annotate + // entry points or fail gracefully. + // dart:core, dart:collection, dart:typed_data: Isolate messaging + // between groups allows any class in these libraries. + retain = PruneClass(Class::Cast(entry_)) || + (lib.url() == Symbols::DartAsync().ptr()) || + (lib.url() == Symbols::DartCore().ptr()) || + (lib.url() == Symbols::DartCollection().ptr()) || + (lib.url() == Symbols::DartTypedData().ptr()); + } else if (entry_.IsFunction() || entry_.IsField()) { + retain = precompiler_->HasApiUse(entry_); + } else { + FATAL("Unexpected library entry: %s", entry_.ToCString()); + } + if (retain) { + used++; + } else { + dict_.SetAt(i, Object::null_object()); + } + } + lib.RehashDictionary(dict_, used * 4 / 3 + 1); + + bool retain = used > 0; + cls_ = lib.toplevel_class(); + if (PruneClass(cls_)) { + retain = true; + } + if (lib.is_dart_scheme()) { + retain = true; + } + if (lib.ptr() == root_lib_.ptr()) { + retain = true; + } + if (precompiler_->HasApiUse(lib)) { + retain = true; + } + return retain; + } + + bool PruneClass(const Class& cls) { + bool retain = precompiler_->HasApiUse(cls); + + functions_ = cls.functions(); + retained_functions_ = GrowableObjectArray::New(); + for (intptr_t i = 0; i < functions_.Length(); i++) { + function_ ^= functions_.At(i); + if (precompiler_->HasApiUse(function_)) { + retained_functions_.Add(function_); + retain = true; + } else if (precompiler_->functions_called_dynamically_.ContainsKey( + function_)) { + retained_functions_.Add(function_); + // No `retain = true`: the function must appear in the method + // dictionary for lookup, but the class may still be removed from the + // library. + } + } + if (retained_functions_.Length() > 0) { + functions_ = Array::MakeFixedLength(retained_functions_); + cls.SetFunctions(functions_); + } else { + cls.SetFunctions(Object::empty_array()); + } + + fields_ = cls.fields(); + retained_fields_ = GrowableObjectArray::New(); + for (intptr_t i = 0; i < fields_.Length(); i++) { + field_ ^= fields_.At(i); + if (precompiler_->HasApiUse(field_)) { + retained_fields_.Add(field_); + retain = true; + } + } + if (retained_fields_.Length() > 0) { + fields_ = Array::MakeFixedLength(retained_fields_); + cls.SetFields(fields_); + } else { + cls.SetFields(Object::empty_array()); + } + + return retain; + } + + explicit PruneDictionariesVisitor(Precompiler* precompiler, Zone* zone) + : precompiler_(precompiler), + lib_(Library::Handle(zone)), + dict_(Array::Handle(zone)), + entry_(Object::Handle(zone)), + cls_(Class::Handle(zone)), + functions_(Array::Handle(zone)), + fields_(Array::Handle(zone)), + function_(Function::Handle(zone)), + field_(Field::Handle(zone)), + retained_functions_(GrowableObjectArray::Handle(zone)), + retained_fields_(GrowableObjectArray::Handle(zone)), + retained_libraries_( + GrowableObjectArray::Handle(zone, GrowableObjectArray::New())), + root_lib_(Library::Handle( + zone, + precompiler->isolate_group()->object_store()->root_library())), + null_string_(String::Handle(zone)) {} + + private: + Precompiler* const precompiler_; + Library& lib_; + Array& dict_; + Object& entry_; + Class& cls_; + Array& functions_; + Array& fields_; + Function& function_; + Field& field_; + GrowableObjectArray& retained_functions_; + GrowableObjectArray& retained_fields_; + const GrowableObjectArray& retained_libraries_; + const Library& root_lib_; + const String& null_string_; + }; + + HANDLESCOPE(T); + SafepointWriteRwLocker ml(T, T->isolate_group()->program_lock()); + PruneDictionariesVisitor visitor(this, Z); + libraries_ = visitor.PruneLibraries(libraries_); +#endif // defined(PRODUCT) +} + // Traits for the HashTable template. struct CodeKeyTraits { static uint32_t Hash(const Object& key) { return Code::Cast(key).Size(); } diff --git a/runtime/vm/compiler/aot/precompiler.h b/runtime/vm/compiler/aot/precompiler.h index 4e0d2117bb25..50df9e7d5945 100644 --- a/runtime/vm/compiler/aot/precompiler.h +++ b/runtime/vm/compiler/aot/precompiler.h @@ -210,6 +210,37 @@ class TypeArgumentsKeyValueTrait { typedef DirectChainedHashMap TypeArgumentsSet; +class ProgramElementKeyValueTrait { + public: + // Typedefs needed for the DirectChainedHashMap template. + typedef const Object* Key; + typedef const Object* Value; + typedef const Object* Pair; + + static Key KeyOf(Pair kv) { return kv; } + + static Value ValueOf(Pair kv) { return kv; } + + static inline uword Hash(Key key) { + if (key->IsFunction()) { + return Function::Cast(*key).Hash(); + } else if (key->IsField()) { + return Field::Cast(*key).kernel_offset(); + } else if (key->IsClass()) { + return Class::Cast(*key).kernel_offset(); + } else if (key->IsLibrary()) { + return Library::Cast(*key).index(); + } + FATAL("Unexpected type: %s\n", key->ToCString()); + } + + static inline bool IsKeyEqual(Pair pair, Key key) { + return pair->ptr() == key->ptr(); + } +}; + +typedef DirectChainedHashMap ProgramElementSet; + class InstanceKeyValueTrait { public: // Typedefs needed for the DirectChainedHashMap template. @@ -321,6 +352,8 @@ class Precompiler : public ValueObject { bool IsHitByTableSelector(const Function& function); // Returns the reason if the function must be retained, otherwise nullptr. const char* MustRetainFunction(const Function& function); + void AddApiUse(const Object& obj); + bool HasApiUse(const Object& obj); void ProcessFunction(const Function& function); void CheckForNewDynamicFunctions(); @@ -343,6 +376,7 @@ class Precompiler : public ValueObject { void DropClasses(); void DropLibraries(); void DiscardCodeObjects(); + void PruneDictionaries(); DEBUG_ONLY(FunctionPtr FindUnvisitedRetainedFunction()); @@ -393,6 +427,7 @@ class Precompiler : public ValueObject { TypeParameterSet typeparams_to_retain_; InstanceSet consts_to_retain_; TableSelectorSet seen_table_selectors_; + ProgramElementSet api_uses_; Error& error_; compiler::DispatchTableGenerator* dispatch_table_generator_; diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc index d52cc58fba73..7dba48aceeb9 100644 --- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc +++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc @@ -1891,9 +1891,11 @@ void LoadingUnitsMetadataHelper::ReadMetadata(intptr_t node_offset) { Zone* zone = thread->zone(); intptr_t unit_count = helper_->ReadUInt(); Array& loading_units = Array::Handle(zone, Array::New(unit_count + 1)); + Array& loading_unit_uris = Array::Handle(zone, Array::New(unit_count + 1)); LoadingUnit& unit = LoadingUnit::Handle(zone); LoadingUnit& parent = LoadingUnit::Handle(zone); Library& lib = Library::Handle(zone); + Array& uris = Array::Handle(zone); for (int i = 0; i < unit_count; i++) { intptr_t id = helper_->ReadUInt(); @@ -1907,6 +1909,7 @@ void LoadingUnitsMetadataHelper::ReadMetadata(intptr_t node_offset) { unit.set_parent(parent); intptr_t library_count = helper_->ReadUInt(); + uris = Array::New(library_count); for (intptr_t j = 0; j < library_count; j++) { const String& uri = translation_helper_.DartSymbolPlain(helper_->ReadStringReference()); @@ -1915,14 +1918,18 @@ void LoadingUnitsMetadataHelper::ReadMetadata(intptr_t node_offset) { FATAL1("Missing library: %s\n", uri.ToCString()); } lib.set_loading_unit(unit); + uris.SetAt(j, uri); } loading_units.SetAt(id, unit); + loading_unit_uris.SetAt(id, uris); } ObjectStore* object_store = IG->object_store(); ASSERT(object_store->loading_units() == Array::null()); object_store->set_loading_units(loading_units); + ASSERT(object_store->loading_unit_uris() == Array::null()); + object_store->set_loading_unit_uris(loading_unit_uris); } CallSiteAttributesMetadataHelper::CallSiteAttributesMetadataHelper( diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index 4f03fb7a8a75..f8b05d203a6f 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -6728,24 +6728,12 @@ DART_EXPORT Dart_Handle Dart_LoadingUnitLibraryUris(intptr_t loading_unit_id) { DARTSCOPE(Thread::Current()); API_TIMELINE_DURATION(T); - const GrowableObjectArray& result = - GrowableObjectArray::Handle(Z, GrowableObjectArray::New()); - const GrowableObjectArray& libs = GrowableObjectArray::Handle( - Z, T->isolate_group()->object_store()->libraries()); - Library& lib = Library::Handle(Z); - LoadingUnit& unit = LoadingUnit::Handle(Z); - String& uri = String::Handle(Z); - for (intptr_t i = 0; i < libs.Length(); i++) { - lib ^= libs.At(i); - unit = lib.loading_unit(); - if (unit.IsNull() || (unit.id() != loading_unit_id)) { - continue; - } - uri = lib.url(); - result.Add(uri); + const Array& loading_units = + Array::Handle(Z, T->isolate_group()->object_store()->loading_unit_uris()); + if (loading_unit_id >= 0 && loading_unit_id < loading_units.Length()) { + return Api::NewHandle(T, loading_units.At(loading_unit_id)); } - - return Api::NewHandle(T, Array::MakeFixedLength(result)); + return Api::NewError("Invalid loading_unit_id"); #endif } diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index deae3104f6cb..0b5f8bc69282 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -12452,8 +12452,12 @@ void Library::set_name(const String& name) const { untag()->set_name(name.ptr()); } -void Library::set_url(const String& name) const { - untag()->set_url(name.ptr()); +void Library::set_url(const String& url) const { + untag()->set_url(url.ptr()); +} + +void Library::set_private_key(const String& key) const { + untag()->set_private_key(key.ptr()); } void Library::set_kernel_data(const ExternalTypedData& data) const { diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 6f226c376e2a..dd1699a81700 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -5109,6 +5109,7 @@ class Library : public Object { // the name and url. void set_name(const String& name) const; void set_url(const String& url) const; + void set_private_key(const String& key) const; void set_num_imports(intptr_t value) const; void set_flags(uint8_t flags) const; diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h index 674d1b4180fe..718e61d8471a 100644 --- a/runtime/vm/object_store.h +++ b/runtime/vm/object_store.h @@ -261,6 +261,7 @@ class ObjectPointerVisitor; RW(Array, dispatch_table_code_entries) \ RW(GrowableObjectArray, instructions_tables) \ RW(Array, obfuscation_map) \ + RW(Array, loading_unit_uris) \ RW(GrowableObjectArray, ffi_callback_functions) \ RW(Class, ffi_pointer_class) \ RW(Class, ffi_native_type_class) \ diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart index d0bddf85fbfc..a70896e9f0f1 100644 --- a/sdk/lib/_internal/vm/bin/socket_patch.dart +++ b/sdk/lib/_internal/vm/bin/socket_patch.dart @@ -2543,6 +2543,7 @@ Datagram _makeDatagram( } @patch +@pragma("vm:entry-point") class ResourceHandle { factory ResourceHandle.fromFile(RandomAccessFile file) { int fd = (file as _RandomAccessFile).fd;