Add DelegateLastClassLoader to the list of WellKnownClassLoaders
Generalize CreatePathClassLoader from the class linker to be able to
create a well known class loader with a given parent.
For now, it only supports PathClassLoader and DelegateLastClassLoader.
Test: m test-art-host
Bug: 38138251
Change-Id: Iced0de7c21010c6d61dbf29a631e670011833a7d
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index cc8402f..e1817c0 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8764,8 +8764,15 @@
return descriptor;
}
-jobject ClassLinker::CreatePathClassLoader(Thread* self,
- const std::vector<const DexFile*>& dex_files) {
+jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files,
+ jclass loader_class,
+ jobject parent_loader) {
+ CHECK(self->GetJniEnv()->IsSameObject(loader_class,
+ WellKnownClasses::dalvik_system_PathClassLoader) ||
+ self->GetJniEnv()->IsSameObject(loader_class,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader));
+
// SOAAlreadyRunnable is protected, and we need something to add a global reference.
// We could move the jobject to the callers, but all call-sites do this...
ScopedObjectAccessUnchecked soa(self);
@@ -8801,8 +8808,8 @@
for (const DexFile* dex_file : dex_files) {
StackHandleScope<4> hs2(self);
- // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the
- // oat file but we can leave it null.
+ // CreateWellKnownClassLoader is only used by gtests and compiler.
+ // Index 0 of h_long_array is supposed to be the oat file but we can leave it null.
Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
self,
kDexFileIndexStart + 1));
@@ -8837,36 +8844,44 @@
// Set elements.
dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get());
- // Create PathClassLoader.
- Handle<mirror::Class> h_path_class_class = hs.NewHandle(
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
- Handle<mirror::Object> h_path_class_loader = hs.NewHandle(
- h_path_class_class->AllocObject(self));
- DCHECK(h_path_class_loader != nullptr);
+ // Create the class loader..
+ Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class));
+ Handle<mirror::Object> h_class_loader = hs.NewHandle(h_loader_class->AllocObject(self));
+ DCHECK(h_class_loader != nullptr);
// Set DexPathList.
ArtField* path_list_field =
jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
DCHECK(path_list_field != nullptr);
- path_list_field->SetObject<false>(h_path_class_loader.Get(), h_dex_path_list.Get());
+ path_list_field->SetObject<false>(h_class_loader.Get(), h_dex_path_list.Get());
// Make a pretend boot-classpath.
// TODO: Should we scan the image?
ArtField* const parent_field =
mirror::Class::FindField(self,
- h_path_class_loader->GetClass(),
+ h_class_loader->GetClass(),
"parent",
"Ljava/lang/ClassLoader;");
DCHECK(parent_field != nullptr);
- ObjPtr<mirror::Object> boot_cl =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
- parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl);
+
+ ObjPtr<mirror::Object> parent = (parent_loader != nullptr)
+ ? soa.Decode<mirror::ClassLoader>(parent_loader)
+ : soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
+ parent_field->SetObject<false>(h_class_loader.Get(), parent);
// Make it a global ref and return.
ScopedLocalRef<jobject> local_ref(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(h_path_class_loader.Get()));
+ soa.Env(), soa.Env()->AddLocalReference<jobject>(h_class_loader.Get()));
return soa.Env()->NewGlobalRef(local_ref.get());
}
+jobject ClassLinker::CreatePathClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files) {
+ return CreateWellKnownClassLoader(self,
+ dex_files,
+ WellKnownClasses::dalvik_system_PathClassLoader,
+ nullptr);
+}
+
void ClassLinker::DropFindArrayClassCache() {
std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
find_array_class_cache_next_victim_ = 0;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 3773cd6..de1fefd 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -553,8 +553,24 @@
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files.
+ // Creates a GlobalRef PathClassLoader or DelegateLastClassLoader (specified by loader_class)
+ // that can be used to load classes from the given dex files. The parent of the class loader
+ // will be set to `parent_loader`. If `parent_loader` is null the parent will be
+ // the boot class loader.
+ // If class_loader points to a different class than PathClassLoader or DelegateLastClassLoader
+ // this method will abort.
// Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
+ jobject CreateWellKnownClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files,
+ jclass loader_class,
+ jobject parent_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
+ // Calls CreateWellKnownClassLoader(self,
+ // dex_files,
+ // WellKnownClasses::dalvik_system_PathClassLoader,
+ // nullptr)
jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 684a261..6f06917 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1533,4 +1533,15 @@
ASSERT_TRUE(method1_type.Get() != method2_type.Get());
}
+// Verify that ClassLinker's CreateWellknownClassLoader works as expected
+// by creating a chain of class loaders with various dex files.
+TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) {
+ // LoadDexIn*ClassLoader methods already assert that the parent loader is the expected one.
+ // No need to check again.
+ jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr);
+ jobject class_loader_b = LoadDexInDelegateLastClassLoader("Nested", class_loader_a);
+ jobject class_loader_c = LoadDexInPathClassLoader("MultiDex", class_loader_b);
+ LoadDexInDelegateLastClassLoader("Interfaces", class_loader_c);
+}
+
} // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index f925994..da89c2a 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -672,19 +672,66 @@
}
jobject CommonRuntimeTestImpl::LoadDex(const char* dex_name) {
- std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
+ jobject class_loader = LoadDexInPathClassLoader(dex_name, nullptr);
+ Thread::Current()->SetClassLoaderOverride(class_loader);
+ return class_loader;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::string& dex_name,
+ jclass loader_class,
+ jobject parent_loader) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name.c_str());
std::vector<const DexFile*> class_path;
CHECK_NE(0U, dex_files.size());
for (auto& dex_file : dex_files) {
class_path.push_back(dex_file.get());
loaded_dex_files_.push_back(std::move(dex_file));
}
-
Thread* self = Thread::Current();
- jobject class_loader = Runtime::Current()->GetClassLinker()->CreatePathClassLoader(self,
- class_path);
- self->SetClassLoaderOverride(class_loader);
- return class_loader;
+ ScopedObjectAccess soa(self);
+
+ jobject result = Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
+ self,
+ class_path,
+ loader_class,
+ parent_loader);
+
+ {
+ // Verify we build the correct chain.
+
+ ObjPtr<mirror::ClassLoader> actual_class_loader = soa.Decode<mirror::ClassLoader>(result);
+ // Verify that the result has the correct class.
+ CHECK_EQ(soa.Decode<mirror::Class>(loader_class), actual_class_loader->GetClass());
+ // Verify that the parent is not null. The boot class loader will be set up as a
+ // proper object.
+ ObjPtr<mirror::ClassLoader> actual_parent(actual_class_loader->GetParent());
+ CHECK(actual_parent != nullptr);
+
+ if (parent_loader != nullptr) {
+ // We were given a parent. Verify that it's what we expect.
+ ObjPtr<mirror::ClassLoader> expected_parent = soa.Decode<mirror::ClassLoader>(parent_loader);
+ CHECK_EQ(expected_parent, actual_parent);
+ } else {
+ // No parent given. The parent must be the BootClassLoader.
+ CHECK(Runtime::Current()->GetClassLinker()->IsBootClassLoader(soa, actual_parent));
+ }
+ }
+
+ return result;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::string& dex_name,
+ jobject parent_loader) {
+ return LoadDexInWellKnownClassLoader(dex_name,
+ WellKnownClasses::dalvik_system_PathClassLoader,
+ parent_loader);
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::string& dex_name,
+ jobject parent_loader) {
+ return LoadDexInWellKnownClassLoader(dex_name,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+ parent_loader);
}
std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 1274a36..94ca22d 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -135,10 +135,20 @@
std::unique_ptr<const DexFile> OpenTestDexFile(const char* name)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Loads the test dex file identified by the given dex_name into a PathClassLoader.
+ // Returns the created class loader.
jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Loads the test dex file identified by the given first_dex_name and second_dex_name
+ // into a PathClassLoader. Returns the created class loader.
jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
REQUIRES_SHARED(Locks::mutator_lock_);
+ jobject LoadDexInPathClassLoader(const std::string& dex_name, jobject parent_loader);
+ jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader);
+ jobject LoadDexInWellKnownClassLoader(const std::string& dex_name,
+ jclass loader_class,
+ jobject parent_loader);
+
std::string android_data_;
std::string dalvik_cache_;
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 24f194b..8d505e2 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -37,6 +37,7 @@
jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
+jclass WellKnownClasses::dalvik_system_DelegateLastClassLoader;
jclass WellKnownClasses::dalvik_system_DexClassLoader;
jclass WellKnownClasses::dalvik_system_DexFile;
jclass WellKnownClasses::dalvik_system_DexPathList;
@@ -270,6 +271,7 @@
CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
dalvik_system_BaseDexClassLoader = CacheClass(env, "dalvik/system/BaseDexClassLoader");
+ dalvik_system_DelegateLastClassLoader = CacheClass(env, "dalvik/system/DelegateLastClassLoader");
dalvik_system_DexClassLoader = CacheClass(env, "dalvik/system/DexClassLoader");
dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c184731..c5a16c1 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -47,6 +47,7 @@
static jclass dalvik_annotation_optimization_CriticalNative;
static jclass dalvik_annotation_optimization_FastNative;
static jclass dalvik_system_BaseDexClassLoader;
+ static jclass dalvik_system_DelegateLastClassLoader;
static jclass dalvik_system_DexClassLoader;
static jclass dalvik_system_DexFile;
static jclass dalvik_system_DexPathList;