xref: /aosp_15_r20/art/test/497-inlining-and-class-loader/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.Field;
18*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.Method;
19*795d594fSAndroid Build Coastguard Worker import java.util.List;
20*795d594fSAndroid Build Coastguard Worker 
21*795d594fSAndroid Build Coastguard Worker class MyClassLoader extends ClassLoader {
MyClassLoader()22*795d594fSAndroid Build Coastguard Worker     MyClassLoader() throws Exception {
23*795d594fSAndroid Build Coastguard Worker         super(MyClassLoader.class.getClassLoader());
24*795d594fSAndroid Build Coastguard Worker 
25*795d594fSAndroid Build Coastguard Worker         // Some magic to get access to the pathList field of BaseDexClassLoader.
26*795d594fSAndroid Build Coastguard Worker         ClassLoader loader = getClass().getClassLoader();
27*795d594fSAndroid Build Coastguard Worker         Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
28*795d594fSAndroid Build Coastguard Worker         Field f = baseDexClassLoader.getDeclaredField("pathList");
29*795d594fSAndroid Build Coastguard Worker         f.setAccessible(true);
30*795d594fSAndroid Build Coastguard Worker         Object pathList = f.get(loader);
31*795d594fSAndroid Build Coastguard Worker 
32*795d594fSAndroid Build Coastguard Worker         // Some magic to get access to the dexField field of pathList.
33*795d594fSAndroid Build Coastguard Worker         f = pathList.getClass().getDeclaredField("dexElements");
34*795d594fSAndroid Build Coastguard Worker         f.setAccessible(true);
35*795d594fSAndroid Build Coastguard Worker         dexElements = (Object[]) f.get(pathList);
36*795d594fSAndroid Build Coastguard Worker         dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
37*795d594fSAndroid Build Coastguard Worker         dexFileField.setAccessible(true);
38*795d594fSAndroid Build Coastguard Worker     }
39*795d594fSAndroid Build Coastguard Worker 
40*795d594fSAndroid Build Coastguard Worker     Object[] dexElements;
41*795d594fSAndroid Build Coastguard Worker     Field dexFileField;
42*795d594fSAndroid Build Coastguard Worker 
43*795d594fSAndroid Build Coastguard Worker     static ClassLoader level1ClassLoader;
44*795d594fSAndroid Build Coastguard Worker 
loadClass(String className, boolean resolve)45*795d594fSAndroid Build Coastguard Worker     protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
46*795d594fSAndroid Build Coastguard Worker         if (this != level1ClassLoader) {
47*795d594fSAndroid Build Coastguard Worker             if (className.equals("Level1")) {
48*795d594fSAndroid Build Coastguard Worker                 return level1ClassLoader.loadClass(className);
49*795d594fSAndroid Build Coastguard Worker             } else if (className.equals("Level2")) {
50*795d594fSAndroid Build Coastguard Worker                 throw new ClassNotFoundException("None of my methods require Level2!");
51*795d594fSAndroid Build Coastguard Worker             } else if (!className.equals("LoadedByMyClassLoader")) {
52*795d594fSAndroid Build Coastguard Worker                 // We're only going to handle LoadedByMyClassLoader.
53*795d594fSAndroid Build Coastguard Worker                 return getParent().loadClass(className);
54*795d594fSAndroid Build Coastguard Worker             }
55*795d594fSAndroid Build Coastguard Worker         } else {
56*795d594fSAndroid Build Coastguard Worker             if (className != "Level1" && className != "Level2") {
57*795d594fSAndroid Build Coastguard Worker                 return getParent().loadClass(className);
58*795d594fSAndroid Build Coastguard Worker             }
59*795d594fSAndroid Build Coastguard Worker         }
60*795d594fSAndroid Build Coastguard Worker 
61*795d594fSAndroid Build Coastguard Worker         // Mimic what DexPathList.findClass is doing.
62*795d594fSAndroid Build Coastguard Worker         try {
63*795d594fSAndroid Build Coastguard Worker             for (Object element : dexElements) {
64*795d594fSAndroid Build Coastguard Worker                 Object dex = dexFileField.get(element);
65*795d594fSAndroid Build Coastguard Worker                 Method method = dex.getClass().getDeclaredMethod(
66*795d594fSAndroid Build Coastguard Worker                         "loadClassBinaryName", String.class, ClassLoader.class, List.class);
67*795d594fSAndroid Build Coastguard Worker 
68*795d594fSAndroid Build Coastguard Worker                 if (dex != null) {
69*795d594fSAndroid Build Coastguard Worker                     Class<?> clazz = (Class<?>) method.invoke(dex, className, this, null);
70*795d594fSAndroid Build Coastguard Worker                     if (clazz != null) {
71*795d594fSAndroid Build Coastguard Worker                         return clazz;
72*795d594fSAndroid Build Coastguard Worker                     }
73*795d594fSAndroid Build Coastguard Worker                 }
74*795d594fSAndroid Build Coastguard Worker             }
75*795d594fSAndroid Build Coastguard Worker         } catch (Exception e) { /* Ignore */ }
76*795d594fSAndroid Build Coastguard Worker         return null;
77*795d594fSAndroid Build Coastguard Worker     }
78*795d594fSAndroid Build Coastguard Worker }
79*795d594fSAndroid Build Coastguard Worker 
80*795d594fSAndroid Build Coastguard Worker class LoadedByMyClassLoader {
bar()81*795d594fSAndroid Build Coastguard Worker     public static void bar() {
82*795d594fSAndroid Build Coastguard Worker         Level1.$inline$bar();
83*795d594fSAndroid Build Coastguard Worker     }
84*795d594fSAndroid Build Coastguard Worker }
85*795d594fSAndroid Build Coastguard Worker 
86*795d594fSAndroid Build Coastguard Worker class Main {
main(String[] args)87*795d594fSAndroid Build Coastguard Worker     public static void main(String[] args) throws Exception {
88*795d594fSAndroid Build Coastguard Worker         System.loadLibrary(args[0]);
89*795d594fSAndroid Build Coastguard Worker         // Clone resolved methods, to restore the original version just
90*795d594fSAndroid Build Coastguard Worker         // before we walk the stack in $noinline$bar.
91*795d594fSAndroid Build Coastguard Worker         savedResolvedMethods = cloneResolvedMethods(Main.class);
92*795d594fSAndroid Build Coastguard Worker 
93*795d594fSAndroid Build Coastguard Worker         MyClassLoader o = new MyClassLoader();
94*795d594fSAndroid Build Coastguard Worker         MyClassLoader.level1ClassLoader = new MyClassLoader();
95*795d594fSAndroid Build Coastguard Worker         Class<?> foo = o.loadClass("LoadedByMyClassLoader");
96*795d594fSAndroid Build Coastguard Worker         Method m = foo.getDeclaredMethod("bar");
97*795d594fSAndroid Build Coastguard Worker         try {
98*795d594fSAndroid Build Coastguard Worker             m.invoke(null);
99*795d594fSAndroid Build Coastguard Worker         } catch (Error e) { /* Ignore */ }
100*795d594fSAndroid Build Coastguard Worker     }
101*795d594fSAndroid Build Coastguard Worker 
$inline$bar()102*795d594fSAndroid Build Coastguard Worker     public static void $inline$bar() {
103*795d594fSAndroid Build Coastguard Worker     }
104*795d594fSAndroid Build Coastguard Worker 
$noinline$bar()105*795d594fSAndroid Build Coastguard Worker     public static void $noinline$bar() {
106*795d594fSAndroid Build Coastguard Worker         try {
107*795d594fSAndroid Build Coastguard Worker             // Be evil and clear all dex cache entries.
108*795d594fSAndroid Build Coastguard Worker             Field f = Class.class.getDeclaredField("dexCache");
109*795d594fSAndroid Build Coastguard Worker             f.setAccessible(true);
110*795d594fSAndroid Build Coastguard Worker             Object dexCache = f.get(Main.class);
111*795d594fSAndroid Build Coastguard Worker             f = dexCache.getClass().getDeclaredField("resolvedTypes");
112*795d594fSAndroid Build Coastguard Worker             f.setAccessible(true);
113*795d594fSAndroid Build Coastguard Worker             Object[] array = (Object[]) f.get(dexCache);
114*795d594fSAndroid Build Coastguard Worker             for (int i = 0; i < array.length; i++) {
115*795d594fSAndroid Build Coastguard Worker                 array[i] = null;
116*795d594fSAndroid Build Coastguard Worker             }
117*795d594fSAndroid Build Coastguard Worker             restoreResolvedMethods(Main.class, savedResolvedMethods);
118*795d594fSAndroid Build Coastguard Worker         } catch (Throwable t) { /* Ignore */ }
119*795d594fSAndroid Build Coastguard Worker 
120*795d594fSAndroid Build Coastguard Worker         // This will walk the stack, trying to resolve methods in it.
121*795d594fSAndroid Build Coastguard Worker         // Because we cleared dex cache entries, we will have to find
122*795d594fSAndroid Build Coastguard Worker         // classes again, which require to use the correct class loader
123*795d594fSAndroid Build Coastguard Worker         // in the presence of inlining.
124*795d594fSAndroid Build Coastguard Worker         new Exception().printStackTrace(System.out);
125*795d594fSAndroid Build Coastguard Worker     }
126*795d594fSAndroid Build Coastguard Worker     static Object savedResolvedMethods;
127*795d594fSAndroid Build Coastguard Worker 
cloneResolvedMethods(Class<?> cls)128*795d594fSAndroid Build Coastguard Worker     static native Object cloneResolvedMethods(Class<?> cls);
restoreResolvedMethods(Class<?> cls, Object saved)129*795d594fSAndroid Build Coastguard Worker     static native void restoreResolvedMethods(Class<?> cls, Object saved);
130*795d594fSAndroid Build Coastguard Worker }
131