diff options
Diffstat (limited to 'runtime/class_linker.cc')
| -rw-r--r-- | runtime/class_linker.cc | 760 |
1 files changed, 404 insertions, 356 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 6a76bf7f07..5f5b42f7df 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -30,6 +30,7 @@ #include "base/arena_allocator.h" #include "base/casts.h" #include "base/logging.h" +#include "base/out.h" #include "base/scoped_arena_containers.h" #include "base/scoped_flock.h" #include "base/stl_util.h" @@ -198,7 +199,7 @@ struct FieldGapsComparator { return lhs.size < rhs.size || (lhs.size == rhs.size && lhs.start_offset > rhs.start_offset); } }; -typedef std::priority_queue<FieldGap, std::vector<FieldGap>, FieldGapsComparator> FieldGaps; +using FieldGaps = std::priority_queue<FieldGap, std::vector<FieldGap>, FieldGapsComparator>; // Adds largest aligned gaps to queue of gaps. static void AddFieldGap(uint32_t gap_start, uint32_t gap_end, FieldGaps* gaps) { @@ -775,7 +776,8 @@ class DexFileAndClassPair : ValueObject { // be from multidex, which resolves correctly). }; -static void AddDexFilesFromOat(const OatFile* oat_file, bool already_loaded, +static void AddDexFilesFromOat(const OatFile* oat_file, + bool already_loaded, std::priority_queue<DexFileAndClassPair>* heap) { const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles(); for (const OatDexFile* oat_dex_file : oat_dex_files) { @@ -836,7 +838,7 @@ const OatFile* ClassLinker::GetPrimaryOatFile() { // against the following top element. If the descriptor is the same, it is now checked whether // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. -bool ClassLinker::HasCollisions(const OatFile* oat_file, std::string* error_msg) { +bool ClassLinker::HasCollisions(const OatFile* oat_file, out<std::string> error_msg) { if (!kDuplicateClassesCheck) { return false; } @@ -901,10 +903,9 @@ bool ClassLinker::HasCollisions(const OatFile* oat_file, std::string* error_msg) } std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat( - const char* dex_location, const char* oat_location, - std::vector<std::string>* error_msgs) { - CHECK(error_msgs != nullptr); - + const char* dex_location, + const char* oat_location, + out<std::vector<std::string>> error_msgs) { // Verify we aren't holding the mutator lock, which could starve GC if we // have to generate or relocate an oat file. Locks::mutator_lock_->AssertNotHeld(Thread::Current()); @@ -946,7 +947,7 @@ std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat( std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); if (oat_file.get() != nullptr) { // Take the file only if it has no collisions, or we must take it because of preopting. - bool accept_oat_file = !HasCollisions(oat_file.get(), &error_msg); + bool accept_oat_file = !HasCollisions(oat_file.get(), outof(error_msg)); if (!accept_oat_file) { // Failed the collision check. Print warning. if (Runtime::Current()->IsDexFileFallbackEnabled()) { @@ -980,8 +981,7 @@ std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat( if (source_oat_file != nullptr) { dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location); if (dex_files.empty()) { - error_msgs->push_back("Failed to open dex files from " - + source_oat_file->GetLocation()); + error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation()); } } @@ -1017,7 +1017,8 @@ const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& return nullptr; } -static void SanityCheckArtMethod(ArtMethod* m, mirror::Class* expected_class, +static void SanityCheckArtMethod(ArtMethod* m, + mirror::Class* expected_class, gc::space::ImageSpace* space) SHARED_REQUIRES(Locks::mutator_lock_) { if (m->IsRuntimeMethod()) { @@ -1035,9 +1036,11 @@ static void SanityCheckArtMethod(ArtMethod* m, mirror::Class* expected_class, } } -static void SanityCheckArtMethodPointerArray( - mirror::PointerArray* arr, mirror::Class* expected_class, size_t pointer_size, - gc::space::ImageSpace* space) SHARED_REQUIRES(Locks::mutator_lock_) { +static void SanityCheckArtMethodPointerArray(mirror::PointerArray* arr, + mirror::Class* expected_class, + size_t pointer_size, + gc::space::ImageSpace* space) + SHARED_REQUIRES(Locks::mutator_lock_) { CHECK(arr != nullptr); for (int32_t j = 0; j < arr->GetLength(); ++j) { auto* method = arr->GetElementPtrSize<ArtMethod*>(j, pointer_size); @@ -1236,11 +1239,8 @@ void ClassLinker::InitFromImage() { bool ClassLinker::ClassInClassTable(mirror::Class* klass) { ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - auto it = class_table_.Find(GcRoot<mirror::Class>(klass)); - if (it == class_table_.end()) { - return false; - } - return it->Read() == klass; + ClassTable* const class_table = ClassTableForClassLoader(klass->GetClassLoader()); + return class_table != nullptr && class_table->Contains(klass); } void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { @@ -1263,26 +1263,30 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { // Moving concurrent: // Need to make sure to not copy ArtMethods without doing read barriers since the roots are // marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy. - for (GcRoot<mirror::Class>& root : class_table_) { - buffered_visitor.VisitRoot(root); + std::vector<std::pair<GcRoot<mirror::ClassLoader>, ClassTable*>> reinsert; + for (auto it = classes_.begin(); it != classes_.end(); ) { + it->second->VisitRoots(visitor, flags); + const GcRoot<mirror::ClassLoader>& root = it->first; + mirror::ClassLoader* old_ref = root.Read<kWithoutReadBarrier>(); + root.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + mirror::ClassLoader* new_ref = root.Read<kWithoutReadBarrier>(); + if (new_ref != old_ref) { + reinsert.push_back(*it); + it = classes_.erase(it); + } else { + ++it; + } } - // PreZygote classes can't move so we won't need to update fields' declaring classes. - for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) { - buffered_visitor.VisitRoot(root); + for (auto& pair : reinsert) { + classes_.Put(pair.first, pair.second); } } else if ((flags & kVisitRootFlagNewRoots) != 0) { for (auto& root : new_class_roots_) { mirror::Class* old_ref = root.Read<kWithoutReadBarrier>(); root.VisitRoot(visitor, RootInfo(kRootStickyClass)); mirror::Class* new_ref = root.Read<kWithoutReadBarrier>(); - if (UNLIKELY(new_ref != old_ref)) { - // Uh ohes, GC moved a root in the log. Need to search the class_table and update the - // corresponding object. This is slow, but luckily for us, this may only happen with a - // concurrent moving GC. - auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref)); - DCHECK(it != class_table_.end()); - *it = GcRoot<mirror::Class>(new_ref); - } + // Concurrent moving GC marked new roots through the to-space invariant. + CHECK_EQ(new_ref, old_ref); } } buffered_visitor.Flush(); // Flush before clearing new_class_roots_. @@ -1331,91 +1335,103 @@ void ClassLinker::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) { } } -void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) { - if (dex_cache_image_class_lookup_required_) { - MoveImageClassesToClassTable(); - } - // TODO: why isn't this a ReaderMutexLock? - WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - for (GcRoot<mirror::Class>& root : class_table_) { - if (!visitor(root.Read(), arg)) { - return; - } - } - for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) { - if (!visitor(root.Read(), arg)) { +void ClassLinker::VisitClassesInternal(ClassVisitor* visitor) { + for (auto& pair : classes_) { + ClassTable* const class_table = pair.second; + if (!class_table->Visit(visitor)) { return; } } } -static bool GetClassesVisitorSet(mirror::Class* c, void* arg) { - std::set<mirror::Class*>* classes = reinterpret_cast<std::set<mirror::Class*>*>(arg); - classes->insert(c); - return true; +void ClassLinker::VisitClasses(ClassVisitor* visitor) { + if (dex_cache_image_class_lookup_required_) { + MoveImageClassesToClassTable(); + } + Thread* const self = Thread::Current(); + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); + // Not safe to have thread suspension when we are holding a lock. + if (self != nullptr) { + ScopedAssertNoThreadSuspension nts(self, __FUNCTION__); + VisitClassesInternal(visitor); + } else { + VisitClassesInternal(visitor); + } } -struct GetClassesVisitorArrayArg { - Handle<mirror::ObjectArray<mirror::Class>>* classes; - int32_t index; - bool success; +class GetClassesInToVector : public ClassVisitor { + public: + bool Visit(mirror::Class* klass) OVERRIDE { + classes_.push_back(klass); + return true; + } + std::vector<mirror::Class*> classes_; }; -static bool GetClassesVisitorArray(mirror::Class* c, void* varg) - SHARED_REQUIRES(Locks::mutator_lock_) { - GetClassesVisitorArrayArg* arg = reinterpret_cast<GetClassesVisitorArrayArg*>(varg); - if (arg->index < (*arg->classes)->GetLength()) { - (*arg->classes)->Set(arg->index, c); - arg->index++; - return true; - } else { - arg->success = false; +class GetClassInToObjectArray : public ClassVisitor { + public: + explicit GetClassInToObjectArray(mirror::ObjectArray<mirror::Class>* arr) + : arr_(arr), index_(0) {} + + bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { + ++index_; + if (index_ <= arr_->GetLength()) { + arr_->Set(index_ - 1, klass); + return true; + } return false; } -} -void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) { + bool Succeeded() const SHARED_REQUIRES(Locks::mutator_lock_) { + return index_ <= arr_->GetLength(); + } + + private: + mirror::ObjectArray<mirror::Class>* const arr_; + int32_t index_; +}; + +void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) { // TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem // is avoiding duplicates. if (!kMovingClasses) { - std::set<mirror::Class*> classes; - VisitClasses(GetClassesVisitorSet, &classes); - for (mirror::Class* klass : classes) { - if (!visitor(klass, arg)) { + GetClassesInToVector accumulator; + VisitClasses(&accumulator); + for (mirror::Class* klass : accumulator.classes_) { + if (!visitor->Visit(klass)) { return; } } } else { - Thread* self = Thread::Current(); + Thread* const self = Thread::Current(); StackHandleScope<1> hs(self); - MutableHandle<mirror::ObjectArray<mirror::Class>> classes = - hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr); - GetClassesVisitorArrayArg local_arg; - local_arg.classes = &classes; - local_arg.success = false; + auto classes = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr); // We size the array assuming classes won't be added to the class table during the visit. // If this assumption fails we iterate again. - while (!local_arg.success) { + while (true) { size_t class_table_size; { ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); - class_table_size = class_table_.Size() + pre_zygote_class_table_.Size(); + // Add 100 in case new classes get loaded when we are filling in the object array. + class_table_size = NumZygoteClasses() + NumNonZygoteClasses() + 100; } mirror::Class* class_type = mirror::Class::GetJavaLangClass(); mirror::Class* array_of_class = FindArrayClass(self, &class_type); classes.Assign( mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size)); CHECK(classes.Get() != nullptr); // OOME. - local_arg.index = 0; - local_arg.success = true; - VisitClasses(GetClassesVisitorArray, &local_arg); + GetClassInToObjectArray accumulator(classes.Get()); + VisitClasses(&accumulator); + if (accumulator.Succeeded()) { + break; + } } for (int32_t i = 0; i < classes->GetLength(); ++i) { // If the class table shrank during creation of the clases array we expect null elements. If // the class table grew then the loop repeats. If classes are created after the loop has // finished then we don't visit. mirror::Class* klass = classes->Get(i); - if (klass != nullptr && !visitor(klass, arg)) { + if (klass != nullptr && !visitor->Visit(klass)) { return; } } @@ -1443,6 +1459,7 @@ ClassLinker::~ClassLinker() { mirror::LongArray::ResetArrayClass(); mirror::ShortArray::ResetArrayClass(); STLDeleteElements(&oat_files_); + STLDeleteValues(&classes_); } mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) { @@ -1489,7 +1506,8 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_fi return dex_cache.Get(); } -mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Class, +mirror::Class* ClassLinker::AllocClass(Thread* self, + mirror::Class* java_lang_Class, uint32_t class_size) { DCHECK_GE(class_size, sizeof(mirror::Class)); gc::Heap* heap = Runtime::Current()->GetHeap(); @@ -1508,13 +1526,14 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, uint32_t class_size) { return AllocClass(self, GetClassRoot(kJavaLangClass), class_size); } -mirror::ObjectArray<mirror::StackTraceElement>* ClassLinker::AllocStackTraceElementArray( - Thread* self, size_t length) { +mirror::ObjectArray<mirror::StackTraceElement>* +ClassLinker::AllocStackTraceElementArray(Thread* self, size_t length) { return mirror::ObjectArray<mirror::StackTraceElement>::Alloc( self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length); } -mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor, +mirror::Class* ClassLinker::EnsureResolved(Thread* self, + const char* descriptor, mirror::Class* klass) { DCHECK(klass != nullptr); @@ -1573,7 +1592,8 @@ typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry; // Search a collection of DexFiles for a descriptor ClassPathEntry FindInClassPath(const char* descriptor, - size_t hash, const std::vector<const DexFile*>& class_path) { + size_t hash, + const std::vector<const DexFile*>& class_path) { for (const DexFile* dex_file : class_path) { const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash); if (dex_class_def != nullptr) { @@ -1592,16 +1612,17 @@ static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, } bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, - Thread* self, const char* descriptor, + Thread* self, + const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader, - mirror::Class** result) { + out<mirror::Class*> result) { // Termination case: boot class-loader. if (IsBootClassLoader(soa, class_loader.Get())) { // The boot class loader, search the boot class path. ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); if (pair.second != nullptr) { - mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr); + mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr /* no classloader */); if (klass != nullptr) { *result = EnsureResolved(self, descriptor, klass); } else { @@ -1703,7 +1724,8 @@ bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& return true; } -mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, +mirror::Class* ClassLinker::FindClass(Thread* self, + const char* descriptor, Handle<mirror::ClassLoader> class_loader) { DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; DCHECK(self != nullptr); @@ -1739,7 +1761,7 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, } else { ScopedObjectAccessUnchecked soa(self); mirror::Class* cp_klass; - if (FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) { + if (FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader, outof(cp_klass))) { // The chain was understood. So the value in cp_klass is either the class we were looking // for, or not found. if (cp_klass != nullptr) { @@ -1792,7 +1814,9 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, UNREACHABLE(); } -mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash, +mirror::Class* ClassLinker::DefineClass(Thread* self, + const char* descriptor, + size_t hash, Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { @@ -1878,7 +1902,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, si auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr); MutableHandle<mirror::Class> h_new_class = hs.NewHandle<mirror::Class>(nullptr); - if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) { + if (!LinkClass(self, descriptor, klass, interfaces, outof(h_new_class))) { // Linking failed. if (!klass->IsErroneous()) { mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); @@ -1961,8 +1985,9 @@ uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, image_pointer_size_); } -OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, - bool* found) { +OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, + uint16_t class_def_idx, + out<bool> found) { DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16); const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr) { @@ -1973,7 +1998,8 @@ OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t cl return oat_dex_file->GetOatClass(class_def_idx); } -static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, +static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, + uint16_t class_def_idx, uint32_t method_idx) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); const uint8_t* class_data = dex_file.GetClassData(class_def); @@ -2007,7 +2033,7 @@ static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16 UNREACHABLE(); } -const OatFile::OatMethod ClassLinker::FindOatMethodFor(ArtMethod* method, bool* found) { +const OatFile::OatMethod ClassLinker::FindOatMethodFor(ArtMethod* method, out<bool> found) { // Although we overwrite the trampoline of non-static methods, we may get here via the resolution // method for direct methods (or virtual methods made direct). mirror::Class* declaring_class = method->GetDeclaringClass(); @@ -2039,7 +2065,7 @@ const OatFile::OatMethod ClassLinker::FindOatMethodFor(ArtMethod* method, bool* method->GetDexMethodIndex())); OatFile::OatClass oat_class = FindOatClass(*declaring_class->GetDexCache()->GetDexFile(), declaring_class->GetDexClassDefIndex(), - found); + outof_forward(found)); if (!(*found)) { return OatFile::OatMethod::Invalid(); } @@ -2053,7 +2079,7 @@ const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) { return GetQuickProxyInvokeHandler(); } bool found; - OatFile::OatMethod oat_method = FindOatMethodFor(method, &found); + OatFile::OatMethod oat_method = FindOatMethodFor(method, outof(found)); if (found) { auto* code = oat_method.GetQuickCode(); if (code != nullptr) { @@ -2079,7 +2105,7 @@ const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) { return nullptr; } bool found; - OatFile::OatMethod oat_method = FindOatMethodFor(method, &found); + OatFile::OatMethod oat_method = FindOatMethodFor(method, outof(found)); if (found) { return oat_method.GetQuickCode(); } @@ -2093,10 +2119,11 @@ const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) { return nullptr; } -const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, +const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, + uint16_t class_def_idx, uint32_t method_idx) { bool found; - OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found); + OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, outof(found)); if (!found) { return nullptr; } @@ -2147,7 +2174,7 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { } bool has_oat_class; OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), - &has_oat_class); + outof(has_oat_class)); // Link the code of methods skipped by LinkCode. for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) { ArtMethod* method = klass->GetDirectMethod(method_index, image_pointer_size_); @@ -2175,7 +2202,8 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { // Ignore virtual methods on the iterator. } -void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class, +void ClassLinker::LinkCode(ArtMethod* method, + const OatFile::OatClass* oat_class, uint32_t class_def_method_index) { Runtime* const runtime = Runtime::Current(); if (runtime->IsAotCompiler()) { @@ -2227,8 +2255,10 @@ void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class } } -void ClassLinker::SetupClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, - Handle<mirror::Class> klass, mirror::ClassLoader* class_loader) { +void ClassLinker::SetupClass(const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def, + Handle<mirror::Class> klass, + mirror::ClassLoader* class_loader) { CHECK(klass.Get() != nullptr); CHECK(klass->GetDexCache() != nullptr); CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus()); @@ -2248,7 +2278,8 @@ void ClassLinker::SetupClass(const DexFile& dex_file, const DexFile::ClassDef& d CHECK(klass->GetDexCacheStrings() != nullptr); } -void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file, +void ClassLinker::LoadClass(Thread* self, + const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, Handle<mirror::Class> klass) { const uint8_t* class_data = dex_file.GetClassData(dex_class_def); @@ -2258,7 +2289,7 @@ void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file, bool has_oat_class = false; if (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler()) { OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), - &has_oat_class); + outof(has_oat_class)); if (has_oat_class) { LoadClassMembers(self, dex_file, class_data, klass, &oat_class); } @@ -2287,7 +2318,8 @@ ArtMethod* ClassLinker::AllocArtMethodArray(Thread* self, size_t length) { return reinterpret_cast<ArtMethod*>(ptr); } -void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file, +void ClassLinker::LoadClassMembers(Thread* self, + const DexFile& dex_file, const uint8_t* class_data, Handle<mirror::Class> klass, const OatFile::OatClass* oat_class) { @@ -2377,10 +2409,13 @@ void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file, } DCHECK(!it.HasNext()); } + // Ensure that the card is marked so that remembered sets pick up native roots. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get()); self->AllowThreadSuspension(); } -void ClassLinker::LoadField(const ClassDataItemIterator& it, Handle<mirror::Class> klass, +void ClassLinker::LoadField(const ClassDataItemIterator& it, + Handle<mirror::Class> klass, ArtField* dst) { const uint32_t field_idx = it.GetMemberIndex(); dst->SetDexFieldIndex(field_idx); @@ -2388,8 +2423,11 @@ void ClassLinker::LoadField(const ClassDataItemIterator& it, Handle<mirror::Clas dst->SetAccessFlags(it.GetFieldAccessFlags()); } -void ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it, - Handle<mirror::Class> klass, ArtMethod* dst) { +void ClassLinker::LoadMethod(Thread* self, + const DexFile& dex_file, + const ClassDataItemIterator& it, + Handle<mirror::Class> klass, + ArtMethod* dst) { uint32_t dex_method_idx = it.GetMemberIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_); @@ -2458,8 +2496,8 @@ void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, bool ClassLinker::IsDexFileRegisteredLocked(const DexFile& dex_file) { dex_lock_.AssertSharedHeld(Thread::Current()); - for (size_t i = 0; i != dex_caches_.size(); ++i) { - mirror::DexCache* dex_cache = GetDexCache(i); + for (GcRoot<mirror::DexCache>& root : dex_caches_) { + mirror::DexCache* dex_cache = root.Read(); if (dex_cache->GetDexFile() == &dex_file) { return true; } @@ -2589,7 +2627,9 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(mirror::Class* primitive_cl // array class; that always comes from the base element class. // // Returns null with an exception raised on failure. -mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor, size_t hash, +mirror::Class* ClassLinker::CreateArrayClass(Thread* self, + const char* descriptor, + size_t hash, Handle<mirror::ClassLoader> class_loader) { // Identify the underlying component type CHECK_EQ('[', descriptor[0]); @@ -2757,8 +2797,7 @@ mirror::Class* ClassLinker::FindPrimitiveClass(char type) { return nullptr; } -mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* klass, - size_t hash) { +mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* klass, size_t hash) { if (VLOG_IS_ON(class_linker)) { mirror::DexCache* dex_cache = klass->GetDexCache(); std::string source; @@ -2769,11 +2808,13 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k LOG(INFO) << "Loaded class " << descriptor << source; } WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - mirror::Class* existing = LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash); + mirror::ClassLoader* const class_loader = klass->GetClassLoader(); + ClassTable* const class_table = InsertClassTableForClassLoader(class_loader); + mirror::Class* existing = class_table->Lookup(descriptor, hash); if (existing != nullptr) { return existing; } - if (kIsDebugBuild && !klass->IsTemp() && klass->GetClassLoader() == nullptr && + if (kIsDebugBuild && !klass->IsTemp() && class_loader == nullptr && dex_cache_image_class_lookup_required_) { // Check a class loaded with the system class loader matches one in the image if the class // is in the image. @@ -2783,114 +2824,60 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k } } VerifyObject(klass); - class_table_.InsertWithHash(GcRoot<mirror::Class>(klass), hash); + class_table->InsertWithHash(klass, hash); if (log_new_class_table_roots_) { new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); } return nullptr; } -void ClassLinker::UpdateClassVirtualMethods(mirror::Class* klass, ArtMethod* new_methods, +void ClassLinker::UpdateClassVirtualMethods(mirror::Class* klass, + ArtMethod* new_methods, size_t new_num_methods) { - // classlinker_classes_lock_ is used to guard against races between root marking and changing the - // direct and virtual method pointers. - WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + // TODO: Fix the race condition here. b/22832610 klass->SetNumVirtualMethods(new_num_methods); klass->SetVirtualMethodsPtr(new_methods); - if (log_new_class_table_roots_) { - new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); - } -} - -mirror::Class* ClassLinker::UpdateClass(const char* descriptor, mirror::Class* klass, - size_t hash) { - WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - auto existing_it = class_table_.FindWithHash(std::make_pair(descriptor, klass->GetClassLoader()), - hash); - CHECK(existing_it != class_table_.end()); - mirror::Class* existing = existing_it->Read(); - CHECK_NE(existing, klass) << descriptor; - CHECK(!existing->IsResolved()) << descriptor; - CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusResolving) << descriptor; - - CHECK(!klass->IsTemp()) << descriptor; - if (kIsDebugBuild && klass->GetClassLoader() == nullptr && - dex_cache_image_class_lookup_required_) { - // Check a class loaded with the system class loader matches one in the image if the class - // is in the image. - existing = LookupClassFromImage(descriptor); - if (existing != nullptr) { - CHECK_EQ(klass, existing) << descriptor; - } - } - VerifyObject(klass); - - // Update the element in the hash set. - *existing_it = GcRoot<mirror::Class>(klass); - if (log_new_class_table_roots_) { - new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); - } - - return existing; + // Need to mark the card so that the remembered sets and mod union tables get update. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass); } bool ClassLinker::RemoveClass(const char* descriptor, mirror::ClassLoader* class_loader) { WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - auto pair = std::make_pair(descriptor, class_loader); - auto it = class_table_.Find(pair); - if (it != class_table_.end()) { - class_table_.Erase(it); - return true; - } - it = pre_zygote_class_table_.Find(pair); - if (it != pre_zygote_class_table_.end()) { - pre_zygote_class_table_.Erase(it); - return true; - } - return false; + ClassTable* const class_table = ClassTableForClassLoader(class_loader); + return class_table != nullptr && class_table->Remove(descriptor); } -mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor, size_t hash, +mirror::Class* ClassLinker::LookupClass(Thread* self, + const char* descriptor, + size_t hash, mirror::ClassLoader* class_loader) { { ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); - mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash); - if (result != nullptr) { - return result; + ClassTable* const class_table = ClassTableForClassLoader(class_loader); + if (class_table != nullptr) { + mirror::Class* result = class_table->Lookup(descriptor, hash); + if (result != nullptr) { + return result; + } } } if (class_loader != nullptr || !dex_cache_image_class_lookup_required_) { return nullptr; - } else { - // Lookup failed but need to search dex_caches_. - mirror::Class* result = LookupClassFromImage(descriptor); - if (result != nullptr) { - InsertClass(descriptor, result, hash); - } else { - // Searching the image dex files/caches failed, we don't want to get into this situation - // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image - // classes into the class table. - constexpr uint32_t kMaxFailedDexCacheLookups = 1000; - if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) { - MoveImageClassesToClassTable(); - } - } - return result; } -} - -mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor, - mirror::ClassLoader* class_loader, - size_t hash) { - auto descriptor_pair = std::make_pair(descriptor, class_loader); - auto it = pre_zygote_class_table_.FindWithHash(descriptor_pair, hash); - if (it == pre_zygote_class_table_.end()) { - it = class_table_.FindWithHash(descriptor_pair, hash); - if (it == class_table_.end()) { - return nullptr; + // Lookup failed but need to search dex_caches_. + mirror::Class* result = LookupClassFromImage(descriptor); + if (result != nullptr) { + result = InsertClass(descriptor, result, hash); + } else { + // Searching the image dex files/caches failed, we don't want to get into this situation + // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image + // classes into the class table. + constexpr uint32_t kMaxFailedDexCacheLookups = 1000; + if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) { + MoveImageClassesToClassTable(); } } - return it->Read(); + return result; } static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches() @@ -2910,6 +2897,7 @@ void ClassLinker::MoveImageClassesToClassTable() { ScopedAssertNoThreadSuspension ants(self, "Moving image classes to class table"); mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(); std::string temp; + ClassTable* const class_table = InsertClassTableForClassLoader(nullptr); for (int32_t i = 0; i < dex_caches->GetLength(); i++) { mirror::DexCache* dex_cache = dex_caches->Get(i); mirror::ObjectArray<mirror::Class>* types = dex_cache->GetResolvedTypes(); @@ -2919,12 +2907,12 @@ void ClassLinker::MoveImageClassesToClassTable() { DCHECK(klass->GetClassLoader() == nullptr); const char* descriptor = klass->GetDescriptor(&temp); size_t hash = ComputeModifiedUtf8Hash(descriptor); - mirror::Class* existing = LookupClassFromTableLocked(descriptor, nullptr, hash); + mirror::Class* existing = class_table->Lookup(descriptor, hash); if (existing != nullptr) { CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != " << PrettyClassAndClassLoader(klass); } else { - class_table_.Insert(GcRoot<mirror::Class>(klass)); + class_table->Insert(klass); if (log_new_class_table_roots_) { new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); } @@ -2937,9 +2925,9 @@ void ClassLinker::MoveImageClassesToClassTable() { void ClassLinker::MoveClassTableToPreZygote() { WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - DCHECK(pre_zygote_class_table_.Empty()); - pre_zygote_class_table_ = std::move(class_table_); - class_table_.Clear(); + for (auto& class_table : classes_) { + class_table.second->FreezeSnapshot(); + } } mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) { @@ -2965,37 +2953,21 @@ mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) { return nullptr; } -void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Class*>& result) { +void ClassLinker::LookupClasses(const char* descriptor, + out<std::vector<mirror::Class*>> out_result) { + std::vector<mirror::Class*>& result = *out_result; result.clear(); if (dex_cache_image_class_lookup_required_) { MoveImageClassesToClassTable(); } WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - while (true) { - auto it = class_table_.Find(descriptor); - if (it == class_table_.end()) { - break; + for (auto& pair : classes_) { + // There can only be one class with the same descriptor per class loader. + ClassTable* const class_table = pair.second; + mirror::Class* klass = class_table->Lookup(descriptor, ComputeModifiedUtf8Hash(descriptor)); + if (klass != nullptr) { + result.push_back(klass); } - result.push_back(it->Read()); - class_table_.Erase(it); - } - for (mirror::Class* k : result) { - class_table_.Insert(GcRoot<mirror::Class>(k)); - } - size_t pre_zygote_start = result.size(); - // Now handle the pre zygote table. - // Note: This dirties the pre-zygote table but shouldn't be an issue since LookupClasses is only - // called from the debugger. - while (true) { - auto it = pre_zygote_class_table_.Find(descriptor); - if (it == pre_zygote_class_table_.end()) { - break; - } - result.push_back(it->Read()); - pre_zygote_class_table_.Erase(it); - } - for (size_t i = pre_zygote_start; i < result.size(); ++i) { - pre_zygote_class_table_.Insert(GcRoot<mirror::Class>(result[i])); } } @@ -3160,7 +3132,8 @@ void ClassLinker::EnsurePreverifiedMethods(Handle<mirror::Class> klass) { } } -bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class* klass, +bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, + mirror::Class* klass, mirror::Class::Status& oat_file_class_status) { // If we're compiling, we can only verify the class using the oat file if // we are not compiling the image or if the class we're verifying is not part of @@ -3282,9 +3255,12 @@ void ClassLinker::ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, } } -mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, jstring name, - jobjectArray interfaces, jobject loader, - jobjectArray methods, jobjectArray throws) { +mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, + jstring name, + jobjectArray interfaces, + jobject loader, + jobjectArray methods, + jobjectArray throws) { Thread* self = soa.Self(); StackHandleScope<10> hs(self); MutableHandle<mirror::Class> klass(hs.NewHandle( @@ -3303,7 +3279,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache()); mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self); std::string descriptor(GetDescriptorForProxy(klass.Get())); - size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str()); + const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str()); // Insert the class before loading the fields as the field roots // (ArtField::declaring_class_) are only visited from the class @@ -3379,7 +3355,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& // The new class will replace the old one in the class table. Handle<mirror::ObjectArray<mirror::Class>> h_interfaces( hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces))); - if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, &new_class)) { + if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, outof(new_class))) { mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); return nullptr; } @@ -3484,7 +3460,8 @@ void ClassLinker::CheckProxyConstructor(ArtMethod* constructor) const { DCHECK(constructor->IsPublic()); } -void ClassLinker::CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype, +void ClassLinker::CreateProxyMethod(Handle<mirror::Class> klass, + ArtMethod* prototype, ArtMethod* out) { // Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden // prototype method @@ -3530,7 +3507,8 @@ void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) cons CHECK_EQ(np->GetReturnType(), prototype->GetReturnType()); } -bool ClassLinker::CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, +bool ClassLinker::CanWeInitializeClass(mirror::Class* klass, + bool can_init_statics, bool can_init_parents) { if (can_init_statics && can_init_parents) { return true; @@ -3560,8 +3538,10 @@ bool ClassLinker::CanWeInitializeClass(mirror::Class* klass, bool can_init_stati return CanWeInitializeClass(super_class, can_init_statics, can_init_parents); } -bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, - bool can_init_statics, bool can_init_parents) { +bool ClassLinker::InitializeClass(Thread* self, + Handle<mirror::Class> klass, + bool can_init_statics, + bool can_init_parents) { // see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol // Are we already initialized and therefore done? @@ -3630,7 +3610,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, return true; } // No. That's fine. Wait for another thread to finish initializing. - return WaitForInitializeClass(klass, self, lock); + return WaitForInitializeClass(klass, self, &lock); } if (!ValidateSuperClassDescriptors(klass)) { @@ -3764,13 +3744,16 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, return success; } -bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, Thread* self, - ObjectLock<mirror::Class>& lock) +bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, + Thread* self, + ObjectLock<mirror::Class>* lock) SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(lock != nullptr); + while (true) { self->AssertNoPendingException(); CHECK(!klass->IsInitialized()); - lock.WaitIgnoringInterrupts(); + lock->WaitIgnoringInterrupts(); // When we wake up, repeat the test for init-in-progress. If // there's an exception pending (only possible if @@ -3833,7 +3816,8 @@ static void ThrowSignatureCheckResolveArgException(Handle<mirror::Class> klass, Handle<mirror::Class> super_klass, ArtMethod* method, ArtMethod* m, - uint32_t index, uint32_t arg_type_idx) + uint32_t index, + uint32_t arg_type_idx) SHARED_REQUIRES(Locks::mutator_lock_) { DCHECK(Thread::Current()->IsExceptionPending()); DCHECK(!m->IsProxyMethod()); @@ -3995,7 +3979,8 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { return true; } -bool ClassLinker::EnsureInitialized(Thread* self, Handle<mirror::Class> c, bool can_init_fields, +bool ClassLinker::EnsureInitialized(Thread* self, Handle<mirror::Class> c, + bool can_init_fields, bool can_init_parents) { DCHECK(c.Get() != nullptr); if (c->IsInitialized()) { @@ -4016,7 +4001,7 @@ bool ClassLinker::EnsureInitialized(Thread* self, Handle<mirror::Class> c, bool void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class) { ArtField* fields = new_class->GetIFields(); - DCHECK_EQ(temp_class->NumInstanceFields(), new_class->NumInstanceFields()); + DCHECK_EQ(temp_class->NumInstanceFields(), 0u); for (size_t i = 0, count = new_class->NumInstanceFields(); i < count; i++) { if (fields[i].GetDeclaringClass() == temp_class) { fields[i].SetDeclaringClass(new_class); @@ -4024,31 +4009,56 @@ void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, } fields = new_class->GetSFields(); - DCHECK_EQ(temp_class->NumStaticFields(), new_class->NumStaticFields()); + DCHECK_EQ(temp_class->NumStaticFields(), 0u); for (size_t i = 0, count = new_class->NumStaticFields(); i < count; i++) { if (fields[i].GetDeclaringClass() == temp_class) { fields[i].SetDeclaringClass(new_class); } } - DCHECK_EQ(temp_class->NumDirectMethods(), new_class->NumDirectMethods()); + DCHECK_EQ(temp_class->NumDirectMethods(), 0u); for (auto& method : new_class->GetDirectMethods(image_pointer_size_)) { if (method.GetDeclaringClass() == temp_class) { method.SetDeclaringClass(new_class); } } - DCHECK_EQ(temp_class->NumVirtualMethods(), new_class->NumVirtualMethods()); + DCHECK_EQ(temp_class->NumVirtualMethods(), 0u); for (auto& method : new_class->GetVirtualMethods(image_pointer_size_)) { if (method.GetDeclaringClass() == temp_class) { method.SetDeclaringClass(new_class); } } + + // Make sure the remembered set and mod-union tables know that we updated some of the native + // roots. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(new_class); +} + +ClassTable* ClassLinker::InsertClassTableForClassLoader(mirror::ClassLoader* class_loader) { + auto it = classes_.find(GcRoot<mirror::ClassLoader>(class_loader)); + if (it != classes_.end()) { + return it->second; + } + // Class table for loader not found, add it to the table. + auto* const class_table = new ClassTable; + classes_.Put(GcRoot<mirror::ClassLoader>(class_loader), class_table); + return class_table; +} + +ClassTable* ClassLinker::ClassTableForClassLoader(mirror::ClassLoader* class_loader) { + auto it = classes_.find(GcRoot<mirror::ClassLoader>(class_loader)); + if (it != classes_.end()) { + return it->second; + } + return nullptr; } -bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass, +bool ClassLinker::LinkClass(Thread* self, + const char* descriptor, + Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces, - MutableHandle<mirror::Class>* h_new_class_out) { + out<MutableHandle<mirror::Class>> h_new_class_out) { CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus()); if (!LinkSuperClass(klass)) { @@ -4056,14 +4066,14 @@ bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror: } ArtMethod* imt[mirror::Class::kImtSize]; std::fill_n(imt, arraysize(imt), Runtime::Current()->GetImtUnimplementedMethod()); - if (!LinkMethods(self, klass, interfaces, imt)) { + if (!LinkMethods(self, klass, interfaces, outof(imt))) { return false; } if (!LinkInstanceFields(self, klass)) { return false; } size_t class_size; - if (!LinkStaticFields(self, klass, &class_size)) { + if (!LinkStaticFields(self, klass, outof(class_size))) { return false; } CreateReferenceInstanceOffsets(klass); @@ -4087,6 +4097,14 @@ bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror: // Retire the temporary class and create the correctly sized resolved class. StackHandleScope<1> hs(self); auto h_new_class = hs.NewHandle(klass->CopyOf(self, class_size, imt, image_pointer_size_)); + // Set array lengths to 0 since we don't want the GC to visit two different classes with the + // same ArtFields with the same If this occurs, it causes bugs in remembered sets since the GC + // may not see any references to the from space and clean the card. Though there was references + // to the from space that got marked by the first class. + klass->SetNumDirectMethods(0); + klass->SetNumVirtualMethods(0); + klass->SetNumStaticFields(0); + klass->SetNumInstanceFields(0); if (UNLIKELY(h_new_class.Get() == nullptr)) { self->AssertPendingOOMException(); mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); @@ -4096,9 +4114,26 @@ bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror: CHECK_EQ(h_new_class->GetClassSize(), class_size); ObjectLock<mirror::Class> lock(self, h_new_class); FixupTemporaryDeclaringClass(klass.Get(), h_new_class.Get()); - mirror::Class* existing = UpdateClass(descriptor, h_new_class.Get(), - ComputeModifiedUtf8Hash(descriptor)); - CHECK(existing == nullptr || existing == klass.Get()); + + { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + mirror::ClassLoader* const class_loader = h_new_class.Get()->GetClassLoader(); + ClassTable* const table = InsertClassTableForClassLoader(class_loader); + mirror::Class* existing = table->UpdateClass(descriptor, h_new_class.Get(), + ComputeModifiedUtf8Hash(descriptor)); + CHECK_EQ(existing, klass.Get()); + if (kIsDebugBuild && class_loader == nullptr && dex_cache_image_class_lookup_required_) { + // Check a class loaded with the system class loader matches one in the image if the class + // is in the image. + mirror::Class* const image_class = LookupClassFromImage(descriptor); + if (image_class != nullptr) { + CHECK_EQ(klass.Get(), existing) << descriptor; + } + } + if (log_new_class_table_roots_) { + new_class_roots_.push_back(GcRoot<mirror::Class>(h_new_class.Get())); + } + } // This will notify waiters on temp class that saw the not yet resolved class in the // class_table_ during EnsureResolved. @@ -4114,30 +4149,31 @@ bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror: return true; } -static void CountMethodsAndFields(ClassDataItemIterator& dex_data, - size_t* virtual_methods, - size_t* direct_methods, - size_t* static_fields, - size_t* instance_fields) { +static void CountMethodsAndFields(ClassDataItemIterator* dex_data, + out<size_t> virtual_methods, + out<size_t> direct_methods, + out<size_t> static_fields, + out<size_t> instance_fields) { + DCHECK(dex_data != nullptr); *virtual_methods = *direct_methods = *static_fields = *instance_fields = 0; - while (dex_data.HasNextStaticField()) { - dex_data.Next(); + while (dex_data->HasNextStaticField()) { + dex_data->Next(); (*static_fields)++; } - while (dex_data.HasNextInstanceField()) { - dex_data.Next(); + while (dex_data->HasNextInstanceField()) { + dex_data->Next(); (*instance_fields)++; } - while (dex_data.HasNextDirectMethod()) { + while (dex_data->HasNextDirectMethod()) { (*direct_methods)++; - dex_data.Next(); + dex_data->Next(); } - while (dex_data.HasNextVirtualMethod()) { + while (dex_data->HasNextVirtualMethod()) { (*virtual_methods)++; - dex_data.Next(); + dex_data->Next(); } - DCHECK(!dex_data.HasNext()); + DCHECK(!dex_data->HasNext()); } static void DumpClass(std::ostream& os, @@ -4171,8 +4207,10 @@ static void DumpClass(std::ostream& os, } } -static std::string DumpClasses(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1, - const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2) { +static std::string DumpClasses(const DexFile& dex_file1, + const DexFile::ClassDef& dex_class_def1, + const DexFile& dex_file2, + const DexFile::ClassDef& dex_class_def2) { std::ostringstream os; DumpClass(os, dex_file1, dex_class_def1, " (Compile time)"); DumpClass(os, dex_file2, dex_class_def2, " (Runtime)"); @@ -4182,20 +4220,28 @@ static std::string DumpClasses(const DexFile& dex_file1, const DexFile::ClassDef // Very simple structural check on whether the classes match. Only compares the number of // methods and fields. -static bool SimpleStructuralCheck(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1, - const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2, +static bool SimpleStructuralCheck(const DexFile& dex_file1, + const DexFile::ClassDef& dex_class_def1, + const DexFile& dex_file2, + const DexFile::ClassDef& dex_class_def2, std::string* error_msg) { ClassDataItemIterator dex_data1(dex_file1, dex_file1.GetClassData(dex_class_def1)); ClassDataItemIterator dex_data2(dex_file2, dex_file2.GetClassData(dex_class_def2)); // Counters for current dex file. size_t dex_virtual_methods1, dex_direct_methods1, dex_static_fields1, dex_instance_fields1; - CountMethodsAndFields(dex_data1, &dex_virtual_methods1, &dex_direct_methods1, &dex_static_fields1, - &dex_instance_fields1); + CountMethodsAndFields(&dex_data1, + outof(dex_virtual_methods1), + outof(dex_direct_methods1), + outof(dex_static_fields1), + outof(dex_instance_fields1)); // Counters for compile-time dex file. size_t dex_virtual_methods2, dex_direct_methods2, dex_static_fields2, dex_instance_fields2; - CountMethodsAndFields(dex_data2, &dex_virtual_methods2, &dex_direct_methods2, &dex_static_fields2, - &dex_instance_fields2); + CountMethodsAndFields(&dex_data2, + outof(dex_virtual_methods2), + outof(dex_direct_methods2), + outof(dex_static_fields2), + outof(dex_instance_fields2)); if (dex_virtual_methods1 != dex_virtual_methods2) { std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2); @@ -4398,9 +4444,10 @@ bool ClassLinker::LinkSuperClass(Handle<mirror::Class> klass) { } // Populate the class vtable and itable. Compute return type indices. -bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass, +bool ClassLinker::LinkMethods(Thread* self, + Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces, - ArtMethod** out_imt) { + out<ArtMethod* [mirror::Class::kImtSize]> out_imt) { self->AllowThreadSuspension(); if (klass->IsInterface()) { // No vtable. @@ -4415,7 +4462,10 @@ bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass, } else if (!LinkVirtualMethods(self, klass)) { // Link virtual methods first. return false; } - return LinkInterfaceMethods(self, klass, interfaces, out_imt); // Link interface method last. + return LinkInterfaceMethods(self, + klass, + interfaces, + outof_forward(out_imt)); // Link interface method last. } // Comparator for name and signature of a method, used in finding overriding methods. Implementation @@ -4468,7 +4518,9 @@ class MethodNameAndSignatureComparator FINAL : public ValueObject { class LinkVirtualHashTable { public: - LinkVirtualHashTable(Handle<mirror::Class> klass, size_t hash_size, uint32_t* hash_table, + LinkVirtualHashTable(Handle<mirror::Class> klass, + size_t hash_size, + uint32_t* hash_table, size_t image_pointer_size) : klass_(klass), hash_size_(hash_size), hash_table_(hash_table), image_pointer_size_(image_pointer_size) { @@ -4672,9 +4724,12 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) return true; } -bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass, +bool ClassLinker::LinkInterfaceMethods(Thread* self, + Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces, - ArtMethod** out_imt) { + out<ArtMethod* [mirror::Class::kImtSize]> out_imt_array) { + auto& out_imt = *out_imt_array; + StackHandleScope<3> hs(self); Runtime* const runtime = Runtime::Current(); const bool has_superclass = klass->HasSuperClass(); @@ -4862,7 +4917,7 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass } } - auto* old_cause = self->StartAssertNoThreadSuspension( + const char* old_cause = self->StartAssertNoThreadSuspension( "Copying ArtMethods for LinkInterfaceMethods"); for (size_t i = 0; i < ifcount; ++i) { size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods(); @@ -5083,12 +5138,16 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass bool ClassLinker::LinkInstanceFields(Thread* self, Handle<mirror::Class> klass) { CHECK(klass.Get() != nullptr); - return LinkFields(self, klass, false, nullptr); + size_t class_size_dont_care; + UNUSED(class_size_dont_care); // This doesn't get set for instance fields. + return LinkFields(self, klass, false, outof(class_size_dont_care)); } -bool ClassLinker::LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size) { +bool ClassLinker::LinkStaticFields(Thread* self, + Handle<mirror::Class> klass, + out<size_t> class_size) { CHECK(klass.Get() != nullptr); - return LinkFields(self, klass, true, class_size); + return LinkFields(self, klass, true, outof_forward(class_size)); } struct LinkFieldsComparator { @@ -5125,8 +5184,10 @@ struct LinkFieldsComparator { } }; -bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_static, - size_t* class_size) { +bool ClassLinker::LinkFields(Thread* self, + Handle<mirror::Class> klass, + bool is_static, + out<size_t> class_size) { self->AllowThreadSuspension(); const size_t num_fields = is_static ? klass->NumStaticFields() : klass->NumInstanceFields(); ArtField* const fields = is_static ? klass->GetSFields() : klass->GetIFields(); @@ -5297,7 +5358,8 @@ void ClassLinker::CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) { klass->SetReferenceInstanceOffsets(reference_offsets); } -mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t string_idx, +mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, + uint32_t string_idx, Handle<mirror::DexCache> dex_cache) { DCHECK(dex_cache.Get() != nullptr); mirror::String* resolved = dex_cache->GetResolvedString(string_idx); @@ -5311,7 +5373,8 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t str return string; } -mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx, +mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, + uint16_t type_idx, mirror::Class* referrer) { StackHandleScope<2> hs(Thread::Current()); Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); @@ -5319,7 +5382,8 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_i return ResolveType(dex_file, type_idx, dex_cache, class_loader); } -mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx, +mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, + uint16_t type_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { DCHECK(dex_cache.Get() != nullptr); @@ -5352,7 +5416,8 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_i return resolved; } -ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx, +ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, + uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer, InvokeType type) { @@ -5509,9 +5574,11 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_i } } -ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t field_idx, +ArtField* ClassLinker::ResolveField(const DexFile& dex_file, + uint32_t field_idx, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, bool is_static) { + Handle<mirror::ClassLoader> class_loader, + bool is_static) { DCHECK(dex_cache.Get() != nullptr); ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_); if (resolved != nullptr) { @@ -5550,7 +5617,8 @@ ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t field_idx, return resolved; } -ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx, +ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, + uint32_t field_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { DCHECK(dex_cache.Get() != nullptr); @@ -5580,7 +5648,8 @@ ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, uint32_t field_i return resolved; } -const char* ClassLinker::MethodShorty(uint32_t method_idx, ArtMethod* referrer, +const char* ClassLinker::MethodShorty(uint32_t method_idx, + ArtMethod* referrer, uint32_t* length) { mirror::Class* declaring_class = referrer->GetDeclaringClass(); mirror::DexCache* dex_cache = declaring_class->GetDexCache(); @@ -5589,23 +5658,22 @@ const char* ClassLinker::MethodShorty(uint32_t method_idx, ArtMethod* referrer, return dex_file.GetMethodShorty(method_id, length); } -void ClassLinker::DumpAllClasses(int flags) { - if (dex_cache_image_class_lookup_required_) { - MoveImageClassesToClassTable(); - } - // TODO: at the time this was written, it wasn't safe to call PrettyField with the ClassLinker - // lock held, because it might need to resolve a field's type, which would try to take the lock. - std::vector<mirror::Class*> all_classes; - { - ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - for (GcRoot<mirror::Class>& it : class_table_) { - all_classes.push_back(it.Read()); - } - } +class DumpClassVisitor : public ClassVisitor { + public: + explicit DumpClassVisitor(int flags) : flags_(flags) {} - for (size_t i = 0; i < all_classes.size(); ++i) { - all_classes[i]->DumpClass(std::cerr, flags); + bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { + klass->DumpClass(LOG(ERROR), flags_); + return true; } + + private: + const int flags_; +}; + +void ClassLinker::DumpAllClasses(int flags) { + DumpClassVisitor visitor(flags); + VisitClasses(&visitor); } static OatFile::OatMethod CreateOatMethod(const void* code) { @@ -5658,8 +5726,24 @@ void ClassLinker::DumpForSigQuit(std::ostream& os) { MoveImageClassesToClassTable(); } ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); - os << "Zygote loaded classes=" << pre_zygote_class_table_.Size() << " post zygote classes=" - << class_table_.Size() << "\n"; + os << "Zygote loaded classes=" << NumZygoteClasses() << " post zygote classes=" + << NumNonZygoteClasses() << "\n"; +} + +size_t ClassLinker::NumZygoteClasses() const { + size_t sum = 0; + for (auto& pair : classes_) { + sum += pair.second->NumZygoteClasses(); + } + return sum; +} + +size_t ClassLinker::NumNonZygoteClasses() const { + size_t sum = 0; + for (auto& pair : classes_) { + sum += pair.second->NumNonZygoteClasses(); + } + return sum; } size_t ClassLinker::NumLoadedClasses() { @@ -5668,7 +5752,7 @@ size_t ClassLinker::NumLoadedClasses() { } ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); // Only return non zygote classes since these are the ones which apps which care about. - return class_table_.Size(); + return NumNonZygoteClasses(); } pid_t ClassLinker::GetClassesLockOwner() { @@ -5739,43 +5823,6 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { return descriptor; } -std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& root) - const { - std::string temp; - return ComputeModifiedUtf8Hash(root.Read()->GetDescriptor(&temp)); -} - -bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a, - const GcRoot<mirror::Class>& b) const { - if (a.Read()->GetClassLoader() != b.Read()->GetClassLoader()) { - return false; - } - std::string temp; - return a.Read()->DescriptorEquals(b.Read()->GetDescriptor(&temp)); -} - -std::size_t ClassLinker::ClassDescriptorHashEquals::operator()( - const std::pair<const char*, mirror::ClassLoader*>& element) const { - return ComputeModifiedUtf8Hash(element.first); -} - -bool ClassLinker::ClassDescriptorHashEquals::operator()( - const GcRoot<mirror::Class>& a, const std::pair<const char*, mirror::ClassLoader*>& b) const { - if (a.Read()->GetClassLoader() != b.second) { - return false; - } - return a.Read()->DescriptorEquals(b.first); -} - -bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a, - const char* descriptor) const { - return a.Read()->DescriptorEquals(descriptor); -} - -std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const char* descriptor) const { - return ComputeModifiedUtf8Hash(descriptor); -} - bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) { if (Runtime::Current()->UseJit()) { // JIT can have direct code pointers from any method to any other method. @@ -5809,7 +5856,8 @@ bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) { } } -jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) { +jobject ClassLinker::CreatePathClassLoader(Thread* self, + const std::vector<const DexFile*>& dex_files) { // SOAAlreadyRunnable is protected, and we need something to add a global reference. // We could move the jobject to the callers, but all call-sites do this... ScopedObjectAccessUnchecked soa(self); |