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 
testClass(String className)110   private static void testClass(String className) throws Exception {
111     Class<?> base = Class.forName(className);
112     testClass(base);
113   }
114 
testClass(Class<?> base)115   private static void testClass(Class<?> base) throws Exception {
116     String[] result = getClassSignature(base);
117     System.out.println(Arrays.toString(result));
118     int mod = getClassModifiers(base);
119     if (mod != base.getModifiers()) {
120       throw new RuntimeException("Unexpected modifiers: " + base.getModifiers() + " vs " + mod);
121     }
122     System.out.println(Integer.toHexString(mod));
123   }
124 
testClassType(Class<?> c)125   private static void testClassType(Class<?> c) throws Exception {
126     boolean isInterface = isInterface(c);
127     boolean isArray = isArrayClass(c);
128     boolean isModifiable = isModifiableClass(c);
129     System.out.println(c.getName() + " interface=" + isInterface + " array=" + isArray +
130         " modifiable=" + isModifiable);
131   }
132 
testClassFields(Class<?> c)133   private static void testClassFields(Class<?> c) throws Exception {
134     System.out.println(Arrays.toString(getClassFields(c)));
135   }
136 
testClassMethods(Class<?> c)137   private static void testClassMethods(Class<?> c) throws Exception {
138     System.out.println(Arrays.toString(getClassMethods(c)));
139   }
140 
testClassStatus(Class<?> c)141   private static void testClassStatus(Class<?> c) {
142     System.out.println(c + " " + Integer.toBinaryString(getClassStatus(c)));
143   }
144 
testInterfaces(Class<?> c)145   private static void testInterfaces(Class<?> c) {
146     System.out.println(c + " " + Arrays.toString(getImplementedInterfaces(c)));
147   }
148 
IsBootClassLoader(ClassLoader l)149   private static boolean IsBootClassLoader(ClassLoader l) {
150     // Hacky check for Android's fake boot classloader.
151     return l.getClass().getName().equals("java.lang.BootClassLoader");
152   }
153 
testClassLoader(Class<?> c)154   private static void testClassLoader(Class<?> c) {
155     Object cl = getClassLoader(c);
156     System.out.println(c + " " + (cl != null ? cl.getClass().getName() : "null"));
157     if (cl == null) {
158       if (c.getClassLoader() != null && !IsBootClassLoader(c.getClassLoader())) {
159         throw new RuntimeException("Expected " + c.getClassLoader() + ", but got null.");
160       }
161     } else {
162       if (!(cl instanceof ClassLoader)) {
163         throw new RuntimeException("Unexpected \"classloader\": " + cl + " (" + cl.getClass() +
164             ")");
165       }
166       if (cl != c.getClassLoader()) {
167         throw new RuntimeException("Unexpected classloader: " + c.getClassLoader() + " vs " + cl);
168       }
169     }
170   }
171 
testClassLoaderClasses()172   private static void testClassLoaderClasses() throws Exception {
173     System.out.println();
174     System.out.println("boot <- (B) <- (A,C)");
175     ClassLoader cl1 = DexData.create2(DexData.create1());
176     Class.forName("B", false, cl1);
177     Class.forName("A", false, cl1);
178     printClassLoaderClasses(cl1);
179 
180     System.out.println();
181     System.out.println("boot <- (B) <- (A, List)");
182     ClassLoader cl2 = DexData.create2(DexData.create1());
183     Class.forName("A", false, cl2);
184     Class.forName("java.util.List", false, cl2);
185     Class.forName("B", false, cl2.getParent());
186     printClassLoaderClasses(cl2);
187 
188     System.out.println();
189     System.out.println("boot <- 1+2 (A,B)");
190     ClassLoader cl3 = DexData.create12();
191     Class.forName("B", false, cl3);
192     Class.forName("A", false, cl3);
193     printClassLoaderClasses(cl3);
194 
195     // Check that the boot classloader dumps something non-empty.
196     ClassLoader boot = ClassLoader.getSystemClassLoader().getParent();
197     while (boot.getParent() != null) {
198       boot = boot.getParent();
199     }
200 
201     Class<?>[] bootClasses = getClassLoaderClasses(boot);
202     if (bootClasses.length == 0) {
203       throw new RuntimeException("No classes initiated by boot classloader.");
204     }
205     // Check that at least java.util.List is loaded.
206     boolean foundList = false;
207     for (Class<?> c : bootClasses) {
208       if (c == java.util.List.class) {
209         foundList = true;
210         break;
211       }
212     }
213     if (!foundList) {
214       System.out.println(Arrays.toString(bootClasses));
215       throw new RuntimeException("Could not find class java.util.List.");
216     }
217   }
218 
219   /**
220    * base64 encoded class/dex file for
221    * class Transform {
222    *   public void sayHi() {
223    *    System.out.println("Goodbye");
224    *   }
225    * }
226    */
227   private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
228     "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
229     "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
230     "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
231     "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
232     "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
233     "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
234     "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
235     "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
236     "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
237     "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
238     "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
239     "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
240     "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
testClassVersion()241   private static void testClassVersion() throws Exception {
242     Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
243     Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
244     Class target = ((ClassLoader)ctor.newInstance(
245         ByteBuffer.wrap(DEX_BYTES), Test912.class.getClassLoader())).loadClass("Transform");
246     System.out.println(Arrays.toString(getClassVersion(target)));
247   }
248 
testClassEvents()249   private static void testClassEvents() throws Exception {
250     ClassLoader cl = Main.class.getClassLoader();
251     while (cl.getParent() != null) {
252       cl = cl.getParent();
253     }
254     final ClassLoader boot = cl;
255 
256     // The JIT may deeply inline and load some classes. Preload these for test determinism.
257     final String PRELOAD_FOR_JIT[] = {
258         "java.nio.charset.CoderMalfunctionError",
259         "java.util.NoSuchElementException",
260         "java.io.FileNotFoundException",  // b/63581208
261         "java.util.zip.ZipException",     // b/63581208
262     };
263     for (String s : PRELOAD_FOR_JIT) {
264       Class.forName(s);
265     }
266 
267     Runnable r = new Runnable() {
268       @Override
269       public void run() {
270         try {
271           ClassLoader cl6 = DexData.create12();
272           System.out.println("C, true");
273           Class.forName("C", true, cl6);
274           printClassLoadMessages();
275         } catch (Exception e) {
276           throw new RuntimeException(e);
277         }
278       }
279     };
280 
281     Thread dummyThread = new Thread();
282     dummyThread.start();
283     dummyThread.join();
284 
285     enableClassLoadPreparePrintEvents(true, Thread.currentThread());
286 
287     ClassLoader cl1 = DexData.create12();
288     System.out.println("B, false");
289     Class.forName("B", false, cl1);
290     printClassLoadMessages();
291 
292     ClassLoader cl2 = DexData.create12();
293     System.out.println("B, true");
294     Class.forName("B", true, cl2);
295     printClassLoadMessages();
296 
297     ClassLoader cl3 = DexData.create12();
298     System.out.println("C, false");
299     Class.forName("C", false, cl3);
300     printClassLoadMessages();
301     System.out.println("A, false");
302     Class.forName("A", false, cl3);
303     printClassLoadMessages();
304 
305     ClassLoader cl4 = DexData.create12();
306     System.out.println("C, true");
307     Class.forName("C", true, cl4);
308     printClassLoadMessages();
309     System.out.println("A, true");
310     Class.forName("A", true, cl4);
311     printClassLoadMessages();
312 
313     ClassLoader cl5 = DexData.create12();
314     System.out.println("A, true");
315     Class.forName("A", true, cl5);
316     printClassLoadMessages();
317     System.out.println("C, true");
318     Class.forName("C", true, cl5);
319     printClassLoadMessages();
320 
321     enableClassLoadPreparePrintEvents(false, null);
322 
323     Thread t = new Thread(r, "TestRunner");
324     enableClassLoadPreparePrintEvents(true, t);
325     t.start();
326     t.join();
327     enableClassLoadPreparePrintEvents(false, null);
328 
329     enableClassLoadPreparePrintEvents(true, Thread.currentThread());
330 
331     // Check creation of arrays and proxies.
332     Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Comparable.class, I0.class });
333     Class.forName("[Lart.Test912;");
334     printClassLoadMessages();
335 
336     enableClassLoadPreparePrintEvents(false, null);
337 
338     testClassLoadPrepareEquality();
339   }
340 
testClassLoadPrepareEquality()341   private static void testClassLoadPrepareEquality() throws Exception {
342     setEqualityEventStorageClass(ClassF.class);
343 
344     enableClassLoadPrepareEqualityEvents(true);
345 
346     Class.forName("art.Test912$ClassE");
347 
348     enableClassLoadPrepareEqualityEvents(false);
349   }
350 
printClassLoaderClasses(ClassLoader cl)351   private static void printClassLoaderClasses(ClassLoader cl) {
352     for (;;) {
353       if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) {
354         break;
355       }
356 
357       Class<?> classes[] = getClassLoaderClasses(cl);
358       Arrays.sort(classes, new ClassNameComparator());
359       System.out.println(Arrays.toString(classes));
360 
361       cl = cl.getParent();
362     }
363   }
364 
printClassLoadMessages()365   private static void printClassLoadMessages() {
366     for (String s : getClassLoadMessages()) {
367       System.out.println(s);
368     }
369   }
370 
isModifiableClass(Class<?> c)371   private static native boolean isModifiableClass(Class<?> c);
getClassSignature(Class<?> c)372   private static native String[] getClassSignature(Class<?> c);
373 
isInterface(Class<?> c)374   private static native boolean isInterface(Class<?> c);
isArrayClass(Class<?> c)375   private static native boolean isArrayClass(Class<?> c);
376 
getClassModifiers(Class<?> c)377   private static native int getClassModifiers(Class<?> c);
378 
getClassFields(Class<?> c)379   private static native Object[] getClassFields(Class<?> c);
getClassMethods(Class<?> c)380   private static native Object[] getClassMethods(Class<?> c);
getImplementedInterfaces(Class<?> c)381   private static native Class<?>[] getImplementedInterfaces(Class<?> c);
382 
getClassStatus(Class<?> c)383   private static native int getClassStatus(Class<?> c);
384 
getClassLoader(Class<?> c)385   private static native Object getClassLoader(Class<?> c);
386 
getClassLoaderClasses(ClassLoader cl)387   private static native Class<?>[] getClassLoaderClasses(ClassLoader cl);
388 
getClassVersion(Class<?> c)389   private static native int[] getClassVersion(Class<?> c);
390 
enableClassLoadPreparePrintEvents(boolean b, Thread filter)391   private static native void enableClassLoadPreparePrintEvents(boolean b, Thread filter);
getClassLoadMessages()392   private static native String[] getClassLoadMessages();
393 
setEqualityEventStorageClass(Class<?> c)394   private static native void setEqualityEventStorageClass(Class<?> c);
enableClassLoadPrepareEqualityEvents(boolean b)395   private static native void enableClassLoadPrepareEqualityEvents(boolean b);
396 
397   private static class TestForNonInit {
398     public static double dummy = Math.random();  // So it can't be compile-time initialized.
399   }
400 
401   @SuppressWarnings("RandomCast")
402   private static class TestForInitFail {
403     public static int dummy = ((int)Math.random())/0;  // So it throws when initializing.
404   }
405 
406   public static interface InfA {
407   }
408   public static interface InfB extends InfA {
409   }
410   public static interface InfC extends InfB {
411   }
412 
413   public abstract static class ClassA implements InfA {
414   }
415   public abstract static class ClassB extends ClassA implements InfB {
416   }
417   public abstract static class ClassC implements InfA, InfC {
418   }
419 
420   public static class ClassE {
foo()421     public void foo() {
422     }
bar()423     public void bar() {
424     }
425   }
426 
427   public static class ClassF {
428     public static Object STATIC = null;
429     public static Reference<Object> WEAK = null;
430   }
431 
432   private static class ClassNameComparator implements Comparator<Class<?>> {
compare(Class<?> c1, Class<?> c2)433     public int compare(Class<?> c1, Class<?> c2) {
434       return c1.getName().compareTo(c2.getName());
435     }
436   }
437 
438   // See run-test 910 for an explanation.
439 
440   private static Class<?> proxyClass = null;
441 
getProxyClass()442   private static Class<?> getProxyClass() throws Exception {
443     if (proxyClass != null) {
444       return proxyClass;
445     }
446 
447     for (int i = 1; i <= 21; i++) {
448       proxyClass = createProxyClass(i);
449       String name = proxyClass.getName();
450       if (name.equals("$Proxy20")) {
451         return proxyClass;
452       }
453     }
454     return proxyClass;
455   }
456 
createProxyClass(int i)457   private static Class<?> createProxyClass(int i) throws Exception {
458     int count = Integer.bitCount(i);
459     Class<?>[] input = new Class<?>[count + 1];
460     input[0] = Runnable.class;
461     int inputIndex = 1;
462     int bitIndex = 0;
463     while (i != 0) {
464         if ((i & 1) != 0) {
465             input[inputIndex++] = Class.forName("art.Test912$I" + bitIndex);
466         }
467         i >>>= 1;
468         bitIndex++;
469     }
470     return Proxy.getProxyClass(Test912.class.getClassLoader(), input);
471   }
472 
473   // Need this for the proxy naming.
474   public static interface I0 {
475   }
476   public static interface I1 {
477   }
478   public static interface I2 {
479   }
480   public static interface I3 {
481   }
482   public static interface I4 {
483   }
484 }
485