1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package art;
18 
19 import java.lang.ref.Reference;
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.Proxy;
22 import java.nio.ByteBuffer;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Base64;
26 import java.util.Comparator;
27 
28 public class Test912 {
run()29   public static void run() throws Exception {
30     doTest();
31   }
32 
doTest()33   public static void doTest() throws Exception {
34     testClass("java.lang.Object");
35     testClass("java.lang.String");
36     testClass("java.lang.Math");
37     testClass("java.util.List");
38 
39     testClass(getProxyClass());
40 
41     testClass(int.class);
42     testClass(double[].class);
43 
44     testClassType(int.class);
45     testClassType(getProxyClass());
46     testClassType(Runnable.class);
47     testClassType(String.class);
48     testClassType(ArrayList.class);
49 
50     testClassType(int[].class);
51     testClassType(Runnable[].class);
52     testClassType(String[].class);
53 
54     testClassFields(Integer.class);
55     testClassFields(int.class);
56     testClassFields(String[].class);
57 
58     testClassMethods(Integer.class);
59     testClassMethods(int.class);
60     testClassMethods(String[].class);
61 
62     testClassStatus(int.class);
63     testClassStatus(String[].class);
64     testClassStatus(Object.class);
65     testClassStatus(TestForNonInit.class);
66     try {
67       System.out.println(TestForInitFail.dummy);
68     } catch (ExceptionInInitializerError e) {
69     }
70     testClassStatus(TestForInitFail.class);
71 
72     testInterfaces(int.class);
73     testInterfaces(String[].class);
74     testInterfaces(Object.class);
75     testInterfaces(InfA.class);
76     testInterfaces(InfB.class);
77     testInterfaces(InfC.class);
78     testInterfaces(ClassA.class);
79     testInterfaces(ClassB.class);
80     testInterfaces(ClassC.class);
81 
82     testClassLoader(String.class);
83     testClassLoader(String[].class);
84     testClassLoader(InfA.class);
85     testClassLoader(getProxyClass());
86 
87     testClassLoaderClasses();
88 
89     System.out.println();
90 
91     testClassVersion();
92 
93     System.out.println();
94 
95     // Use a dedicated thread to have a well-defined current thread.
96     Thread classEventsThread = new Thread("ClassEvents") {
97       @Override
98       public void run() {
99         try {
100           testClassEvents();
101         } catch (Exception e) {
102           throw new RuntimeException(e);
103         }
104       }
105     };
106     classEventsThread.start();
107     classEventsThread.join();
108 
109     // b/146170757
110     TestRecursiveClassPrepareEvents();
111   }
112 
testClass(String className)113   private static void testClass(String className) throws Exception {
114     Class<?> base = Class.forName(className);
115     testClass(base);
116   }
117 
testClass(Class<?> base)118   private static void testClass(Class<?> base) throws Exception {
119     String[] result = getClassSignature(base);
120     System.out.println(Arrays.toString(result));
121     int mod = getClassModifiers(base);
122     if (mod != base.getModifiers()) {
123       throw new RuntimeException("Unexpected modifiers: " + base.getModifiers() + " vs " + mod);
124     }
125     System.out.println(Integer.toHexString(mod));
126   }
127 
testClassType(Class<?> c)128   private static void testClassType(Class<?> c) throws Exception {
129     boolean isInterface = isInterface(c);
130     boolean isArray = isArrayClass(c);
131     boolean isModifiable = isModifiableClass(c);
132     System.out.println(c.getName() + " interface=" + isInterface + " array=" + isArray +
133         " modifiable=" + isModifiable);
134   }
135 
testClassFields(Class<?> c)136   private static void testClassFields(Class<?> c) throws Exception {
137     System.out.println(Arrays.toString(getClassFields(c)));
138   }
139 
testClassMethods(Class<?> c)140   private static void testClassMethods(Class<?> c) throws Exception {
141     System.out.println(Arrays.toString(getClassMethods(c)));
142   }
143 
testClassStatus(Class<?> c)144   private static void testClassStatus(Class<?> c) {
145     System.out.println(c + " " + Integer.toBinaryString(getClassStatus(c)));
146   }
147 
testInterfaces(Class<?> c)148   private static void testInterfaces(Class<?> c) {
149     System.out.println(c + " " + Arrays.toString(getImplementedInterfaces(c)));
150   }
151 
IsBootClassLoader(ClassLoader l)152   private static boolean IsBootClassLoader(ClassLoader l) {
153     // Hacky check for Android's fake boot classloader.
154     return l.getClass().getName().equals("java.lang.BootClassLoader");
155   }
156 
testClassLoader(Class<?> c)157   private static void testClassLoader(Class<?> c) {
158     Object cl = getClassLoader(c);
159     System.out.println(c + " " + (cl != null ? cl.getClass().getName() : "null"));
160     if (cl == null) {
161       if (c.getClassLoader() != null && !IsBootClassLoader(c.getClassLoader())) {
162         throw new RuntimeException("Expected " + c.getClassLoader() + ", but got null.");
163       }
164     } else {
165       if (!(cl instanceof ClassLoader)) {
166         throw new RuntimeException("Unexpected \"classloader\": " + cl + " (" + cl.getClass() +
167             ")");
168       }
169       if (cl != c.getClassLoader()) {
170         throw new RuntimeException("Unexpected classloader: " + c.getClassLoader() + " vs " + cl);
171       }
172     }
173   }
174 
testClassLoaderClasses()175   private static void testClassLoaderClasses() throws Exception {
176     System.out.println();
177     System.out.println("boot <- (B) <- (A,C)");
178     ClassLoader cl1 = DexData.create2(DexData.create1());
179     Class.forName("B", false, cl1);
180     Class.forName("A", false, cl1);
181     printClassLoaderClasses(cl1);
182 
183     System.out.println();
184     System.out.println("boot <- (B) <- (A, List)");
185     ClassLoader cl2 = DexData.create2(DexData.create1());
186     Class.forName("A", false, cl2);
187     Class.forName("java.util.List", false, cl2);
188     Class.forName("B", false, cl2.getParent());
189     printClassLoaderClasses(cl2);
190 
191     System.out.println();
192     System.out.println("boot <- 1+2 (A,B)");
193     ClassLoader cl3 = DexData.create12();
194     Class.forName("B", false, cl3);
195     Class.forName("A", false, cl3);
196     printClassLoaderClasses(cl3);
197 
198     // Check that the boot classloader dumps something non-empty.
199     ClassLoader boot = ClassLoader.getSystemClassLoader().getParent();
200     while (boot.getParent() != null) {
201       boot = boot.getParent();
202     }
203 
204     Class<?>[] bootClasses = getClassLoaderClasses(boot);
205     if (bootClasses.length == 0) {
206       throw new RuntimeException("No classes initiated by boot classloader.");
207     }
208     // Check that at least java.util.List is loaded.
209     boolean foundList = false;
210     for (Class<?> c : bootClasses) {
211       if (c == java.util.List.class) {
212         foundList = true;
213         break;
214       }
215     }
216     if (!foundList) {
217       System.out.println(Arrays.toString(bootClasses));
218       throw new RuntimeException("Could not find class java.util.List.");
219     }
220   }
221 
222   /**
223    * base64 encoded class/dex file for
224    * class Transform {
225    *   public void sayHi() {
226    *    System.out.println("Goodbye");
227    *   }
228    * }
229    */
230   private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
231     "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
232     "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
233     "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
234     "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
235     "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
236     "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
237     "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
238     "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
239     "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
240     "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
241     "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
242     "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
243     "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
testClassVersion()244   private static void testClassVersion() throws Exception {
245     Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
246     Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
247     Class target = ((ClassLoader)ctor.newInstance(
248         ByteBuffer.wrap(DEX_BYTES), Test912.class.getClassLoader())).loadClass("Transform");
249     System.out.println(Arrays.toString(getClassVersion(target)));
250   }
251 
testClassEvents()252   private static void testClassEvents() throws Exception {
253     ClassLoader cl = Main.class.getClassLoader();
254     while (cl.getParent() != null) {
255       cl = cl.getParent();
256     }
257     final ClassLoader boot = cl;
258 
259     // The JIT may deeply inline and load some classes. Preload these for test determinism.
260     final String PRELOAD_FOR_JIT[] = {
261         "java.nio.charset.CoderMalfunctionError",
262         "java.util.NoSuchElementException",
263         "java.io.FileNotFoundException",  // b/63581208
264         "java.util.zip.ZipException",     // b/63581208
265     };
266     for (String s : PRELOAD_FOR_JIT) {
267       Class.forName(s);
268     }
269 
270     Runnable r = new Runnable() {
271       @Override
272       public void run() {
273         try {
274           ClassLoader cl6 = DexData.create12();
275           System.out.println("C, true");
276           Class.forName("C", true, cl6);
277           printClassLoadMessages();
278         } catch (Exception e) {
279           throw new RuntimeException(e);
280         }
281       }
282     };
283 
284     Thread dummyThread = new Thread();
285     dummyThread.start();
286     dummyThread.join();
287 
288     enableClassLoadPreparePrintEvents(true, Thread.currentThread());
289 
290     ClassLoader cl1 = DexData.create12();
291     System.out.println("B, false");
292     Class.forName("B", false, cl1);
293     printClassLoadMessages();
294 
295     ClassLoader cl2 = DexData.create12();
296     System.out.println("B, true");
297     Class.forName("B", true, cl2);
298     printClassLoadMessages();
299 
300     ClassLoader cl3 = DexData.create12();
301     System.out.println("C, false");
302     Class.forName("C", false, cl3);
303     printClassLoadMessages();
304     System.out.println("A, false");
305     Class.forName("A", false, cl3);
306     printClassLoadMessages();
307 
308     ClassLoader cl4 = DexData.create12();
309     System.out.println("C, true");
310     Class.forName("C", true, cl4);
311     printClassLoadMessages();
312     System.out.println("A, true");
313     Class.forName("A", true, cl4);
314     printClassLoadMessages();
315 
316     ClassLoader cl5 = DexData.create12();
317     System.out.println("A, true");
318     Class.forName("A", true, cl5);
319     printClassLoadMessages();
320     System.out.println("C, true");
321     Class.forName("C", true, cl5);
322     printClassLoadMessages();
323 
324     enableClassLoadPreparePrintEvents(false, null);
325 
326     Thread t = new Thread(r, "TestRunner");
327     enableClassLoadPreparePrintEvents(true, t);
328     t.start();
329     t.join();
330     enableClassLoadPreparePrintEvents(false, null);
331 
332     enableClassLoadPreparePrintEvents(true, Thread.currentThread());
333 
334     // Check creation of arrays and proxies.
335     Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Comparable.class, I0.class });
336     Class.forName("[Lart.Test912;");
337     printClassLoadMessages();
338 
339     enableClassLoadPreparePrintEvents(false, null);
340 
341     testClassLoadPrepareEquality();
342   }
343 
testClassLoadPrepareEquality()344   private static void testClassLoadPrepareEquality() throws Exception {
345     setEqualityEventStorageClass(ClassF.class);
346 
347     enableClassLoadPrepareEqualityEvents(true);
348 
349     Class.forName("art.Test912$ClassE");
350 
351     enableClassLoadPrepareEqualityEvents(false);
352   }
353 
printClassLoaderClasses(ClassLoader cl)354   private static void printClassLoaderClasses(ClassLoader cl) {
355     for (;;) {
356       if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) {
357         break;
358       }
359 
360       Class<?> classes[] = getClassLoaderClasses(cl);
361       Arrays.sort(classes, new ClassNameComparator());
362       System.out.println(Arrays.toString(classes));
363 
364       cl = cl.getParent();
365     }
366   }
367 
printClassLoadMessages()368   private static void printClassLoadMessages() {
369     for (String s : getClassLoadMessages()) {
370       System.out.println(s);
371     }
372   }
373 
isModifiableClass(Class<?> c)374   private static native boolean isModifiableClass(Class<?> c);
getClassSignature(Class<?> c)375   private static native String[] getClassSignature(Class<?> c);
376 
isInterface(Class<?> c)377   private static native boolean isInterface(Class<?> c);
isArrayClass(Class<?> c)378   private static native boolean isArrayClass(Class<?> c);
379 
getClassModifiers(Class<?> c)380   private static native int getClassModifiers(Class<?> c);
381 
getClassFields(Class<?> c)382   private static native Object[] getClassFields(Class<?> c);
getClassMethods(Class<?> c)383   private static native Object[] getClassMethods(Class<?> c);
getImplementedInterfaces(Class<?> c)384   private static native Class<?>[] getImplementedInterfaces(Class<?> c);
385 
getClassStatus(Class<?> c)386   private static native int getClassStatus(Class<?> c);
387 
getClassLoader(Class<?> c)388   private static native Object getClassLoader(Class<?> c);
389 
getClassLoaderClasses(ClassLoader cl)390   private static native Class<?>[] getClassLoaderClasses(ClassLoader cl);
391 
getClassVersion(Class<?> c)392   private static native int[] getClassVersion(Class<?> c);
393 
enableClassLoadPreparePrintEvents(boolean b, Thread filter)394   private static native void enableClassLoadPreparePrintEvents(boolean b, Thread filter);
getClassLoadMessages()395   private static native String[] getClassLoadMessages();
396 
setEqualityEventStorageClass(Class<?> c)397   private static native void setEqualityEventStorageClass(Class<?> c);
enableClassLoadPrepareEqualityEvents(boolean b)398   private static native void enableClassLoadPrepareEqualityEvents(boolean b);
399 
runRecursiveClassPrepareEvents(Runnable forceLoad)400   private static native void runRecursiveClassPrepareEvents(Runnable forceLoad);
401 
TestRecursiveClassPrepareEvents()402   private static void TestRecursiveClassPrepareEvents() {
403     final int[] called = new int[] { 0 };
404     runRecursiveClassPrepareEvents(() -> {
405       if (called[0] == 2) {
406         return;
407       } else {
408         called[0]++;
409       }
410       try {
411         System.out.println("class-prepare event START!");
412         // Load a new class in a new class-loader.
413         Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
414         Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
415         Class<?> target = ((ClassLoader)ctor.newInstance(
416             ByteBuffer.wrap(DEX_BYTES), Test912.class.getClassLoader())).loadClass("Transform");
417         target.newInstance();
418       } catch (Exception e) { }
419       System.out.println("class-prepare event END!");
420     });
421     if (called[0] != 2) {
422       System.out.println("Failed to cause recursive Class prepare.");
423     }
424   }
425 
426   private static class TestForNonInit {
427     public static double dummy = Math.random();  // So it can't be compile-time initialized.
428   }
429 
430   @SuppressWarnings("RandomCast")
431   private static class TestForInitFail {
432     public static int dummy = ((int)Math.random())/0;  // So it throws when initializing.
433   }
434 
435   public static interface InfA {
436   }
437   public static interface InfB extends InfA {
438   }
439   public static interface InfC extends InfB {
440   }
441 
442   public abstract static class ClassA implements InfA {
443   }
444   public abstract static class ClassB extends ClassA implements InfB {
445   }
446   public abstract static class ClassC implements InfA, InfC {
447   }
448 
449   public static class ClassE {
foo()450     public void foo() {
451     }
bar()452     public void bar() {
453     }
454   }
455 
456   public static class ClassF {
457     public static Object STATIC = null;
458     public static Reference<Object> WEAK = null;
459   }
460 
461   private static class ClassNameComparator implements Comparator<Class<?>> {
compare(Class<?> c1, Class<?> c2)462     public int compare(Class<?> c1, Class<?> c2) {
463       return c1.getName().compareTo(c2.getName());
464     }
465   }
466 
467   // See run-test 910 for an explanation.
468 
469   private static Class<?> proxyClass = null;
470 
getProxyClass()471   private static Class<?> getProxyClass() throws Exception {
472     if (proxyClass != null) {
473       return proxyClass;
474     }
475 
476     for (int i = 1; i <= 21; i++) {
477       proxyClass = createProxyClass(i);
478       String name = proxyClass.getName();
479       if (name.equals("$Proxy20")) {
480         return proxyClass;
481       }
482     }
483     return proxyClass;
484   }
485 
createProxyClass(int i)486   private static Class<?> createProxyClass(int i) throws Exception {
487     int count = Integer.bitCount(i);
488     Class<?>[] input = new Class<?>[count + 1];
489     input[0] = Runnable.class;
490     int inputIndex = 1;
491     int bitIndex = 0;
492     while (i != 0) {
493         if ((i & 1) != 0) {
494             input[inputIndex++] = Class.forName("art.Test912$I" + bitIndex);
495         }
496         i >>>= 1;
497         bitIndex++;
498     }
499     return Proxy.getProxyClass(Test912.class.getClassLoader(), input);
500   }
501 
502   // Need this for the proxy naming.
503   public static interface I0 {
504   }
505   public static interface I1 {
506   }
507   public static interface I2 {
508   }
509   public static interface I3 {
510   }
511   public static interface I4 {
512   }
513 }
514