diff options
Diffstat (limited to 'test/626-const-class-linking/src/Main.java')
-rw-r--r-- | test/626-const-class-linking/src/Main.java | 354 |
1 files changed, 354 insertions, 0 deletions
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 0000000000..0029428d90 --- /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; +} |