blob: bb8da6d3a04eef3d7d3946a66dc02e2f52cf79cf [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) {
69 Class clazz = (Class)method.invoke(dex, className, this, null);
70 if (clazz != null) {
71 return clazz;
72 }
73 }
74 }
75 } catch (Exception e) { /* Ignore */ }
76 return null;
77 }
78}
79
80class Level1 {
81 public static void $inline$bar() {
82 Level2.$inline$bar();
83 }
84}
85
86class Level2 {
87 public static void $inline$bar() {
88 Main.$noinline$bar();
89 }
90}
91
92class LoadedByMyClassLoader {
93 public static void bar() {
94 Level1.$inline$bar();
95 }
96}
97
98class Main {
99 static {
100 System.loadLibrary("arttest");
101 }
102
103 public static void main(String[] args) throws Exception {
104 // Clone resolved methods, to restore the original version just
105 // before we walk the stack in $noinline$bar.
106 savedResolvedMethods = cloneResolvedMethods(Main.class);
107
108 MyClassLoader o = new MyClassLoader();
109 MyClassLoader.level1ClassLoader = new MyClassLoader();
110 Class foo = o.loadClass("LoadedByMyClassLoader");
111 Method m = foo.getDeclaredMethod("bar");
112 try {
113 m.invoke(null);
114 } catch (Error e) { /* Ignore */ }
115 }
116
117 public static void $inline$bar() {
118 }
119
120 public static void $noinline$bar() {
121 try {
122 // Be evil and clear all dex cache entries.
123 Field f = Class.class.getDeclaredField("dexCache");
124 f.setAccessible(true);
125 Object dexCache = f.get(Main.class);
126 f = dexCache.getClass().getDeclaredField("resolvedTypes");
127 f.setAccessible(true);
128 Object[] array = (Object[]) f.get(dexCache);
129 for (int i = 0; i < array.length; i++) {
130 array[i] = null;
131 }
132 restoreResolvedMethods(Main.class, savedResolvedMethods);
133 } catch (Throwable t) { /* Ignore */ }
134
135 // This will walk the stack, trying to resolve methods in it.
136 // Because we cleared dex cache entries, we will have to find
137 // classes again, which require to use the correct class loader
138 // in the presence of inlining.
139 new Exception().printStackTrace();
140 }
141 static Object savedResolvedMethods;
142
143 static native Object cloneResolvedMethods(Class<?> cls);
144 static native void restoreResolvedMethods(Class<?> cls, Object saved);
145}