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
35         // with pre-resolved 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")
57                 || className.equals("FirstSeenByMyClassLoader")) {
58             System.out.println("Request for " + className);
59         }
60 
61         // We're only going to handle LoadedByMyClassLoader.
62         if (className != "LoadedByMyClassLoader") {
63             return getParent().loadClass(className);
64         }
65 
66         // Mimic what DexPathList.findClass is doing.
67         try {
68             for (Object dexFile : dexFiles) {
69                 Method method = dexFile.getClass().getDeclaredMethod(
70                         "loadClassBinaryName", String.class, ClassLoader.class, List.class);
71 
72                 if (dexFile != null) {
73                     Class<?> clazz = (Class<?>) method.invoke(dexFile, className, this, null);
74                     if (clazz != null) {
75                         return clazz;
76                     }
77                 }
78             }
79         } catch (Exception e) { /* Ignore */ }
80         return null;
81     }
82 }
83 
84 class LoadedByMyClassLoader {
85     /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (before)
86     /// CHECK:      LoadClass class_name:FirstSeenByMyClassLoader
87     /// CHECK-NEXT: ClinitCheck
88     /// CHECK-NEXT: InvokeStaticOrDirect
89     /// CHECK-NEXT: LoadClass class_name:java.lang.System
90     /// CHECK-NEXT: ClinitCheck
91     /// CHECK-NEXT: StaticFieldGet
92     /// CHECK-NEXT: LoadString
93     /// CHECK-NEXT: NullCheck
94     /// CHECK-NEXT: InvokeVirtual
95 
96     /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after)
97     /// CHECK:      LoadClass class_name:FirstSeenByMyClassLoader
98     /// CHECK-NEXT: ClinitCheck
99                   /* We inlined FirstSeenByMyClassLoader.$inline$bar */
100     /// CHECK-NEXT: LoadClass class_name:java.lang.System
101     /// CHECK-NEXT: ClinitCheck
102     /// CHECK-NEXT: StaticFieldGet
103     /// CHECK-NEXT: LoadString
104     /// CHECK-NEXT: NullCheck
105     /// CHECK-NEXT: InvokeVirtual
106 
107     /// CHECK-START: void LoadedByMyClassLoader.bar() register (before)
108                    /* Load and initialize FirstSeenByMyClassLoader */
109     /// CHECK:      LoadClass class_name:FirstSeenByMyClassLoader gen_clinit_check:true
110                    /* Load and initialize System */
111     // There may be HX86ComputeBaseMethodAddress here.
112     /// CHECK:      LoadClass class_name:java.lang.System
113     // The ClinitCheck may (PIC) or may not (non-PIC) be merged into the LoadClass.
114     // (The merging checks for environment match but HLoadClass/kBootImageAddress
115     // used for non-PIC mode does not have an environment at all.)
116     /// CHECK:      StaticFieldGet
117     // There may be HX86ComputeBaseMethodAddress here.
118     /// CHECK:      LoadString
119     /// CHECK-NEXT: NullCheck
120     /// CHECK-NEXT: InvokeVirtual
bar()121     public static void bar() {
122         FirstSeenByMyClassLoader.$inline$bar();
123         System.out.println("In between the two calls.");
124         FirstSeenByMyClassLoader.$noinline$bar();
125     }
126 }
127 
128 public class Main {
main(String[] args)129     public static void main(String[] args) throws Exception {
130         MyClassLoader o = new MyClassLoader();
131         Class<?> foo = o.loadClass("LoadedByMyClassLoader");
132         Method m = foo.getDeclaredMethod("bar");
133         m.invoke(null);
134     }
135 }
136