/* * 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. */ package art; import java.lang.ref.Reference; import java.lang.reflect.Constructor; import java.lang.reflect.Proxy; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.Comparator; public class Test912 { public static void run() throws Exception { doTest(); } public static void doTest() throws Exception { testClass("java.lang.Object"); testClass("java.lang.String"); testClass("java.lang.Math"); testClass("java.util.List"); testClass(getProxyClass()); testClass(int.class); testClass(double[].class); testClassType(int.class); testClassType(getProxyClass()); testClassType(Runnable.class); testClassType(String.class); testClassType(ArrayList.class); testClassType(int[].class); testClassType(Runnable[].class); testClassType(String[].class); testClassFields(Integer.class); testClassFields(int.class); testClassFields(String[].class); testClassMethods(Integer.class); testClassMethods(int.class); testClassMethods(String[].class); testClassStatus(int.class); testClassStatus(String[].class); testClassStatus(Object.class); testClassStatus(TestForNonInit.class); try { System.out.println(TestForInitFail.intValue); } catch (ExceptionInInitializerError e) { } testClassStatus(TestForInitFail.class); testInterfaces(int.class); testInterfaces(String[].class); testInterfaces(Object.class); testInterfaces(InfA.class); testInterfaces(InfB.class); testInterfaces(InfC.class); testInterfaces(ClassA.class); testInterfaces(ClassB.class); testInterfaces(ClassC.class); testClassLoader(String.class); testClassLoader(String[].class); testClassLoader(InfA.class); testClassLoader(getProxyClass()); testClassLoaderClasses(); System.out.println(); testClassVersion(); System.out.println(); // Use a dedicated thread to have a well-defined current thread. Thread classEventsThread = new Thread("ClassEvents") { @Override public void run() { try { testClassEvents(); } catch (Exception e) { throw new RuntimeException(e); } } }; classEventsThread.start(); classEventsThread.join(); // b/146170757 TestRecursiveClassPrepareEvents(); } private static void testClass(String className) throws Exception { Class base = Class.forName(className); testClass(base); } private static void testClass(Class base) throws Exception { String[] result = getClassSignature(base); System.out.println(Arrays.toString(result)); int mod = getClassModifiers(base); if (mod != base.getModifiers()) { throw new RuntimeException("Unexpected modifiers: " + base.getModifiers() + " vs " + mod); } System.out.println(Integer.toHexString(mod)); } private static void testClassType(Class c) throws Exception { boolean isInterface = isInterface(c); boolean isArray = isArrayClass(c); boolean isModifiable = isModifiableClass(c); System.out.println(c.getName() + " interface=" + isInterface + " array=" + isArray + " modifiable=" + isModifiable); } private static void testClassFields(Class c) throws Exception { System.out.println(Arrays.toString(getClassFields(c))); } private static void testClassMethods(Class c) throws Exception { System.out.println(Arrays.toString(getClassMethods(c))); } private static void testClassStatus(Class c) { System.out.println(c + " " + Integer.toBinaryString(getClassStatus(c))); } private static void testInterfaces(Class c) { System.out.println(c + " " + Arrays.toString(getImplementedInterfaces(c))); } private static boolean IsBootClassLoader(ClassLoader l) { // Hacky check for Android's fake boot classloader. return l.getClass().getName().equals("java.lang.BootClassLoader"); } private static void testClassLoader(Class c) { Object cl = getClassLoader(c); System.out.println(c + " " + (cl != null ? cl.getClass().getName() : "null")); if (cl == null) { if (c.getClassLoader() != null && !IsBootClassLoader(c.getClassLoader())) { throw new RuntimeException("Expected " + c.getClassLoader() + ", but got null."); } } else { if (!(cl instanceof ClassLoader)) { throw new RuntimeException("Unexpected \"classloader\": " + cl + " (" + cl.getClass() + ")"); } if (cl != c.getClassLoader()) { throw new RuntimeException("Unexpected classloader: " + c.getClassLoader() + " vs " + cl); } } } private static void testClassLoaderClasses() throws Exception { System.out.println(); System.out.println("boot <- (B) <- (A,C)"); ClassLoader cl1 = DexData.create2(DexData.create1()); Class.forName("B", false, cl1); Class.forName("A", false, cl1); printClassLoaderClasses(cl1); System.out.println(); System.out.println("boot <- (B) <- (A, List)"); ClassLoader cl2 = DexData.create2(DexData.create1()); Class.forName("A", false, cl2); Class.forName("java.util.List", false, cl2); Class.forName("B", false, cl2.getParent()); printClassLoaderClasses(cl2); System.out.println(); System.out.println("boot <- 1+2 (A,B)"); ClassLoader cl3 = DexData.create12(); Class.forName("B", false, cl3); Class.forName("A", false, cl3); printClassLoaderClasses(cl3); // Check that the boot classloader dumps something non-empty. ClassLoader boot = ClassLoader.getSystemClassLoader().getParent(); while (boot.getParent() != null) { boot = boot.getParent(); } Class[] bootClasses = getClassLoaderClasses(boot); if (bootClasses.length == 0) { throw new RuntimeException("No classes initiated by boot classloader."); } // Check that at least java.util.List is loaded. boolean foundList = false; for (Class c : bootClasses) { if (c == java.util.List.class) { foundList = true; break; } } if (!foundList) { System.out.println(Arrays.toString(bootClasses)); throw new RuntimeException("Could not find class java.util.List."); } } /** * base64 encoded class/dex file for * class Transform { * public void sayHi() { * System.out.println("Goodbye"); * } * } */ private static final byte[] DEX_BYTES = Base64.getDecoder().decode( "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" + "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" + "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" + "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" + "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" + "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); private static void testClassVersion() throws Exception { Class class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader"); Constructor ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class); Class target = ((ClassLoader)ctor.newInstance( ByteBuffer.wrap(DEX_BYTES), Test912.class.getClassLoader())).loadClass("Transform"); System.out.println(Arrays.toString(getClassVersion(target))); } private static void testClassEvents() throws Exception { ClassLoader cl = Main.class.getClassLoader(); while (cl.getParent() != null) { cl = cl.getParent(); } final ClassLoader boot = cl; // The JIT may deeply inline and load some classes. Preload these for test determinism. final String PRELOAD_FOR_JIT[] = { "java.nio.charset.CoderMalfunctionError", "java.util.NoSuchElementException", "java.io.FileNotFoundException", // b/63581208 "java.util.zip.ZipException", // b/63581208 "sun.invoke.util.Wrapper", // For "ClassEvents" thread in JIT/no-image config. }; for (String s : PRELOAD_FOR_JIT) { Class.forName(s); } Runnable r = new Runnable() { @Override public void run() { try { ClassLoader cl6 = DexData.create12(); System.out.println("C, true"); Class.forName("C", true, cl6); printClassLoadMessages(); } catch (Exception e) { throw new RuntimeException(e); } } }; Thread noopThread = new Thread(); noopThread.start(); noopThread.join(); enableClassLoadPreparePrintEvents(true, Thread.currentThread()); ClassLoader cl1 = DexData.create12(); System.out.println("B, false"); Class.forName("B", false, cl1); printClassLoadMessages(); ClassLoader cl2 = DexData.create12(); System.out.println("B, true"); Class.forName("B", true, cl2); printClassLoadMessages(); ClassLoader cl3 = DexData.create12(); System.out.println("C, false"); Class.forName("C", false, cl3); printClassLoadMessages(); System.out.println("A, false"); Class.forName("A", false, cl3); printClassLoadMessages(); ClassLoader cl4 = DexData.create12(); System.out.println("C, true"); Class.forName("C", true, cl4); printClassLoadMessages(); System.out.println("A, true"); Class.forName("A", true, cl4); printClassLoadMessages(); ClassLoader cl5 = DexData.create12(); System.out.println("A, true"); Class.forName("A", true, cl5); printClassLoadMessages(); System.out.println("C, true"); Class.forName("C", true, cl5); printClassLoadMessages(); enableClassLoadPreparePrintEvents(false, null); Thread t = new Thread(r, "TestRunner"); enableClassLoadPreparePrintEvents(true, t); t.start(); t.join(); enableClassLoadPreparePrintEvents(false, null); enableClassLoadPreparePrintEvents(true, Thread.currentThread()); // Check creation of arrays and proxies. Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Comparable.class, I0.class }); Class.forName("[Lart.Test912;"); printClassLoadMessages(); enableClassLoadPreparePrintEvents(false, null); testClassLoadPrepareEquality(); } private static void testClassLoadPrepareEquality() throws Exception { setEqualityEventStorageClass(ClassF.class); enableClassLoadPrepareEqualityEvents(true); Class.forName("art.Test912$ClassE"); enableClassLoadPrepareEqualityEvents(false); } private static void printClassLoaderClasses(ClassLoader cl) { for (;;) { if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) { break; } Class classes[] = getClassLoaderClasses(cl); Arrays.sort(classes, new ClassNameComparator()); System.out.println(Arrays.toString(classes)); cl = cl.getParent(); } } private static void printClassLoadMessages() { for (String s : getClassLoadMessages()) { System.out.println(s); } } private static native boolean isModifiableClass(Class c); private static native String[] getClassSignature(Class c); private static native boolean isInterface(Class c); private static native boolean isArrayClass(Class c); private static native int getClassModifiers(Class c); private static native Object[] getClassFields(Class c); private static native Object[] getClassMethods(Class c); private static native Class[] getImplementedInterfaces(Class c); private static native int getClassStatus(Class c); private static native Object getClassLoader(Class c); private static native Class[] getClassLoaderClasses(ClassLoader cl); private static native int[] getClassVersion(Class c); private static native void enableClassLoadPreparePrintEvents(boolean b, Thread filter); private static native String[] getClassLoadMessages(); private static native void setEqualityEventStorageClass(Class c); private static native void enableClassLoadPrepareEqualityEvents(boolean b); private static native void runRecursiveClassPrepareEvents(Runnable forceLoad); private static void TestRecursiveClassPrepareEvents() { final int[] called = new int[] { 0 }; runRecursiveClassPrepareEvents(() -> { if (called[0] == 2) { return; } else { called[0]++; } try { System.out.println("class-prepare event START!"); // Load a new class in a new class-loader. Class class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader"); Constructor ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class); Class target = ((ClassLoader)ctor.newInstance( ByteBuffer.wrap(DEX_BYTES), Test912.class.getClassLoader())).loadClass("Transform"); target.newInstance(); } catch (Exception e) { } System.out.println("class-prepare event END!"); }); if (called[0] != 2) { System.out.println("Failed to cause recursive Class prepare."); } } private static class TestForNonInit { public static double doubleValue = Math.random(); // So it can't be compile-time initialized. } @SuppressWarnings("RandomCast") private static class TestForInitFail { public static int intValue = ((int)Math.random())/0; // So it throws when initializing. } public static interface InfA { } public static interface InfB extends InfA { } public static interface InfC extends InfB { } public abstract static class ClassA implements InfA { } public abstract static class ClassB extends ClassA implements InfB { } public abstract static class ClassC implements InfA, InfC { } public static class ClassE { public void foo() { } public void bar() { } } public static class ClassF { public static Object STATIC = null; public static Reference WEAK = null; } private static class ClassNameComparator implements Comparator> { public int compare(Class c1, Class c2) { return c1.getName().compareTo(c2.getName()); } } // See run-test 910 for an explanation. private static Class proxyClass = null; private static Class getProxyClass() throws Exception { if (proxyClass != null) { return proxyClass; } for (int i = 1; i <= 21; i++) { proxyClass = createProxyClass(i); String name = proxyClass.getName(); if (name.equals("$Proxy20")) { return proxyClass; } } return proxyClass; } private static Class createProxyClass(int i) throws Exception { int count = Integer.bitCount(i); Class[] input = new Class[count + 1]; input[0] = Runnable.class; int inputIndex = 1; int bitIndex = 0; while (i != 0) { if ((i & 1) != 0) { input[inputIndex++] = Class.forName("art.Test912$I" + bitIndex); } i >>>= 1; bitIndex++; } return Proxy.getProxyClass(Test912.class.getClassLoader(), input); } // Need this for the proxy naming. public static interface I0 { } public static interface I1 { } public static interface I2 { } public static interface I3 { } public static interface I4 { } }