diff options
26 files changed, 522 insertions, 139 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 1d4eaf8c5a..7af850a263 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -878,7 +878,7 @@ static void ResolveConstStrings(CompilerDriver* driver, MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr)); for (const DexFile* dex_file : dex_files) { - dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file, false)); + dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); TimingLogger::ScopedTiming t("Resolve const-string Strings", timings); size_t class_def_count = dex_file->NumClassDefs(); @@ -1182,10 +1182,12 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file, nullptr))); Handle<mirror::Class> klass(hs2.NewHandle( - class_linker->ResolveType(*dex_file, - exception_type_idx, - dex_cache, - ScopedNullHandle<mirror::ClassLoader>()))); + (dex_cache.Get() != nullptr) + ? class_linker->ResolveType(*dex_file, + exception_type_idx, + dex_cache, + ScopedNullHandle<mirror::ClassLoader>()) + : nullptr)); if (klass.Get() == nullptr) { const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx); const char* descriptor = dex_file->GetTypeDescriptor(type_id); @@ -1776,7 +1778,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache( - soa.Self(), dex_file, false))); + soa.Self(), dex_file))); // Resolve the class. mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, class_loader); @@ -1875,10 +1877,9 @@ class ResolveTypeVisitor : public CompilationVisitor { Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile( dex_file, class_loader.Get()))); - mirror::Class* klass = class_linker->ResolveType(dex_file, - dex::TypeIndex(type_idx), - dex_cache, - class_loader); + ObjPtr<mirror::Class> klass = (dex_cache.Get() != nullptr) + ? class_linker->ResolveType(dex_file, dex::TypeIndex(type_idx), dex_cache, class_loader) + : nullptr; if (klass == nullptr) { soa.Self()->AssertPendingException(); @@ -2135,7 +2136,7 @@ class VerifyClassVisitor : public CompilationVisitor { * will be rejected by the verifier and later skipped during compilation in the compiler. */ Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache( - soa.Self(), dex_file, false))); + soa.Self(), dex_file))); std::string error_msg; failure_kind = verifier::MethodVerifier::VerifyClass(soa.Self(), diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index bd2c5e3bfc..73c40ab6a3 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -1250,7 +1250,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { const ScopedObjectAccess soa_; const ScopedAssertNoThreadSuspension no_thread_suspension_; ClassLinker* const class_linker_; - mirror::DexCache* dex_cache_; + ObjPtr<mirror::DexCache> dex_cache_; std::vector<uint8_t> patched_code_; void ReportWriteFailure(const char* what, const ClassDataItemIterator& it) { @@ -1261,7 +1261,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { ArtMethod* GetTargetMethod(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { MethodReference ref = patch.TargetMethod(); - mirror::DexCache* dex_cache = + ObjPtr<mirror::DexCache> dex_cache = (dex_file_ == ref.dex_file) ? dex_cache_ : class_linker_->FindDexCache( Thread::Current(), *ref.dex_file); ArtMethod* method = dex_cache->GetResolvedMethod( @@ -1295,7 +1295,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { return target_offset; } - mirror::DexCache* GetDexCache(const DexFile* target_dex_file) + ObjPtr<mirror::DexCache> GetDexCache(const DexFile* target_dex_file) REQUIRES_SHARED(Locks::mutator_lock_) { return (target_dex_file == dex_file_) ? dex_cache_ @@ -1303,7 +1303,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { } mirror::Class* GetTargetType(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::DexCache* dex_cache = GetDexCache(patch.TargetTypeDexFile()); + ObjPtr<mirror::DexCache> dex_cache = GetDexCache(patch.TargetTypeDexFile()); mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex()); CHECK(type != nullptr); return type; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index b02f2509ab..c55fccc7d3 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -24,9 +24,8 @@ namespace art { -static inline mirror::DexCache* FindDexCacheWithHint(Thread* self, - const DexFile& dex_file, - Handle<mirror::DexCache> hint_dex_cache) +static inline ObjPtr<mirror::DexCache> FindDexCacheWithHint( + Thread* self, const DexFile& dex_file, Handle<mirror::DexCache> hint_dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { if (LIKELY(hint_dex_cache->GetDexFile() == &dex_file)) { return hint_dex_cache.Get(); @@ -542,7 +541,7 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* DCHECK_EQ(instr->GetType(), Primitive::kPrimNot); ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); + ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); // Get type from dex cache assuming it was populated by the verifier. SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact); } @@ -562,7 +561,7 @@ static mirror::Class* GetClassFromDexCache(Thread* self, dex::TypeIndex type_idx, Handle<mirror::DexCache> hint_dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::DexCache* dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache); + ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache); // Get type from dex cache assuming it was populated by the verifier. return dex_cache->GetResolvedType(type_idx); } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 196d8d4220..192fc270f9 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1644,6 +1644,12 @@ class Dex2Oat FINAL { dex_caches_.push_back(soa.AddLocalReference<jobject>( class_linker->RegisterDexFile(*dex_file, soa.Decode<mirror::ClassLoader>(class_loader_).Ptr()))); + if (dex_caches_.back() == nullptr) { + soa.Self()->AssertPendingException(); + soa.Self()->ClearException(); + PLOG(ERROR) << "Failed to register dex file."; + return false; + } // Pre-register dex files so that we can access verification results without locks during // compilation and verification. verification_results_->AddDexFile(dex_file); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index ae175eb113..ee3f9b42db 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1432,6 +1432,7 @@ class OatDumper { Runtime* const runtime = Runtime::Current(); Handle<mirror::DexCache> dex_cache( hs->NewHandle(runtime->GetClassLinker()->RegisterDexFile(*dex_file, nullptr))); + CHECK(dex_cache.Get() != nullptr); DCHECK(options_.class_loader_ != nullptr); return verifier::MethodVerifier::VerifyMethodAndDump( soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_, @@ -2685,7 +2686,9 @@ static jobject InstallOatFile(Runtime* runtime, std::string error_msg; const DexFile* const dex_file = OpenDexFile(odf, &error_msg); CHECK(dex_file != nullptr) << error_msg; - class_linker->RegisterDexFile(*dex_file, nullptr); + ObjPtr<mirror::DexCache> dex_cache = + class_linker->RegisterDexFile(*dex_file, nullptr); + CHECK(dex_cache != nullptr); class_path->push_back(dex_file); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index edd6e3b522..27ad0d9e9b 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1322,16 +1322,8 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( // Make sure to do this after we update the arrays since we store the resolved types array // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the // BSS. - ObjPtr<mirror::DexCache> existing_dex_cache = FindDexCacheLocked(self, - *dex_file, - /*allow_failure*/true); - CHECK(existing_dex_cache == nullptr); - StackHandleScope<1> hs3(self); - Handle<mirror::DexCache> h_dex_cache = hs3.NewHandle(dex_cache); - RegisterDexFileLocked(*dex_file, h_dex_cache); - if (kIsDebugBuild) { - dex_cache.Assign(h_dex_cache.Get()); // Update dex_cache, used below in debug build. - } + CHECK(!FindDexCacheDataLocked(*dex_file).IsValid()); + RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get()); } if (kIsDebugBuild) { CHECK(new_class_set != nullptr); @@ -1675,11 +1667,9 @@ bool ClassLinker::AddImageSpace( return false; } - StackHandleScope<1> hs2(self); - MutableHandle<mirror::DexCache> h_dex_cache(hs2.NewHandle<mirror::DexCache>(nullptr)); for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - h_dex_cache.Assign(dex_caches->Get(i)); - std::string dex_file_location(h_dex_cache->GetLocation()->ToModifiedUtf8()); + ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i); + std::string dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); // TODO: Only store qualified paths. // If non qualified, qualify it. if (dex_file_location.find('/') == std::string::npos) { @@ -1699,9 +1689,9 @@ bool ClassLinker::AddImageSpace( if (app_image) { // The current dex file field is bogus, overwrite it so that we can get the dex file in the // loop below. - h_dex_cache->SetDexFile(dex_file.get()); - GcRoot<mirror::Class>* const types = h_dex_cache->GetResolvedTypes(); - for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) { + dex_cache->SetDexFile(dex_file.get()); + GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes(); + for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) { ObjPtr<mirror::Class> klass = types[j].Read(); if (klass != nullptr) { DCHECK(!klass->IsErroneous()) << klass->GetStatus(); @@ -1711,11 +1701,11 @@ bool ClassLinker::AddImageSpace( if (kSanityCheckObjects) { ImageSanityChecks::CheckPointerArray(heap, this, - h_dex_cache->GetResolvedMethods(), - h_dex_cache->NumResolvedMethods()); + dex_cache->GetResolvedMethods(), + dex_cache->NumResolvedMethods()); } // Register dex files, keep track of existing ones that are conflicts. - AppendToBootClassPath(*dex_file.get(), h_dex_cache); + AppendToBootClassPath(*dex_file.get(), dex_cache); } out_dex_files->push_back(std::move(dex_file)); } @@ -2656,7 +2646,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, } ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get()); if (dex_cache == nullptr) { - self->AssertPendingOOMException(); + self->AssertPendingException(); return nullptr; } klass->SetDexCache(dex_cache); @@ -3264,28 +3254,27 @@ void ClassLinker::LoadMethod(const DexFile& dex_file, } void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile& dex_file) { - StackHandleScope<1> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocAndInitializeDexCache( + ObjPtr<mirror::DexCache> dex_cache = AllocAndInitializeDexCache( self, dex_file, - Runtime::Current()->GetLinearAlloc()))); - CHECK(dex_cache.Get() != nullptr) << "Failed to allocate dex cache for " - << dex_file.GetLocation(); + Runtime::Current()->GetLinearAlloc()); + CHECK(dex_cache != nullptr) << "Failed to allocate dex cache for " << dex_file.GetLocation(); AppendToBootClassPath(dex_file, dex_cache); } void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, - Handle<mirror::DexCache> dex_cache) { - CHECK(dex_cache.Get() != nullptr) << dex_file.GetLocation(); + ObjPtr<mirror::DexCache> dex_cache) { + CHECK(dex_cache != nullptr) << dex_file.GetLocation(); boot_class_path_.push_back(&dex_file); - RegisterDexFile(dex_file, dex_cache); + RegisterBootClassPathDexFile(dex_file, dex_cache); } void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, - Handle<mirror::DexCache> dex_cache) { + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) { Thread* const self = Thread::Current(); Locks::dex_lock_->AssertExclusiveHeld(self); - CHECK(dex_cache.Get() != nullptr) << dex_file.GetLocation(); + CHECK(dex_cache != nullptr) << dex_file.GetLocation(); // For app images, the dex cache location may be a suffix of the dex file location since the // dex file location is an absolute path. const std::string dex_cache_location = dex_cache->GetLocation()->ToModifiedUtf8(); @@ -3313,25 +3302,49 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, ++it; } } - jweak dex_cache_jweak = vm->AddWeakGlobalRef(self, dex_cache.Get()); + jweak dex_cache_jweak = vm->AddWeakGlobalRef(self, dex_cache); dex_cache->SetDexFile(&dex_file); DexCacheData data; data.weak_root = dex_cache_jweak; data.dex_file = dex_cache->GetDexFile(); data.resolved_methods = dex_cache->GetResolvedMethods(); + data.class_table = ClassTableForClassLoader(class_loader); + DCHECK(data.class_table != nullptr); dex_caches_.push_back(data); } -mirror::DexCache* ClassLinker::RegisterDexFile(const DexFile& dex_file, - ObjPtr<mirror::ClassLoader> class_loader) { +ObjPtr<mirror::DexCache> ClassLinker::DecodeDexCache(Thread* self, const DexCacheData& data) { + return data.IsValid() + ? ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)) + : nullptr; +} + +ObjPtr<mirror::DexCache> ClassLinker::EnsureSameClassLoader( + Thread* self, + ObjPtr<mirror::DexCache> dex_cache, + const DexCacheData& data, + ObjPtr<mirror::ClassLoader> class_loader) { + DCHECK_EQ(dex_cache->GetDexFile(), data.dex_file); + if (data.class_table != ClassTableForClassLoader(class_loader)) { + self->ThrowNewExceptionF("Ljava/lang/InternalError;", + "Attempt to register dex file %s with multiple class loaders", + data.dex_file->GetLocation().c_str()); + return nullptr; + } + return dex_cache; +} + +ObjPtr<mirror::DexCache> ClassLinker::RegisterDexFile(const DexFile& dex_file, + ObjPtr<mirror::ClassLoader> class_loader) { Thread* self = Thread::Current(); + DexCacheData old_data; { ReaderMutexLock mu(self, *Locks::dex_lock_); - ObjPtr<mirror::DexCache> dex_cache = FindDexCacheLocked(self, dex_file, true); - if (dex_cache != nullptr) { - // TODO: Check if the dex file was registered with the same class loader. Bug: 34193123 - return dex_cache.Ptr(); - } + old_data = FindDexCacheDataLocked(dex_file); + } + ObjPtr<mirror::DexCache> old_dex_cache = DecodeDexCache(self, old_data); + if (old_dex_cache != nullptr) { + return EnsureSameClassLoader(self, old_dex_cache, old_data, class_loader); } LinearAlloc* const linear_alloc = GetOrCreateAllocatorForClassLoader(class_loader); DCHECK(linear_alloc != nullptr); @@ -3343,7 +3356,8 @@ mirror::DexCache* ClassLinker::RegisterDexFile(const DexFile& dex_file, // Don't alloc while holding the lock, since allocation may need to // suspend all threads and another thread may need the dex_lock_ to // get to a suspend point. - StackHandleScope<2> hs(self); + StackHandleScope<3> hs(self); + Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader)); ObjPtr<mirror::String> location; Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(AllocDexCache(/*out*/&location, self, @@ -3351,75 +3365,92 @@ mirror::DexCache* ClassLinker::RegisterDexFile(const DexFile& dex_file, Handle<mirror::String> h_location(hs.NewHandle(location)); { WriterMutexLock mu(self, *Locks::dex_lock_); - ObjPtr<mirror::DexCache> dex_cache = FindDexCacheLocked(self, dex_file, true); - if (dex_cache != nullptr) { - // Another thread managed to initialize the dex cache faster, so use that DexCache. - // If this thread encountered OOME, ignore it. - DCHECK_EQ(h_dex_cache.Get() == nullptr, self->IsExceptionPending()); - self->ClearException(); - return dex_cache.Ptr(); - } - if (h_dex_cache.Get() == nullptr) { - self->AssertPendingOOMException(); - return nullptr; - } - // Do InitializeDexCache while holding dex lock to make sure two threads don't call it at the - // same time with the same dex cache. Since the .bss is shared this can cause failing DCHECK - // that the arrays are null. - mirror::DexCache::InitializeDexCache(self, - h_dex_cache.Get(), - h_location.Get(), - &dex_file, - linear_alloc, - image_pointer_size_); - RegisterDexFileLocked(dex_file, h_dex_cache); + old_data = FindDexCacheDataLocked(dex_file); + old_dex_cache = DecodeDexCache(self, old_data); + if (old_dex_cache == nullptr && h_dex_cache.Get() != nullptr) { + // Do InitializeDexCache while holding dex lock to make sure two threads don't call it at the + // same time with the same dex cache. Since the .bss is shared this can cause failing DCHECK + // that the arrays are null. + mirror::DexCache::InitializeDexCache(self, + h_dex_cache.Get(), + h_location.Get(), + &dex_file, + linear_alloc, + image_pointer_size_); + RegisterDexFileLocked(dex_file, h_dex_cache.Get(), h_class_loader.Get()); + } + } + if (old_dex_cache != nullptr) { + // Another thread managed to initialize the dex cache faster, so use that DexCache. + // If this thread encountered OOME, ignore it. + DCHECK_EQ(h_dex_cache.Get() == nullptr, self->IsExceptionPending()); + self->ClearException(); + // We cannot call EnsureSameClassLoader() while holding the dex_lock_. + return EnsureSameClassLoader(self, old_dex_cache, old_data, h_class_loader.Get()); + } + if (h_dex_cache.Get() == nullptr) { + self->AssertPendingOOMException(); + return nullptr; } table->InsertStrongRoot(h_dex_cache.Get()); return h_dex_cache.Get(); } -void ClassLinker::RegisterDexFile(const DexFile& dex_file, - Handle<mirror::DexCache> dex_cache) { +void ClassLinker::RegisterBootClassPathDexFile(const DexFile& dex_file, + ObjPtr<mirror::DexCache> dex_cache) { WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_); - RegisterDexFileLocked(dex_file, dex_cache); + RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr); +} + +bool ClassLinker::IsDexFileRegistered(Thread* self, const DexFile& dex_file) { + ReaderMutexLock mu(self, *Locks::dex_lock_); + return DecodeDexCache(self, FindDexCacheDataLocked(dex_file)) != nullptr; } -mirror::DexCache* ClassLinker::FindDexCache(Thread* self, - const DexFile& dex_file, - bool allow_failure) { +ObjPtr<mirror::DexCache> ClassLinker::FindDexCache(Thread* self, const DexFile& dex_file) { ReaderMutexLock mu(self, *Locks::dex_lock_); - return FindDexCacheLocked(self, dex_file, allow_failure); + ObjPtr<mirror::DexCache> dex_cache = DecodeDexCache(self, FindDexCacheDataLocked(dex_file)); + if (dex_cache != nullptr) { + return dex_cache; + } + // Failure, dump diagnostic and abort. + std::string location(dex_file.GetLocation()); + for (const DexCacheData& data : dex_caches_) { + if (DecodeDexCache(self, data) != nullptr) { + LOG(ERROR) << "Registered dex file " << data.dex_file->GetLocation(); + } + } + LOG(FATAL) << "Failed to find DexCache for DexFile " << location; + UNREACHABLE(); } -mirror::DexCache* ClassLinker::FindDexCacheLocked(Thread* self, - const DexFile& dex_file, - bool allow_failure) { +ClassTable* ClassLinker::FindClassTable(Thread* self, ObjPtr<mirror::DexCache> dex_cache) { + const DexFile* dex_file = dex_cache->GetDexFile(); + DCHECK(dex_file != nullptr); + ReaderMutexLock mu(self, *Locks::dex_lock_); // Search assuming unique-ness of dex file. for (const DexCacheData& data : dex_caches_) { // Avoid decoding (and read barriers) other unrelated dex caches. - if (data.dex_file == &dex_file) { - ObjPtr<mirror::DexCache> dex_cache = - ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); - if (dex_cache != nullptr) { - return dex_cache.Ptr(); + if (data.dex_file == dex_file) { + ObjPtr<mirror::DexCache> registered_dex_cache = DecodeDexCache(self, data); + if (registered_dex_cache != nullptr) { + CHECK_EQ(registered_dex_cache, dex_cache) << dex_file->GetLocation(); + return data.class_table; } - break; } } - if (allow_failure) { - return nullptr; - } - std::string location(dex_file.GetLocation()); - // Failure, dump diagnostic and abort. + return nullptr; +} + +ClassLinker::DexCacheData ClassLinker::FindDexCacheDataLocked(const DexFile& dex_file) { + // Search assuming unique-ness of dex file. for (const DexCacheData& data : dex_caches_) { - ObjPtr<mirror::DexCache> dex_cache = - ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); - if (dex_cache != nullptr) { - LOG(ERROR) << "Registered dex file " << dex_cache->GetDexFile()->GetLocation(); + // Avoid decoding (and read barriers) other unrelated dex caches. + if (data.dex_file == &dex_file) { + return data; } } - LOG(FATAL) << "Failed to find DexCache for DexFile " << location; - UNREACHABLE(); + return DexCacheData(); } void ClassLinker::FixupDexCaches(ArtMethod* resolution_method) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 5042fb7609..62d3c29a19 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -382,11 +382,11 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - mirror::DexCache* RegisterDexFile(const DexFile& dex_file, - ObjPtr<mirror::ClassLoader> class_loader) + ObjPtr<mirror::DexCache> RegisterDexFile(const DexFile& dex_file, + ObjPtr<mirror::ClassLoader> class_loader) REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void RegisterDexFile(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) + void RegisterBootClassPathDexFile(const DexFile& dex_file, ObjPtr<mirror::DexCache> dex_cache) REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -413,9 +413,13 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_, !Locks::classlinker_classes_lock_, !Locks::trace_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::DexCache* FindDexCache(Thread* self, - const DexFile& dex_file, - bool allow_failure = false) + bool IsDexFileRegistered(Thread* self, const DexFile& dex_file) + REQUIRES(!Locks::dex_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::DexCache> FindDexCache(Thread* self, const DexFile& dex_file) + REQUIRES(!Locks::dex_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + ClassTable* FindClassTable(Thread* self, ObjPtr<mirror::DexCache> dex_cache) REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); void FixupDexCaches(ArtMethod* resolution_method) @@ -655,6 +659,18 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_); struct DexCacheData { + // Construct an invalid data object. + DexCacheData() + : weak_root(nullptr), + dex_file(nullptr), + resolved_methods(nullptr), + class_table(nullptr) { } + + // Check if the data is valid. + bool IsValid() const { + return dex_file != nullptr; + } + // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may // not work properly. jweak weak_root; @@ -663,6 +679,11 @@ class ClassLinker { // class unloading.) const DexFile* dex_file; ArtMethod** resolved_methods; + // Identify the associated class loader's class table. This is used to make sure that + // the Java call to native DexCache.setResolvedType() inserts the resolved type in that + // class table. It is also used to make sure we don't register the same dex cache with + // multiple class loaders. + ClassTable* class_table; }; private: @@ -749,7 +770,7 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) + void AppendToBootClassPath(const DexFile& dex_file, ObjPtr<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); @@ -810,12 +831,24 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) + void RegisterDexFileLocked(const DexFile& dex_file, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) REQUIRES(Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::DexCache* FindDexCacheLocked(Thread* self, const DexFile& dex_file, bool allow_failure) + DexCacheData FindDexCacheDataLocked(const DexFile& dex_file) REQUIRES(Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + static ObjPtr<mirror::DexCache> DecodeDexCache(Thread* self, const DexCacheData& data) + REQUIRES_SHARED(Locks::mutator_lock_); + // Called to ensure that the dex cache has been registered with the same class loader. + // If yes, returns the dex cache, otherwise throws InternalError and returns null. + ObjPtr<mirror::DexCache> EnsureSameClassLoader(Thread* self, + ObjPtr<mirror::DexCache> dex_cache, + const DexCacheData& data, + ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES(!Locks::dex_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); bool InitializeClass(Thread* self, Handle<mirror::Class> klass, diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 17510bb598..03105cb6fb 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -439,7 +439,7 @@ class ClassLinkerTest : public CommonRuntimeTest { TestRootVisitor visitor; class_linker_->VisitRoots(&visitor, kVisitRootFlagAllRoots); // Verify the dex cache has resolution methods in all resolved method slots - mirror::DexCache* dex_cache = class_linker_->FindDexCache(Thread::Current(), dex); + ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), dex); auto* resolved_methods = dex_cache->GetResolvedMethods(); for (size_t i = 0, num_methods = dex_cache->NumResolvedMethods(); i != num_methods; ++i) { EXPECT_TRUE( @@ -1454,7 +1454,7 @@ TEST_F(ClassLinkerTest, RegisterDexFileName) { { WriterMutexLock mu(soa.Self(), *Locks::dex_lock_); // Check that inserting with a UTF16 name works. - class_linker->RegisterDexFileLocked(*dex_file, dex_cache); + class_linker->RegisterDexFileLocked(*dex_file, dex_cache.Get(), /* class_loader */ nullptr); } } diff --git a/runtime/class_table.cc b/runtime/class_table.cc index ff846a718e..1f9dc8c6ee 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -123,6 +123,19 @@ mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) { return nullptr; } +ObjPtr<mirror::Class> ClassTable::TryInsert(ObjPtr<mirror::Class> klass) { + TableSlot slot(klass); + WriterMutexLock mu(Thread::Current(), lock_); + for (ClassSet& class_set : classes_) { + auto it = class_set.Find(slot); + if (it != class_set.end()) { + return it->Read(); + } + } + classes_.back().Insert(slot); + return klass; +} + void ClassTable::Insert(ObjPtr<mirror::Class> klass) { const uint32_t hash = TableSlot::HashDescriptor(klass); WriterMutexLock mu(Thread::Current(), lock_); diff --git a/runtime/class_table.h b/runtime/class_table.h index c8ec28eca4..711eae45b8 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -192,6 +192,12 @@ class ClassTable { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // Try to insert a class and return the inserted class if successful. If another class + // with the same descriptor is already in the table, return the existing entry. + ObjPtr<mirror::Class> TryInsert(ObjPtr<mirror::Class> klass) + REQUIRES(!lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + void Insert(ObjPtr<mirror::Class> klass) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index cd0e55f261..1234933db9 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -188,7 +188,7 @@ static jobject DexFile_openDexFileNative(JNIEnv* env, if (array == nullptr) { ScopedObjectAccess soa(env); for (auto& dex_file : dex_files) { - if (linker->FindDexCache(soa.Self(), *dex_file, true) != nullptr) { + if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) { dex_file.release(); } } @@ -230,7 +230,7 @@ static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) { if (dex_file != nullptr) { // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there // are calls to DexFile.close while the ART DexFile is still in use. - if (class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr) { + if (!class_linker->IsDexFileRegistered(soa.Self(), *dex_file)) { // Clear the element in the array so that we can call close again. long_dex_files->Set(i, 0); delete dex_file; @@ -281,7 +281,13 @@ static jclass DexFile_defineClassNative(JNIEnv* env, StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader))); - class_linker->RegisterDexFile(*dex_file, class_loader.Get()); + ObjPtr<mirror::DexCache> dex_cache = + class_linker->RegisterDexFile(*dex_file, class_loader.Get()); + if (dex_cache == nullptr) { + // OOME or InternalError (dexFile already registered with a different class loader). + soa.Self()->AssertPendingException(); + return nullptr; + } ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash, diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc index 981be68199..07959607fc 100644 --- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc +++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc @@ -128,7 +128,7 @@ static void InMemoryDexClassLoader_DexData_uninitialize(JNIEnv* env, jclass, jlo if (kIsDebugBuild) { ScopedObjectAccess soa(env); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - CHECK(class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr); + CHECK(!class_linker->IsDexFileRegistered(soa.Self(), *dex_file)); } delete dex_file; } @@ -153,7 +153,13 @@ static jclass InMemoryDexClassLoader_DexData_findClass( StackHandleScope<1> handle_scope(soa.Self()); Handle<mirror::ClassLoader> class_loader( handle_scope.NewHandle(soa.Decode<mirror::ClassLoader>(loader))); - class_linker->RegisterDexFile(*dex_file, class_loader.Get()); + ObjPtr<mirror::DexCache> dex_cache = + class_linker->RegisterDexFile(*dex_file, class_loader.Get()); + if (dex_cache == nullptr) { + // OOME or InternalError (dexFile already registered with a different class loader). + soa.Self()->AssertPendingException(); + return nullptr; + } ObjPtr<mirror::Class> result = class_linker->DefineClass( soa.Self(), class_descriptor, diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 1af861929e..24308d9e81 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -448,11 +448,8 @@ static void PreloadDexCachesStatsFilled(DexCacheStats* filled) Thread* const self = Thread::Current(); for (const DexFile* dex_file : class_linker->GetBootClassPath()) { CHECK(dex_file != nullptr); - ObjPtr<mirror::DexCache> const dex_cache = class_linker->FindDexCache(self, *dex_file, true); - // If dex cache was deallocated, just continue. - if (dex_cache == nullptr) { - continue; - } + ObjPtr<mirror::DexCache> const dex_cache = class_linker->FindDexCache(self, *dex_file); + CHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded. for (size_t j = 0; j < dex_cache->NumStrings(); j++) { ObjPtr<mirror::String> string = dex_cache->GetResolvedString(dex::StringIndex(j)); if (string != nullptr) { @@ -515,7 +512,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { CHECK(dex_file != nullptr); StackHandleScope<1> hs(soa.Self()); Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->RegisterDexFile(*dex_file, nullptr))); - + CHECK(dex_cache.Get() != nullptr); // Boot class path dex caches are never unloaded. if (kPreloadDexCachesStrings) { for (size_t j = 0; j < dex_cache->NumStrings(); j++) { PreloadDexCachesResolveString(dex_cache, dex::StringIndex(j), strings); diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc index f1c350f23c..b1ed74a6de 100644 --- a/runtime/native/java_lang_DexCache.cc +++ b/runtime/native/java_lang_DexCache.cc @@ -65,12 +65,22 @@ static jobject DexCache_getResolvedString(JNIEnv* env, jobject javaDexCache, jin dex_cache->GetResolvedString(dex::StringIndex(string_index))); } -static void DexCache_setResolvedType(JNIEnv* env, jobject javaDexCache, jint type_index, +static void DexCache_setResolvedType(JNIEnv* env, + jobject javaDexCache, + jint type_index, jobject type) { ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache); - CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes()); - dex_cache->SetResolvedType(dex::TypeIndex(type_index), soa.Decode<mirror::Class>(type)); + const DexFile& dex_file = *dex_cache->GetDexFile(); + CHECK_LT(static_cast<size_t>(type_index), dex_file.NumTypeIds()); + ObjPtr<mirror::Class> t = soa.Decode<mirror::Class>(type); + if (t != nullptr && t->DescriptorEquals(dex_file.StringByTypeIdx(dex::TypeIndex(type_index)))) { + ClassTable* table = + Runtime::Current()->GetClassLinker()->FindClassTable(soa.Self(), dex_cache); + if (table != nullptr && table->TryInsert(t) == t) { + dex_cache->SetResolvedType(dex::TypeIndex(type_index), t); + } + } } static void DexCache_setResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index, @@ -78,7 +88,10 @@ static void DexCache_setResolvedString(JNIEnv* env, jobject javaDexCache, jint s ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache); CHECK_LT(static_cast<size_t>(string_index), dex_cache->GetDexFile()->NumStringIds()); - dex_cache->SetResolvedString(dex::StringIndex(string_index), soa.Decode<mirror::String>(string)); + ObjPtr<mirror::String> s = soa.Decode<mirror::String>(string); + if (s != nullptr) { + dex_cache->SetResolvedString(dex::StringIndex(string_index), s); + } } static JNINativeMethod gMethods[] = { diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index 4b8108accf..dace2164c9 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -378,7 +378,7 @@ art::mirror::ClassLoader* Redefiner::ClassRedefinition::GetClassLoader() { art::mirror::DexCache* Redefiner::ClassRedefinition::CreateNewDexCache( art::Handle<art::mirror::ClassLoader> loader) { - return driver_->runtime_->GetClassLinker()->RegisterDexFile(*dex_file_, loader.Get()); + return driver_->runtime_->GetClassLinker()->RegisterDexFile(*dex_file_, loader.Get()).Ptr(); } void Redefiner::RecordFailure(jvmtiError result, @@ -732,7 +732,7 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations( } holder->SetNewDexCache(klass_index, CreateNewDexCache(loader)); if (holder->GetNewDexCache(klass_index) == nullptr) { - driver_->self_->AssertPendingOOMException(); + driver_->self_->AssertPendingException(); driver_->self_->ClearException(); RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate DexCache"); return false; diff --git a/test/155-java-set-resolved-type/expected.txt b/test/155-java-set-resolved-type/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/155-java-set-resolved-type/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/155-java-set-resolved-type/info.txt b/test/155-java-set-resolved-type/info.txt new file mode 100644 index 0000000000..ba5bc0ad61 --- /dev/null +++ b/test/155-java-set-resolved-type/info.txt @@ -0,0 +1,2 @@ +Regression test for Java call to DexCache.setResolvedType() storing the +type in the dex cache while it was not in the class loader's class table. diff --git a/test/155-java-set-resolved-type/src-ex/TestInterface.java b/test/155-java-set-resolved-type/src-ex/TestInterface.java new file mode 100644 index 0000000000..037c760765 --- /dev/null +++ b/test/155-java-set-resolved-type/src-ex/TestInterface.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface TestInterface { + public void foo(); +} diff --git a/test/155-java-set-resolved-type/src/Main.java b/test/155-java-set-resolved-type/src/Main.java new file mode 100644 index 0000000000..f92363e915 --- /dev/null +++ b/test/155-java-set-resolved-type/src/Main.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class Main { + public static String TEST_NAME = "155-java-set-resolved-type"; + + public static void main(String[] args) { + try { + Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader"); + System.loadLibrary(args[0]); + } catch (ClassNotFoundException e) { + usingRI = true; + // Add expected JNI_OnLoad log line to match expected.txt. + System.out.println("JNI_OnLoad called"); + } + try { + String dex_location = System.getenv("DEX_LOCATION"); + ClassLoader systemLoader = ClassLoader.getSystemClassLoader().getParent(); + ClassLoader exLoader = getClassLoaderFor(dex_location, systemLoader, /* ex */ true); + ClassLoader mainLoader = getClassLoaderFor(dex_location, exLoader, /* ex */ false); + + // Resolve TestParameter class. It shall be defined by mainLoader. + // This does not resolve method parameter types. + Class<?> tpc = Class.forName("TestParameter", false, mainLoader); + // Get declared methods of TestParameter. + // This still does not resolve method parameter types. + Method[] ms = tpc.getDeclaredMethods(); + if (ms == null || ms.length != 1) { throw new Error("Unexpected methods"); }; + // Call getParameterTypes() to resolve parameter types. The parameter type + // TestInterface shall be defined by the exLoader. This used to store the + // TestInterface class in the dex cache resolved types for the mainLoader + // but not in the mainLoader's class table. This discrepancy used to cause + // a crash further down. + ms[0].getParameterTypes(); + + // Resolve but do not initialize TestImplementation. During the resolution, + // we see the TestInterface in the dex cache, so we do not try to look it up + // or resolve it using the mainLoader. + Class<?> timpl = Class.forName("TestImplementation", false, mainLoader); + // Clear the dex cache resolved types to force a proper lookup the next time + // we need to find TestInterface. + // TODO: Enable clearing the dex cache when we switch to the hash-based type array + // and do a proper lookup. Currently, ClassLinker fully relies on the DexCache. + if (false) { + clearResolvedTypes(timpl); + } + + // Force intialization of TestClass2. This expects the interface type to be + // resolved and found through simple lookup. + timpl.newInstance(); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public static ClassLoader getClassLoaderFor(String location, ClassLoader parent, boolean ex) + throws Exception { + try { + Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader"); + Constructor<?> ctor = + class_loader_class.getConstructor(String.class, ClassLoader.class); + /* on Dalvik, this is a DexFile; otherwise, it's null */ + String path = location + "/" + TEST_NAME + (ex ? "-ex.jar" : ".jar"); + return (ClassLoader)ctor.newInstance(path, parent); + } catch (ClassNotFoundException e) { + // Running on RI. Use URLClassLoader. + String url = "file://" + location + (ex ? "/classes-ex/" : "/classes/"); + return new java.net.URLClassLoader( + new java.net.URL[] { new java.net.URL(url) }, parent); + } + } + + public static void clearResolvedTypes(Class<?> c) { + if (!usingRI) { + nativeClearResolvedTypes(c); + } + } + + private static boolean usingRI = false; + + public static native void nativeClearResolvedTypes(Class<?> c); +} diff --git a/test/155-java-set-resolved-type/src/TestImplementation.java b/test/155-java-set-resolved-type/src/TestImplementation.java new file mode 100644 index 0000000000..4a3e74d157 --- /dev/null +++ b/test/155-java-set-resolved-type/src/TestImplementation.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class TestImplementation implements TestInterface { + public void foo() { } +} diff --git a/test/155-java-set-resolved-type/src/TestInterface.java b/test/155-java-set-resolved-type/src/TestInterface.java new file mode 100644 index 0000000000..037c760765 --- /dev/null +++ b/test/155-java-set-resolved-type/src/TestInterface.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface TestInterface { + public void foo(); +} diff --git a/test/155-java-set-resolved-type/src/TestParameter.java b/test/155-java-set-resolved-type/src/TestParameter.java new file mode 100644 index 0000000000..c881f3f8cf --- /dev/null +++ b/test/155-java-set-resolved-type/src/TestParameter.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class TestParameter { + public void bar(TestInterface ti) { } +} diff --git a/test/156-register-dex-file-multi-loader/expected.txt b/test/156-register-dex-file-multi-loader/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/156-register-dex-file-multi-loader/expected.txt diff --git a/test/156-register-dex-file-multi-loader/info.txt b/test/156-register-dex-file-multi-loader/info.txt new file mode 100644 index 0000000000..49d153ca98 --- /dev/null +++ b/test/156-register-dex-file-multi-loader/info.txt @@ -0,0 +1,2 @@ +Regression test to check that we do not allow registering the same dex file +with multiple class loaders. diff --git a/test/156-register-dex-file-multi-loader/src/Main.java b/test/156-register-dex-file-multi-loader/src/Main.java new file mode 100644 index 0000000000..ff5a2bd570 --- /dev/null +++ b/test/156-register-dex-file-multi-loader/src/Main.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + +class MyClassLoader extends ClassLoader { + MyClassLoader() throws Exception { + super(MyClassLoader.class.getClassLoader()); + + // Some magic to get access to the pathList field of BaseDexClassLoader. + ClassLoader loader = getClass().getClassLoader(); + Class<?> baseDexClassLoader = loader.getClass().getSuperclass(); + Field f = baseDexClassLoader.getDeclaredField("pathList"); + f.setAccessible(true); + Object pathList = f.get(loader); + + // Some magic to get access to the dexField field of pathList. + f = pathList.getClass().getDeclaredField("dexElements"); + f.setAccessible(true); + dexElements = (Object[]) f.get(pathList); + dexFileField = dexElements[0].getClass().getDeclaredField("dexFile"); + dexFileField.setAccessible(true); + } + + Object[] dexElements; + Field dexFileField; + + protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { + // Mimic what DexPathList.findClass is doing. + try { + for (Object element : dexElements) { + Object dex = dexFileField.get(element); + Method method = dex.getClass().getDeclaredMethod( + "loadClassBinaryName", String.class, ClassLoader.class, List.class); + + if (dex != null) { + Class<?> clazz = (Class<?>)method.invoke(dex, className, this, null); + if (clazz != null) { + return clazz; + } + } + } + } catch (InvocationTargetException ite) { + throw new ClassNotFoundException(className, ite.getCause()); + } catch (Exception e) { + throw new Error(e); + } + return getParent().loadClass(className); + } +} + +public class Main { + public static void main(String[] args) throws Exception { + MyClassLoader o = new MyClassLoader(); + try { + Class<?> foo = o.loadClass("Main"); + throw new Error("Unreachable"); + } catch (ClassNotFoundException cnfe) { + boolean unexpected = false; + if (!(cnfe.getCause() instanceof InternalError)) { + unexpected = true; + } else { + String message = cnfe.getCause().getMessage(); + unexpected = !message.startsWith("Attempt to register dex file ") || + !message.endsWith(" with multiple class loaders"); + } + if (unexpected) { + cnfe.getCause().printStackTrace(); + } + } + } +} diff --git a/test/626-const-class-linking/src/Main.java b/test/626-const-class-linking/src/Main.java index 0029428d90..1bc94a7c7d 100644 --- a/test/626-const-class-linking/src/Main.java +++ b/test/626-const-class-linking/src/Main.java @@ -23,8 +23,10 @@ import java.util.ArrayList; public class Main { public static void main(String[] args) throws Exception { try { + // Check if we're running dalvik or RI. + Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader"); System.loadLibrary(args[0]); - } catch (UnsatisfiedLinkError ule) { + } catch (ClassNotFoundException e) { usingRI = true; // Add expected JNI_OnLoad log line to match expected.txt. System.out.println("JNI_OnLoad called"); |