diff options
author | 2017-01-23 15:08:01 +0000 | |
---|---|---|
committer | 2017-01-23 15:08:02 +0000 | |
commit | b0dde4397fa5b0756312b46bd18477a2c1f6a7da (patch) | |
tree | 6a76cc50a8b2746aefaaefe5c8e1c6eee3d3d7a8 | |
parent | 17aba3529161ddd6f351608da383fdedb985ba44 (diff) | |
parent | 6ad2f6d0e17b8cd1fd20aeb1958196e856475e80 (diff) |
Merge "Fix inserting classes to initiating loader's class table."
-rw-r--r-- | build/Android.gtest.mk | 2 | ||||
-rw-r--r-- | compiler/image_writer.cc | 13 | ||||
-rw-r--r-- | runtime/class_linker.cc | 131 | ||||
-rw-r--r-- | runtime/class_linker_test.cc | 35 | ||||
-rw-r--r-- | runtime/class_table.cc | 13 | ||||
-rw-r--r-- | runtime/class_table.h | 1 |
6 files changed, 131 insertions, 64 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index d8b780ac35..c87075f51c 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -87,7 +87,7 @@ $(ART_TEST_TARGET_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $( ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces -ART_GTEST_class_linker_test_DEX_DEPS := ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode +ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode ART_GTEST_class_table_test_DEX_DEPS := XandY ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 4109345f80..459aca3b38 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -1166,9 +1166,9 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, // belongs. oat_index = GetOatIndexForDexCache(dex_cache); ImageInfo& image_info = GetImageInfo(oat_index); - { - // Note: This table is only accessed from the image writer, avoid locking to prevent lock - // order violations from root visiting. + if (!compile_app_image_) { + // Note: Avoid locking to prevent lock order violations from root visiting; + // image_info.class_table_ is only accessed from the image writer. image_info.class_table_->InsertWithoutLocks(as_klass); } for (LengthPrefixedArray<ArtField>* cur_fields : fields) { @@ -1265,7 +1265,14 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, // class loader. mirror::ClassLoader* class_loader = obj->AsClassLoader(); if (class_loader->GetClassTable() != nullptr) { + DCHECK(compile_app_image_); + DCHECK(class_loaders_.empty()); class_loaders_.insert(class_loader); + ImageInfo& image_info = GetImageInfo(oat_index); + // Note: Avoid locking to prevent lock order violations from root visiting; + // image_info.class_table_ table is only accessed from the image writer + // and class_loader->GetClassTable() is iterated but not modified. + image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable()); } } AssignImageBinSlot(obj, oat_index); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 14918df4d5..2e258beb48 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1398,7 +1398,11 @@ class UpdateClassLoaderVisitor { class_loader_(class_loader) {} bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) { - klass->SetClassLoader(class_loader_); + // Do not update class loader for boot image classes where the app image + // class loader is only the initiating loader but not the defining loader. + if (klass->GetClassLoader() != nullptr) { + klass->SetClassLoader(class_loader_); + } return true; } @@ -2458,10 +2462,8 @@ mirror::Class* ClassLinker::FindClass(Thread* self, return EnsureResolved(self, descriptor, klass); } // Class is not yet loaded. - if (descriptor[0] == '[') { - return CreateArrayClass(self, descriptor, hash, class_loader); - } else if (class_loader.Get() == nullptr) { - // The boot class loader, search the boot class path. + if (descriptor[0] != '[' && class_loader.Get() == nullptr) { + // Non-array class and the boot class loader, search the boot class path. ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); if (pair.second != nullptr) { return DefineClass(self, @@ -2474,14 +2476,21 @@ mirror::Class* ClassLinker::FindClass(Thread* self, // The boot class loader is searched ahead of the application class loader, failures are // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to // trigger the chaining with a proper stack trace. - ObjPtr<mirror::Throwable> pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + ObjPtr<mirror::Throwable> pre_allocated = + Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); self->SetException(pre_allocated); return nullptr; } + } + ObjPtr<mirror::Class> result_ptr; + bool descriptor_equals; + if (descriptor[0] == '[') { + result_ptr = CreateArrayClass(self, descriptor, hash, class_loader); + DCHECK_EQ(result_ptr == nullptr, self->IsExceptionPending()); + DCHECK(result_ptr == nullptr || result_ptr->DescriptorEquals(descriptor)); + descriptor_equals = true; } else { ScopedObjectAccessUnchecked soa(self); - ObjPtr<mirror::Class> result_ptr; - bool descriptor_equals; bool known_hierarchy = FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr); if (result_ptr != nullptr) { @@ -2525,16 +2534,7 @@ mirror::Class* ClassLinker::FindClass(Thread* self, WellKnownClasses::java_lang_ClassLoader_loadClass, class_name_object.get())); } - if (self->IsExceptionPending()) { - // If the ClassLoader threw, pass that exception up. - // However, to comply with the RI behavior, first check if another thread succeeded. - result_ptr = LookupClass(self, descriptor, hash, class_loader.Get()); - if (result_ptr != nullptr && !result_ptr->IsErroneous()) { - self->ClearException(); - return EnsureResolved(self, descriptor, result_ptr); - } - return nullptr; - } else if (result.get() == nullptr) { + if (result.get() == nullptr && !self->IsExceptionPending()) { // broken loader - throw NPE to be compatible with Dalvik ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s", class_name_string.c_str()).c_str()); @@ -2542,50 +2542,60 @@ mirror::Class* ClassLinker::FindClass(Thread* self, } result_ptr = soa.Decode<mirror::Class>(result.get()); // Check the name of the returned class. - descriptor_equals = result_ptr->DescriptorEquals(descriptor); + descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor); } + } - // Try to insert the class to the class table, checking for mismatch. - ObjPtr<mirror::Class> old; - { - WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get()); - old = class_table->Lookup(descriptor, hash); - if (old == nullptr) { - old = result_ptr; // For the comparison below, after releasing the lock. - if (descriptor_equals) { - class_table->InsertWithHash(result_ptr.Ptr(), hash); - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); - } // else throw below, after releasing the lock. - } - } - if (UNLIKELY(old != result_ptr)) { - // Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel - // capable class loaders. (All class loaders are considered parallel capable on Android.) - mirror::Class* loader_class = class_loader->GetClass(); - const char* loader_class_name = - loader_class->GetDexFile().StringByTypeIdx(loader_class->GetDexTypeIndex()); - LOG(WARNING) << "Initiating class loader of type " << DescriptorToDot(loader_class_name) - << " is not well-behaved; it returned a different Class for racing loadClass(\"" - << DescriptorToDot(descriptor) << "\")."; - return EnsureResolved(self, descriptor, old); - } - if (UNLIKELY(!descriptor_equals)) { - std::string result_storage; - const char* result_name = result_ptr->GetDescriptor(&result_storage); - std::string loader_storage; - const char* loader_class_name = class_loader->GetClass()->GetDescriptor(&loader_storage); - ThrowNoClassDefFoundError( - "Initiating class loader of type %s returned class %s instead of %s.", - DescriptorToDot(loader_class_name).c_str(), - DescriptorToDot(result_name).c_str(), - DescriptorToDot(descriptor).c_str()); - return nullptr; + if (self->IsExceptionPending()) { + // If the ClassLoader threw or array class allocation failed, pass that exception up. + // However, to comply with the RI behavior, first check if another thread succeeded. + result_ptr = LookupClass(self, descriptor, hash, class_loader.Get()); + if (result_ptr != nullptr && !result_ptr->IsErroneous()) { + self->ClearException(); + return EnsureResolved(self, descriptor, result_ptr); } - // success, return mirror::Class* - return result_ptr.Ptr(); + return nullptr; } - UNREACHABLE(); + + // Try to insert the class to the class table, checking for mismatch. + ObjPtr<mirror::Class> old; + { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get()); + old = class_table->Lookup(descriptor, hash); + if (old == nullptr) { + old = result_ptr; // For the comparison below, after releasing the lock. + if (descriptor_equals) { + class_table->InsertWithHash(result_ptr.Ptr(), hash); + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); + } // else throw below, after releasing the lock. + } + } + if (UNLIKELY(old != result_ptr)) { + // Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel + // capable class loaders. (All class loaders are considered parallel capable on Android.) + mirror::Class* loader_class = class_loader->GetClass(); + const char* loader_class_name = + loader_class->GetDexFile().StringByTypeIdx(loader_class->GetDexTypeIndex()); + LOG(WARNING) << "Initiating class loader of type " << DescriptorToDot(loader_class_name) + << " is not well-behaved; it returned a different Class for racing loadClass(\"" + << DescriptorToDot(descriptor) << "\")."; + return EnsureResolved(self, descriptor, old); + } + if (UNLIKELY(!descriptor_equals)) { + std::string result_storage; + const char* result_name = result_ptr->GetDescriptor(&result_storage); + std::string loader_storage; + const char* loader_class_name = class_loader->GetClass()->GetDescriptor(&loader_storage); + ThrowNoClassDefFoundError( + "Initiating class loader of type %s returned class %s instead of %s.", + DescriptorToDot(loader_class_name).c_str(), + DescriptorToDot(result_name).c_str(), + DescriptorToDot(descriptor).c_str()); + return nullptr; + } + // success, return mirror::Class* + return result_ptr.Ptr(); } mirror::Class* ClassLinker::DefineClass(Thread* self, @@ -3494,7 +3504,8 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto // class to the hash table --- necessary because of possible races with // other threads.) if (class_loader.Get() != component_type->GetClassLoader()) { - ObjPtr<mirror::Class> new_class = LookupClass(self, descriptor, hash, component_type->GetClassLoader()); + ObjPtr<mirror::Class> new_class = + LookupClass(self, descriptor, hash, component_type->GetClassLoader()); if (new_class != nullptr) { return new_class.Ptr(); } @@ -7712,7 +7723,7 @@ ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(const DexFile& dex_file, type = LookupClass(self, descriptor, hash, class_loader.Ptr()); } } - if (type != nullptr || type->IsResolved()) { + if (type != nullptr && type->IsResolved()) { return type.Ptr(); } return nullptr; diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index d98daa51fe..7d4b1589b6 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -906,6 +906,41 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { klass); } +TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("AllFields")))); + // Get the AllFields class for the dex cache and dex file. + ObjPtr<mirror::Class> all_fields_klass + = class_linker_->FindClass(soa.Self(), "LAllFields;", class_loader); + ASSERT_OBJ_PTR_NE(all_fields_klass, ObjPtr<mirror::Class>(nullptr)); + Handle<mirror::DexCache> dex_cache = hs.NewHandle(all_fields_klass->GetDexCache()); + const DexFile& dex_file = *dex_cache->GetDexFile(); + // Get the index of the array class we want to test. + const DexFile::TypeId* array_id = dex_file.FindTypeId("[Ljava/lang/Object;"); + ASSERT_TRUE(array_id != nullptr); + dex::TypeIndex array_idx = dex_file.GetIndexForTypeId(*array_id); + // Check that the array class wasn't resolved yet. + EXPECT_OBJ_PTR_EQ( + class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + ObjPtr<mirror::Class>(nullptr)); + // Resolve the array class we want to test. + ObjPtr<mirror::Class> array_klass + = class_linker_->FindClass(soa.Self(), "[Ljava/lang/Object;", class_loader); + ASSERT_OBJ_PTR_NE(array_klass, ObjPtr<mirror::Class>(nullptr)); + // Test that LookupResolvedType() finds the array class. + EXPECT_OBJ_PTR_EQ( + class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + array_klass); + // Zero out the resolved type and make sure LookupResolvedType() still finds it. + dex_cache->SetResolvedType(array_idx, nullptr); + EXPECT_TRUE(dex_cache->GetResolvedType(array_idx) == nullptr); + EXPECT_OBJ_PTR_EQ( + class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + array_klass); +} + TEST_F(ClassLinkerTest, LibCore) { ScopedObjectAccess soa(Thread::Current()); ASSERT_TRUE(java_lang_dex_file_ != nullptr); diff --git a/runtime/class_table.cc b/runtime/class_table.cc index 0f985c6424..ff846a718e 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -129,6 +129,19 @@ void ClassTable::Insert(ObjPtr<mirror::Class> klass) { classes_.back().InsertWithHash(TableSlot(klass, hash), hash); } +void ClassTable::CopyWithoutLocks(const ClassTable& source_table) { + if (kIsDebugBuild) { + for (ClassSet& class_set : classes_) { + CHECK(class_set.Empty()); + } + } + for (const ClassSet& class_set : source_table.classes_) { + for (const TableSlot& slot : class_set) { + classes_.back().Insert(slot); + } + } +} + void ClassTable::InsertWithoutLocks(ObjPtr<mirror::Class> klass) { const uint32_t hash = TableSlot::HashDescriptor(klass); classes_.back().InsertWithHash(TableSlot(klass, hash), hash); diff --git a/runtime/class_table.h b/runtime/class_table.h index f27d8093ce..c8ec28eca4 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -240,6 +240,7 @@ class ClassTable { } private: + void CopyWithoutLocks(const ClassTable& source_table) NO_THREAD_SAFETY_ANALYSIS; void InsertWithoutLocks(ObjPtr<mirror::Class> klass) NO_THREAD_SAFETY_ANALYSIS; size_t CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader, |