blob: 01b4bcd39174a8632757413f353596ebe7e74041 [file] [log] [blame]
Nicolas Geoffray32c9ea52015-06-12 14:52:33 +01001/*
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
17import java.lang.reflect.Field;
18import java.lang.reflect.Method;
19import java.util.List;
20
21class MyClassLoader extends ClassLoader {
22 MyClassLoader() throws Exception {
23 super(MyClassLoader.class.getClassLoader());
24
25 // Some magic to get access to the pathList field of BaseDexClassLoader.
26 ClassLoader loader = getClass().getClassLoader();
27 Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
28 Field f = baseDexClassLoader.getDeclaredField("pathList");
29 f.setAccessible(true);
30 Object pathList = f.get(loader);
31
32 // Some magic to get access to the dexField field of pathList.
33 f = pathList.getClass().getDeclaredField("dexElements");
34 f.setAccessible(true);
35 dexElements = (Object[]) f.get(pathList);
36 dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
37 dexFileField.setAccessible(true);
38 }
39
40 Object[] dexElements;
41 Field dexFileField;
42
43 static ClassLoader level1ClassLoader;
44
45 protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
46 if (this != level1ClassLoader) {
47 if (className.equals("Level1")) {
48 return level1ClassLoader.loadClass(className);
49 } else if (className.equals("Level2")) {
50 throw new ClassNotFoundException("None of my methods require Level2!");
51 } else if (!className.equals("LoadedByMyClassLoader")) {
52 // We're only going to handle LoadedByMyClassLoader.
53 return getParent().loadClass(className);
54 }
55 } else {
56 if (className != "Level1" && className != "Level2") {
57 return getParent().loadClass(className);
58 }
59 }
60
61 // Mimic what DexPathList.findClass is doing.
62 try {
63 for (Object element : dexElements) {
64 Object dex = dexFileField.get(element);
65 Method method = dex.getClass().getDeclaredMethod(
66 "loadClassBinaryName", String.class, ClassLoader.class, List.class);
67
68 if (dex != null) {
Andreas Gampe166aaee2016-07-18 08:27:23 -070069 Class<?> clazz = (Class<?>)method.invoke(dex, className, this, null);
Nicolas Geoffray32c9ea52015-06-12 14:52:33 +010070 if (clazz != null) {
71 return clazz;
72 }
73 }
74 }
75 } catch (Exception e) { /* Ignore */ }
76 return null;
77 }
78}
79
Nicolas Geoffray32c9ea52015-06-12 14:52:33 +010080class LoadedByMyClassLoader {
81 public static void bar() {
82 Level1.$inline$bar();
83 }
84}
85
86class Main {
Nicolas Geoffray32c9ea52015-06-12 14:52:33 +010087 public static void main(String[] args) throws Exception {
Mathieu Chartier031768a2015-08-27 10:25:02 -070088 System.loadLibrary(args[0]);
Nicolas Geoffray32c9ea52015-06-12 14:52:33 +010089 // Clone resolved methods, to restore the original version just
90 // before we walk the stack in $noinline$bar.
91 savedResolvedMethods = cloneResolvedMethods(Main.class);
92
93 MyClassLoader o = new MyClassLoader();
94 MyClassLoader.level1ClassLoader = new MyClassLoader();
Andreas Gampe166aaee2016-07-18 08:27:23 -070095 Class<?> foo = o.loadClass("LoadedByMyClassLoader");
Nicolas Geoffray32c9ea52015-06-12 14:52:33 +010096 Method m = foo.getDeclaredMethod("bar");
97 try {
98 m.invoke(null);
99 } catch (Error e) { /* Ignore */ }
100 }
101
102 public static void $inline$bar() {
103 }
104
105 public static void $noinline$bar() {
106 try {
107 // Be evil and clear all dex cache entries.
108 Field f = Class.class.getDeclaredField("dexCache");
109 f.setAccessible(true);
110 Object dexCache = f.get(Main.class);
111 f = dexCache.getClass().getDeclaredField("resolvedTypes");
112 f.setAccessible(true);
113 Object[] array = (Object[]) f.get(dexCache);
114 for (int i = 0; i < array.length; i++) {
115 array[i] = null;
116 }
117 restoreResolvedMethods(Main.class, savedResolvedMethods);
118 } catch (Throwable t) { /* Ignore */ }
119
120 // This will walk the stack, trying to resolve methods in it.
121 // Because we cleared dex cache entries, we will have to find
122 // classes again, which require to use the correct class loader
123 // in the presence of inlining.
Kevin Brodskyf6c66c32015-12-17 14:13:00 +0000124 new Exception().printStackTrace(System.out);
Nicolas Geoffray32c9ea52015-06-12 14:52:33 +0100125 }
126 static Object savedResolvedMethods;
127
128 static native Object cloneResolvedMethods(Class<?> cls);
129 static native void restoreResolvedMethods(Class<?> cls, Object saved);
130}