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.util.*;
20 import java.util.function.*;
21 import java.lang.reflect.*;
22 import java.nio.ByteBuffer;
23 import dalvik.system.InMemoryDexClassLoader;
24 
25 public class Test1946 {
26   // Base64 encoded dex file containing the following classes. Note the class E cannot be loaded.
27   // public class A {}
28   // public class B {}
29   // public class C {}
30   // public class D {}
31   // public class E extends ClassNotThere {}
32   private static final byte[] TEST_CLASSES = Base64.getDecoder().decode(
33       "ZGV4CjAzNQDzTO8rVDlKlz80vQF4NLYV5MjMMjHlOtRoAwAAcAAAAHhWNBIAAAAAAAAAAOACAAAO" +
34       "AAAAcAAAAAgAAACoAAAAAQAAAMgAAAAAAAAAAAAAAAcAAADUAAAABQAAAAwBAAC8AQAArAEAACQC" +
35       "AAAsAgAANAIAADwCAABEAgAATAIAAFQCAABZAgAAXgIAAGMCAAB0AgAAeQIAAH4CAACSAgAABgAA" +
36       "AAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAANAAAABwAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA" +
37       "AgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAB" +
38       "AAAAAAAAAK4CAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAIAAAAAAAAAuAIAAAAAAAACAAAAAAAAAAYA" +
39       "AAAAAAAAAwAAAAAAAADCAgAAAAAAAAQAAAAAAAAABgAAAAAAAAAEAAAAAAAAAMwCAAAAAAAABQAA" +
40       "AAAAAAADAAAAAAAAAAUAAAAAAAAA1gIAAAAAAAABAAEAAQAAAJUCAAAEAAAAcBAGAAAADgABAAEA" +
41       "AQAAAJoCAAAEAAAAcBAGAAAADgABAAEAAQAAAJ8CAAAEAAAAcBAGAAAADgABAAEAAQAAAKQCAAAE" +
42       "AAAAcBAGAAAADgABAAEAAQAAAKkCAAAEAAAAcBADAAAADgAGPGluaXQ+AAZBLmphdmEABkIuamF2" +
43       "YQAGQy5qYXZhAAZELmphdmEABkUuamF2YQADTEE7AANMQjsAA0xDOwAPTENsYXNzTm90VGhlcmU7" +
44       "AANMRDsAA0xFOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAEABw4AAQAHDgABAAcOAAEABw4AAQAH" +
45       "DgAAAAEAAICABKwDAAABAAGAgATEAwAAAQACgIAE3AMAAAEABICABPQDAAABAAWAgASMBAsAAAAA" +
46       "AAAAAQAAAAAAAAABAAAADgAAAHAAAAACAAAACAAAAKgAAAADAAAAAQAAAMgAAAAFAAAABwAAANQA" +
47       "AAAGAAAABQAAAAwBAAABIAAABQAAAKwBAAACIAAADgAAACQCAAADIAAABQAAAJUCAAAAIAAABQAA" +
48       "AK4CAAAAEAAAAQAAAOACAAA=");
49   public class TMP1 {}
50   public class TMP2 {}
51   public class TMP3 extends ArrayList {}
52 
check(boolean b, Supplier<String> msg)53   private static void check(boolean b, Supplier<String> msg) {
54     if (!b) {
55       throw new Error("Test failed! " + msg.get());
56     }
57   }
58 
checkEq(T[] full, T[] sub, final String msg)59   private static <T> void checkEq(T[] full, T[] sub, final String msg) {
60     List<T> f = Arrays.asList(full);
61     check(full.length == sub.length, () -> "not equal length");
62     Supplier<String> msgGen =
63       () -> Arrays.toString(full) + " is not same as " + Arrays.toString(sub) + ": " + msg;
64     check(new HashSet<T>(Arrays.asList(full)).containsAll(Arrays.asList(sub)), msgGen);
65   }
66 
checkSubset(T[] full, T[] sub, final String msg)67   private static <T> void checkSubset(T[] full, T[] sub, final String msg) {
68     Supplier<String> msgGen =
69       () -> Arrays.toString(full) + " does not contain all of " + Arrays.toString(sub) + ": " + msg;
70     check(new HashSet<T>(Arrays.asList(full)).containsAll(Arrays.asList(sub)), msgGen);
71   }
72 
run()73   public static void run() throws Exception {
74     initializeTest();
75     // Check a few random classes in BCP.
76     checkSubset(getClassloaderDescriptors(null),
77         new String[] { "Ljava/lang/String;", "Ljava/util/TreeSet;" },
78         "Missing entries for null classloader.");
79     // Make sure that null is the same as BootClassLoader
80     checkEq(getClassloaderDescriptors(null),
81         getClassloaderDescriptors(Object.class.getClassLoader()), "Object not in bcp!");
82     // Check the current class loader gets expected classes.
83     checkSubset(getClassloaderDescriptors(Test1946.class.getClassLoader()),
84         new String[] {
85           "Lart/Test1946;",
86           "Lart/Test1946$TMP1;",
87           "Lart/Test1946$TMP2;",
88           "Lart/Test1946$TMP3;"
89         },
90         "Missing entries for current class classloader.");
91     // Check that the result is exactly what we expect and includes classes that fail verification.
92     checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES,
93             ClassLoader.getSystemClassLoader())),
94         new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
95         "Unexpected classes in custom classloader");
96     checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES,
97             Object.class.getClassLoader())),
98         new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
99         "Unexpected classes in custom classloader");
100     checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES,
101             Test1946.class.getClassLoader())),
102         new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
103         "Unexpected classes in custom classloader");
104     // Check we only get 1 copy of each descriptor.
105     checkEq(getClassloaderDescriptors(makeClassLoaderFrom(Arrays.asList(TEST_CLASSES, TEST_CLASSES),
106             Test1946.class.getClassLoader())),
107         new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
108         "Unexpected classes in custom classloader");
109     System.out.println("Passed!");
110   }
111 
makeClassLoaderFrom(byte[] data, ClassLoader parent)112   private static ClassLoader makeClassLoaderFrom(byte[] data, ClassLoader parent) throws Exception {
113     return new InMemoryDexClassLoader(ByteBuffer.wrap(data), parent);
114   }
115 
makeClassLoaderFrom(List<byte[]> data, ClassLoader parent)116   private static ClassLoader makeClassLoaderFrom(List<byte[]> data, ClassLoader parent)
117       throws Exception {
118     ArrayList<ByteBuffer> bufs = new ArrayList<>();
119     for (byte[] d : data) {
120       bufs.add(ByteBuffer.wrap(d));
121     }
122     return new InMemoryDexClassLoader(bufs.toArray(new ByteBuffer[0]), parent);
123   }
124 
initializeTest()125   private static native void initializeTest();
getClassloaderDescriptors(ClassLoader loader)126   private static native String[] getClassloaderDescriptors(ClassLoader loader);
127 }
128