1 /*
2  * Copyright (C) 2015 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 import java.lang.reflect.Field;
18 import java.lang.reflect.Method;
19 import java.util.ArrayList;
20 import java.util.List;
21 
22 class MyClassLoader extends ClassLoader {
MyClassLoader()23   MyClassLoader() throws Exception {
24     super(MyClassLoader.class.getClassLoader());
25 
26     // Some magic to get access to the pathList field of BaseDexClassLoader.
27     ClassLoader loader = getClass().getClassLoader();
28     Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
29     Field f = baseDexClassLoader.getDeclaredField("pathList");
30     f.setAccessible(true);
31     Object pathList = f.get(loader);
32 
33     // Some magic to get access to the dexField field of pathList.
34     // Need to make a copy of the dex elements since we don't want an app image with pre-resolved
35     // things.
36     f = pathList.getClass().getDeclaredField("dexElements");
37     f.setAccessible(true);
38     Object[] dexElements = (Object[]) f.get(pathList);
39     f = dexElements[0].getClass().getDeclaredField("dexFile");
40     f.setAccessible(true);
41     for (Object element : dexElements) {
42       Object dexFile = f.get(element);
43       // Make copy.
44       Field fileNameField = dexFile.getClass().getDeclaredField("mFileName");
45       fileNameField.setAccessible(true);
46       dexFiles.add(dexFile.getClass().getDeclaredConstructor(String.class).newInstance(
47         fileNameField.get(dexFile)));
48     }
49   }
50 
51   ArrayList<Object> dexFiles = new ArrayList<Object>();
52   Field dexFileField;
53 
loadClass(String className, boolean resolve)54   protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
55     // Other classes may also get loaded, ignore those.
56     if (className.equals("LoadedByMyClassLoader") || className.equals("FirstSeenByMyClassLoader")) {
57       System.out.println("Request for " + className);
58     }
59 
60     // We're only going to handle LoadedByMyClassLoader.
61     if (className != "LoadedByMyClassLoader") {
62       return getParent().loadClass(className);
63     }
64 
65     // Mimic what DexPathList.findClass is doing.
66     try {
67       for (Object dexFile : dexFiles) {
68         Method method = dexFile.getClass().getDeclaredMethod(
69             "loadClassBinaryName", String.class, ClassLoader.class, List.class);
70 
71         if (dexFile != null) {
72           Class clazz = (Class)method.invoke(dexFile, className, this, null);
73           if (clazz != null) {
74             return clazz;
75           }
76         }
77       }
78     } catch (Exception e) { /* Ignore */ }
79     return null;
80   }
81 }
82 
83 class LoadedByMyClassLoader {
84   /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (before)
85   /// CHECK:      LoadClass
86   /// CHECK-NEXT: ClinitCheck
87   /// CHECK-NEXT: InvokeStaticOrDirect
88   /// CHECK-NEXT: LoadClass
89   /// CHECK-NEXT: ClinitCheck
90   /// CHECK-NEXT: StaticFieldGet
91   /// CHECK-NEXT: LoadString
92   /// CHECK-NEXT: NullCheck
93   /// CHECK-NEXT: InvokeVirtual
94 
95   /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after)
96   /// CHECK:      LoadClass
97   /// CHECK-NEXT: ClinitCheck
98                 /* We inlined FirstSeenByMyClassLoader.$inline$bar */
99   /// CHECK-NEXT: LoadClass
100   /// CHECK-NEXT: ClinitCheck
101   /// CHECK-NEXT: StaticFieldGet
102   /// CHECK-NEXT: LoadString
103   /// CHECK-NEXT: NullCheck
104   /// CHECK-NEXT: InvokeVirtual
105 
106   /// CHECK-START: void LoadedByMyClassLoader.bar() register (before)
107                 /* Load and initialize FirstSeenByMyClassLoader */
108   /// CHECK:      LoadClass gen_clinit_check:true
109                 /* Load and initialize System */
110   /// CHECK-NEXT: LoadClass gen_clinit_check:true
111   /// CHECK-NEXT: StaticFieldGet
112   // There may be HArmDexCacheArraysBase or HX86ComputeBaseMethodAddress here.
113   /// CHECK:      LoadString
114   /// CHECK-NEXT: NullCheck
115   /// CHECK-NEXT: InvokeVirtual
bar()116   public static void bar() {
117     FirstSeenByMyClassLoader.$inline$bar();
118     System.out.println("In between the two calls.");
119     FirstSeenByMyClassLoader.$noinline$bar();
120   }
121 }
122 
123 public class Main {
main(String[] args)124   public static void main(String[] args) throws Exception {
125     MyClassLoader o = new MyClassLoader();
126     Class foo = o.loadClass("LoadedByMyClassLoader");
127     Method m = foo.getDeclaredMethod("bar");
128     m.invoke(null);
129   }
130 }
131