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 class_name:FirstSeenByMyClassLoader
86   /// CHECK-NEXT: ClinitCheck
87   /// CHECK-NEXT: InvokeStaticOrDirect
88   /// CHECK-NEXT: LoadClass class_name:java.lang.System
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 class_name:FirstSeenByMyClassLoader
97   /// CHECK-NEXT: ClinitCheck
98                 /* We inlined FirstSeenByMyClassLoader.$inline$bar */
99   /// CHECK-NEXT: LoadClass class_name:java.lang.System
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 class_name:FirstSeenByMyClassLoader gen_clinit_check:true
109                 /* Load and initialize System */
110   // There may be HX86ComputeBaseMethodAddress here.
111   /// CHECK:      LoadClass class_name:java.lang.System
112   // The ClinitCheck may (PIC) or may not (non-PIC) be merged into the LoadClass.
113   // (The merging checks for environment match but HLoadClass/kBootImageAddress
114   // used for non-PIC mode does not have an environment at all.)
115   /// CHECK:      StaticFieldGet
116   // There may be HX86ComputeBaseMethodAddress here.
117   /// CHECK:      LoadString
118   /// CHECK-NEXT: NullCheck
119   /// CHECK-NEXT: InvokeVirtual
bar()120   public static void bar() {
121     FirstSeenByMyClassLoader.$inline$bar();
122     System.out.println("In between the two calls.");
123     FirstSeenByMyClassLoader.$noinline$bar();
124   }
125 }
126 
127 public class Main {
main(String[] args)128   public static void main(String[] args) throws Exception {
129     MyClassLoader o = new MyClassLoader();
130     Class<?> foo = o.loadClass("LoadedByMyClassLoader");
131     Method m = foo.getDeclaredMethod("bar");
132     m.invoke(null);
133   }
134 }
135