Revert^8 "Make sure that const-class linkage is preserved."
Replaced two ReaderMutexLocks with WriterMutexLocks.
Removed some unnecessary debugging output.
Test: m test-art-host
Bug: 30627598
Original-Change-Id: Ie9b721464b4e9a5dcce8df8095548e983bba1fe8
This reverts commit 2c8c6b63da6ecb2ac701cc30f9b4fa4a8eea5cc8.
Change-Id: I3a1aeecf64e4b202cef61cceb248d48106a2f4a6
diff --git a/test/626-const-class-linking/src/Main.java b/test/626-const-class-linking/src/Main.java
new file mode 100644
index 0000000..0029428
--- /dev/null
+++ b/test/626-const-class-linking/src/Main.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2016 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.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try {
+ System.loadLibrary(args[0]);
+ } catch (UnsatisfiedLinkError ule) {
+ usingRI = true;
+ // Add expected JNI_OnLoad log line to match expected.txt.
+ System.out.println("JNI_OnLoad called");
+ }
+
+ testClearDexCache();
+ testMultiDex();
+ testRacyLoader();
+ testRacyLoader2();
+ testMisbehavingLoader();
+ testRacyMisbehavingLoader();
+ testRacyMisbehavingLoader2();
+ }
+
+ private static void testClearDexCache() throws Exception {
+ DelegatingLoader delegating_loader = createDelegatingLoader();
+ Class<?> helper = delegating_loader.loadClass("Helper1");
+
+ WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper);
+ changeInner(delegating_loader);
+ clearResolvedTypes(helper);
+ Runtime.getRuntime().gc();
+ WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper);
+ Runtime.getRuntime().gc();
+
+ Class<?> test1 = weak_test1.get();
+ if (test1 == null) {
+ System.out.println("test1 disappeared");
+ }
+ Class<?> test2 = weak_test2.get();
+ if (test2 == null) {
+ System.out.println("test2 disappeared");
+ }
+ if (test1 != test2) {
+ System.out.println("test1 != test2");
+ }
+
+ System.out.println("testClearDexCache done");
+ }
+
+ private static void testMultiDex() throws Exception {
+ DelegatingLoader delegating_loader = createDelegatingLoader();
+
+ Class<?> helper1 = delegating_loader.loadClass("Helper1");
+ WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper1);
+
+ changeInner(delegating_loader);
+
+ Class<?> helper2 = delegating_loader.loadClass("Helper2");
+ WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper2);
+
+ Runtime.getRuntime().gc();
+
+ Class<?> test1 = weak_test1.get();
+ if (test1 == null) {
+ System.out.println("test1 disappeared");
+ }
+ Class<?> test2 = weak_test2.get();
+ if (test2 == null) {
+ System.out.println("test2 disappeared");
+ }
+ if (test1 != test2) {
+ System.out.println("test1 != test2");
+ }
+
+ System.out.println("testMultiDex done");
+ }
+
+ private static void testMisbehavingLoader() throws Exception {
+ ClassLoader system_loader = ClassLoader.getSystemClassLoader();
+ DefiningLoader defining_loader = new DefiningLoader(system_loader);
+ MisbehavingLoader misbehaving_loader =
+ new MisbehavingLoader(system_loader, defining_loader);
+ Class<?> helper = misbehaving_loader.loadClass("Helper1");
+
+ try {
+ WeakReference<Class<?>> weak_test = wrapHelperGet(helper);
+ } catch (InvocationTargetException ite) {
+ String message = ite.getCause().getMessage();
+ if (usingRI && "Test".equals(message)) {
+ // Replace RI message with dalvik message to match expected.txt.
+ message = "Initiating class loader of type " +
+ misbehaving_loader.getClass().getName() +
+ " returned class Helper2 instead of Test.";
+ }
+ System.out.println(ite.getCause().getClass().getName() + ": " + message);
+ }
+ System.out.println("testMisbehavingLoader done");
+ }
+
+ private static void testRacyLoader() throws Exception {
+ final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
+
+ final Thread[] threads = new Thread[4];
+ final Object[] results = new Object[threads.length];
+
+ final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length);
+ final Class<?> helper1 = racy_loader.loadClass("Helper1");
+ skipVerification(helper1); // Avoid class loading during verification.
+
+ for (int i = 0; i != threads.length; ++i) {
+ final int my_index = i;
+ Thread t = new Thread() {
+ public void run() {
+ try {
+ Method get = helper1.getDeclaredMethod("get");
+ results[my_index] = get.invoke(null);
+ } catch (InvocationTargetException ite) {
+ results[my_index] = ite.getCause();
+ } catch (Throwable t) {
+ results[my_index] = t;
+ }
+ }
+ };
+ t.start();
+ threads[i] = t;
+ }
+ for (Thread t : threads) {
+ t.join();
+ }
+ dumpResultStats(results, 1);
+ System.out.println("testRacyLoader done");
+ }
+
+ private static void testRacyLoader2() throws Exception {
+ final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
+
+ final Thread[] threads = new Thread[4];
+ final Object[] results = new Object[threads.length];
+
+ final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length);
+ final Class<?> helper1 = racy_loader.loadClass("Helper1");
+ skipVerification(helper1); // Avoid class loading during verification.
+ final Class<?> helper3 = racy_loader.loadClass("Helper3");
+ skipVerification(helper3); // Avoid class loading during verification.
+
+ for (int i = 0; i != threads.length; ++i) {
+ final int my_index = i;
+ Thread t = new Thread() {
+ public void run() {
+ try {
+ Class<?> helper = (my_index < threads.length / 2) ? helper1 : helper3;
+ Method get = helper.getDeclaredMethod("get");
+ results[my_index] = get.invoke(null);
+ } catch (InvocationTargetException ite) {
+ results[my_index] = ite.getCause();
+ } catch (Throwable t) {
+ results[my_index] = t;
+ }
+ }
+ };
+ t.start();
+ threads[i] = t;
+ }
+ for (Thread t : threads) {
+ t.join();
+ }
+ dumpResultStats(results, 2);
+ System.out.println("testRacyLoader2 done");
+ }
+
+ private static void testRacyMisbehavingLoader() throws Exception {
+ final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
+
+ final Thread[] threads = new Thread[4];
+ final Object[] results = new Object[threads.length];
+
+ final RacyMisbehavingLoader racy_loader =
+ new RacyMisbehavingLoader(system_loader, threads.length, false);
+ final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper");
+ skipVerification(helper1); // Avoid class loading during verification.
+
+ for (int i = 0; i != threads.length; ++i) {
+ final int my_index = i;
+ Thread t = new Thread() {
+ public void run() {
+ try {
+ Method get = helper1.getDeclaredMethod("get");
+ results[my_index] = get.invoke(null);
+ } catch (InvocationTargetException ite) {
+ results[my_index] = ite.getCause();
+ } catch (Throwable t) {
+ results[my_index] = t;
+ }
+ }
+ };
+ t.start();
+ threads[i] = t;
+ }
+ for (Thread t : threads) {
+ t.join();
+ }
+ dumpResultStats(results, 1);
+ System.out.println("testRacyMisbehavingLoader done");
+ }
+
+ private static void testRacyMisbehavingLoader2() throws Exception {
+ final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
+
+ final Thread[] threads = new Thread[4];
+ final Object[] results = new Object[threads.length];
+
+ final RacyMisbehavingLoader racy_loader =
+ new RacyMisbehavingLoader(system_loader, threads.length, true);
+ final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper");
+ skipVerification(helper1); // Avoid class loading during verification.
+
+ for (int i = 0; i != threads.length; ++i) {
+ final int my_index = i;
+ Thread t = new Thread() {
+ public void run() {
+ try {
+ Method get = helper1.getDeclaredMethod("get");
+ results[my_index] = get.invoke(null);
+ } catch (InvocationTargetException ite) {
+ results[my_index] = ite.getCause();
+ } catch (Throwable t) {
+ results[my_index] = t;
+ }
+ }
+ };
+ t.start();
+ threads[i] = t;
+ }
+ for (Thread t : threads) {
+ t.join();
+ }
+ dumpResultStats(results, 1);
+ System.out.println("testRacyMisbehavingLoader2 done");
+ }
+
+ private static void dumpResultStats(Object[] results, int expected_unique) throws Exception {
+ int throwables = 0;
+ int classes = 0;
+ int unique_classes = 0;
+ for (int i = 0; i != results.length; ++i) {
+ Object r = results[i];
+ if (r instanceof Throwable) {
+ ++throwables;
+ System.out.println(((Throwable) r).getMessage());
+ } else if (isClassPair(r)) {
+ printPair(r);
+ Object ref = getSecond(r);
+ ++classes;
+ ++unique_classes;
+ for (int j = 0; j != i; ++j) {
+ Object rj = results[j];
+ if (isClassPair(results[j]) && getSecond(results[j]) == ref) {
+ --unique_classes;
+ break;
+ }
+ }
+ }
+ }
+ System.out.println("total: " + results.length);
+ System.out.println(" throwables: " + throwables);
+ System.out.println(" classes: " + classes
+ + " (" + unique_classes + " unique)");
+ if (expected_unique != unique_classes) {
+ System.out.println("MISMATCH with expected_unique: " + expected_unique);
+ ArrayList<Class<?>> list = new ArrayList<Class<?>>();
+ for (int i = 0; i != results.length; ++i) {
+ Object r = results[i];
+ if (isClassPair(r)) {
+ list.add(getSecond(r));
+ }
+ }
+ nativeDumpClasses(list.toArray());
+ }
+ }
+
+ private static DelegatingLoader createDelegatingLoader() {
+ ClassLoader system_loader = ClassLoader.getSystemClassLoader();
+ DefiningLoader defining_loader = new DefiningLoader(system_loader);
+ return new DelegatingLoader(system_loader, defining_loader);
+ }
+
+ private static void changeInner(DelegatingLoader delegating_loader) {
+ ClassLoader system_loader = ClassLoader.getSystemClassLoader();
+ DefiningLoader defining_loader = new DefiningLoader(system_loader);
+ delegating_loader.resetDefiningLoader(defining_loader);
+ }
+
+ private static WeakReference<Class<?>> wrapHelperGet(Class<?> helper) throws Exception {
+ Method get = helper.getDeclaredMethod("get");
+ Object pair = get.invoke(null);
+ printPair(pair);
+ return new WeakReference<Class<?>>(getSecond(pair));
+ }
+
+ private static void printPair(Object pair) throws Exception {
+ Method print = pair.getClass().getDeclaredMethod("print");
+ print.invoke(pair);
+ }
+
+ private static Class<?> getSecond(Object pair) throws Exception {
+ Field second = pair.getClass().getDeclaredField("second");
+ return (Class<?>) second.get(pair);
+ }
+
+ private static boolean isClassPair(Object r) {
+ return r != null && r.getClass().getName().equals("ClassPair");
+ }
+
+ public static void clearResolvedTypes(Class<?> c) {
+ if (!usingRI) {
+ nativeClearResolvedTypes(c);
+ }
+ }
+
+ // Skip verification of a class on ART. Verification can cause classes to be loaded
+ // while holding a lock on the class being verified and holding that lock can interfere
+ // with the intent of the "racy" tests. In these tests we're waiting in the loadClass()
+ // for all the tested threads to synchronize and they cannot reach that point if they
+ // are waiting for the class lock on ClassLinker::InitializeClass(Helper1/Helper3).
+ public static void skipVerification(Class<?> c) {
+ if (!usingRI) {
+ nativeSkipVerification(c);
+ }
+ }
+
+ public static native void nativeClearResolvedTypes(Class<?> c);
+ public static native void nativeSkipVerification(Class<?> c);
+ public static native void nativeDumpClasses(Object[] array);
+
+ static boolean usingRI = false;
+}