Add native support for BaseDexClassLoader shared libraries.
bug: 112405321
Test: 688-shared-library
Change-Id: Ia993b3ded71d4491a59fb78b0282eacdb66634bd
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b747225..ba520f9 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2404,6 +2404,35 @@
return ClassPathEntry(nullptr, nullptr);
}
+bool ClassLinker::FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader,
+ /*out*/ ObjPtr<mirror::Class>* result) {
+ ArtField* field =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader.Get());
+ if (raw_shared_libraries == nullptr) {
+ return true;
+ }
+
+ StackHandleScope<2> hs(self);
+ Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
+ hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+ MutableHandle<mirror::ClassLoader> temp_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
+ for (int32_t i = 0; i < shared_libraries->GetLength(); ++i) {
+ temp_loader.Assign(shared_libraries->Get(i));
+ if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, temp_loader, result)) {
+ return false; // One of the shared libraries is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class up the chain.
+ }
+ }
+ return true;
+}
+
bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self,
const char* descriptor,
@@ -2419,6 +2448,7 @@
if (IsPathOrDexClassLoader(soa, class_loader)) {
// For regular path or dex class loader the search order is:
// - parent
+ // - shared libraries
// - class loader dex files
// Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
@@ -2431,6 +2461,13 @@
return true; // Found the class up the chain.
}
+ if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) {
+ return false; // One of the shared library loader is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class in a shared library.
+ }
+
// Search the current class loader classpath.
*result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
return true;
@@ -2439,6 +2476,7 @@
if (IsDelegateLastClassLoader(soa, class_loader)) {
// For delegate last, the search order is:
// - boot class path
+ // - shared libraries
// - class loader dex files
// - parent
*result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
@@ -2446,6 +2484,13 @@
return true; // The class is part of the boot class path.
}
+ if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) {
+ return false; // One of the shared library loader is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class in a shared library.
+ }
+
*result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
if (*result != nullptr) {
return true; // Found the class in the current class loader
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 0e276cd..4aa9fcd 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -875,6 +875,15 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
+ bool FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader,
+ /*out*/ ObjPtr<mirror::Class>* result)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
// Finds the class in the classpath of the given class loader. It only searches the class loader
// dex files and does not recurse into its parent.
// The method checks that the provided class loader is either a PathClassLoader or a
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 206418f..ef95cca 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -119,6 +119,7 @@
jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
jfieldID WellKnownClasses::dalvik_system_DexFile_fileName;
jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
+jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
@@ -365,6 +366,7 @@
org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
dalvik_system_BaseDexClassLoader_pathList = CacheField(env, dalvik_system_BaseDexClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
+ dalvik_system_BaseDexClassLoader_sharedLibraryLoaders = CacheField(env, dalvik_system_BaseDexClassLoader, false, "sharedLibraryLoaders", "[Ljava/lang/ClassLoader;");
dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;");
dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;");
dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index ce5ab1d..01e7d99 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -126,6 +126,7 @@
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
static jfieldID dalvik_system_BaseDexClassLoader_pathList;
+ static jfieldID dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
static jfieldID dalvik_system_DexFile_cookie;
static jfieldID dalvik_system_DexFile_fileName;
static jfieldID dalvik_system_DexPathList_dexElements;
diff --git a/test/688-shared-library/expected.txt b/test/688-shared-library/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/688-shared-library/expected.txt
diff --git a/test/688-shared-library/info.txt b/test/688-shared-library/info.txt
new file mode 100644
index 0000000..2eda65d
--- /dev/null
+++ b/test/688-shared-library/info.txt
@@ -0,0 +1,2 @@
+Tests on BaseDexClassLoader shared libraries and their class
+loading behavior.
diff --git a/test/688-shared-library/src-art/Main.java b/test/688-shared-library/src-art/Main.java
new file mode 100644
index 0000000..d59e7dc
--- /dev/null
+++ b/test/688-shared-library/src-art/Main.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018 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 dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
+
+public class Main {
+ static final String TEST_NAME = "688-shared-library";
+ static final String MAIN_JAR_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar";
+ static final String EX_JAR_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + "-ex.jar";
+ static ClassLoader bootLoader = Object.class.getClassLoader();
+
+ public static void main(String[] args) throws Exception {
+ testNoLibrary();
+ testOneLibrary();
+ testTwoLibraries1();
+ testTwoLibraries2();
+ testTransitive1();
+ testTransitive2();
+ testTransitive3();
+ testTransitive4();
+ }
+
+ public static void assertIdentical(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void testNoLibrary() throws Exception {
+ ClassLoader loader = new PathClassLoader(MAIN_JAR_FILE, null, bootLoader);
+ Class<?> cls = loader.loadClass("Main");
+ assertIdentical(loader, cls.getClassLoader());
+ }
+
+ public static void testOneLibrary() throws Exception {
+ ClassLoader[] sharedLibraries = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader delegateFirst =
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ }
+
+ public static void testTwoLibraries1() throws Exception {
+ ClassLoader[] sharedLibraries = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraries);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[1], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[1], cls.getClassLoader());
+ }
+
+ public static void testTwoLibraries2() throws Exception {
+ ClassLoader[] sharedLibraries = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraries);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+
+ ClassLoader delegateLast = new DelegateLastClassLoader("", null, bootLoader, sharedLibraries);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive1() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive2() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive3() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ sharedLibraryLevel2[0],
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive4() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ sharedLibraryLevel2[0],
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ }
+}
diff --git a/test/688-shared-library/src-ex/Main.java b/test/688-shared-library/src-ex/Main.java
new file mode 100644
index 0000000..f6555b9
--- /dev/null
+++ b/test/688-shared-library/src-ex/Main.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 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 Main {
+}
diff --git a/test/688-shared-library/src-ex/SharedLibraryOne.java b/test/688-shared-library/src-ex/SharedLibraryOne.java
new file mode 100644
index 0000000..d86755f
--- /dev/null
+++ b/test/688-shared-library/src-ex/SharedLibraryOne.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 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 SharedLibraryOne {
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index e27a4d6..3cc58ac 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1024,6 +1024,7 @@
"677-fsi2",
"678-quickening",
"679-locks",
+ "688-shared-library",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1951-monitor-enter-no-suspend"],