summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/driver/compiler_driver.cc23
-rw-r--r--compiler/oat_writer.cc8
-rw-r--r--compiler/optimizing/reference_type_propagation.cc9
-rw-r--r--dex2oat/dex2oat.cc6
-rw-r--r--oatdump/oatdump.cc5
-rw-r--r--runtime/class_linker.cc209
-rw-r--r--runtime/class_linker.h51
-rw-r--r--runtime/class_linker_test.cc4
-rw-r--r--runtime/class_table.cc13
-rw-r--r--runtime/class_table.h6
-rw-r--r--runtime/native/dalvik_system_DexFile.cc12
-rw-r--r--runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc10
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc9
-rw-r--r--runtime/native/java_lang_DexCache.cc21
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc4
-rw-r--r--test/155-java-set-resolved-type/expected.txt1
-rw-r--r--test/155-java-set-resolved-type/info.txt2
-rw-r--r--test/155-java-set-resolved-type/src-ex/TestInterface.java19
-rw-r--r--test/155-java-set-resolved-type/src/Main.java98
-rw-r--r--test/155-java-set-resolved-type/src/TestImplementation.java19
-rw-r--r--test/155-java-set-resolved-type/src/TestInterface.java19
-rw-r--r--test/155-java-set-resolved-type/src/TestParameter.java19
-rw-r--r--test/156-register-dex-file-multi-loader/expected.txt0
-rw-r--r--test/156-register-dex-file-multi-loader/info.txt2
-rw-r--r--test/156-register-dex-file-multi-loader/src/Main.java88
-rw-r--r--test/626-const-class-linking/src/Main.java4
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");