blob: 05a6baca7177741da08cf2997ce7899ff7b76bbf [file] [log] [blame]
Vladimir Marko25dcbad2016-11-23 13:35:26 +00001/*
2 * Copyright (C) 2016 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.ref.WeakReference;
18import java.lang.reflect.Field;
19import java.lang.reflect.InvocationTargetException;
20import java.lang.reflect.Method;
21
22public class Main {
23 public static void main(String[] args) throws Exception {
24 try {
25 System.loadLibrary(args[0]);
26 } catch (UnsatisfiedLinkError ule) {
27 usingRI = true;
28 // Add expected JNI_OnLoad log line to match expected.txt.
29 System.out.println("JNI_OnLoad called");
30 }
31
32 testClearDexCache();
33 testMultiDex();
34 testRacyLoader();
35 testRacyLoader2();
36 testMisbehavingLoader();
37 testRacyMisbehavingLoader();
38 testRacyMisbehavingLoader2();
39 }
40
41 private static void testClearDexCache() throws Exception {
42 DelegatingLoader delegating_loader = createDelegatingLoader();
43 Class<?> helper = delegating_loader.loadClass("Helper1");
44
45 WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper);
46 changeInner(delegating_loader);
47 if (!usingRI) {
48 clearResolvedTypes(helper);
49 }
50 Runtime.getRuntime().gc();
51 WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper);
52 Runtime.getRuntime().gc();
53
54 Class<?> test1 = weak_test1.get();
55 if (test1 == null) {
56 System.out.println("test1 disappeared");
57 }
58 Class<?> test2 = weak_test2.get();
59 if (test2 == null) {
60 System.out.println("test2 disappeared");
61 }
62 if (test1 != test2) {
63 System.out.println("test1 != test2");
64 }
65
66 System.out.println("testClearDexCache done");
67 }
68
69 private static void testMultiDex() throws Exception {
70 DelegatingLoader delegating_loader = createDelegatingLoader();
71
72 Class<?> helper1 = delegating_loader.loadClass("Helper1");
73 WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper1);
74
75 changeInner(delegating_loader);
76
77 Class<?> helper2 = delegating_loader.loadClass("Helper2");
78 WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper2);
79
80 Runtime.getRuntime().gc();
81
82 Class<?> test1 = weak_test1.get();
83 if (test1 == null) {
84 System.out.println("test1 disappeared");
85 }
86 Class<?> test2 = weak_test2.get();
87 if (test2 == null) {
88 System.out.println("test2 disappeared");
89 }
90 if (test1 != test2) {
91 System.out.println("test1 != test2");
92 }
93
94 System.out.println("testMultiDex done");
95 }
96
97 private static void testMisbehavingLoader() throws Exception {
98 ClassLoader system_loader = ClassLoader.getSystemClassLoader();
99 DefiningLoader defining_loader = new DefiningLoader(system_loader);
100 MisbehavingLoader misbehaving_loader =
101 new MisbehavingLoader(system_loader, defining_loader);
102 Class<?> helper = misbehaving_loader.loadClass("Helper1");
103
104 try {
105 WeakReference<Class<?>> weak_test = wrapHelperGet(helper);
106 } catch (InvocationTargetException ite) {
107 String message = ite.getCause().getMessage();
108 if (usingRI && "Test".equals(message)) {
109 // Replace RI message with dalvik message to match expected.txt.
110 message = "Initiating class loader of type " +
111 misbehaving_loader.getClass().getName() +
112 " returned class Helper2 instead of Test.";
113 }
114 System.out.println(ite.getCause().getClass().getName() + ": " + message);
115 }
116 System.out.println("testMisbehavingLoader done");
117 }
118
119 private static void testRacyLoader() throws Exception {
120 final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
121
122 final Thread[] threads = new Thread[4];
123 final Object[] results = new Object[threads.length];
124
125 final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length);
126 final Class<?> helper1 = racy_loader.loadClass("Helper1");
127
128 for (int i = 0; i != threads.length; ++i) {
129 final int my_index = i;
130 Thread t = new Thread() {
131 public void run() {
132 try {
133 Method get = helper1.getDeclaredMethod("get");
134 results[my_index] = get.invoke(null);
135 } catch (InvocationTargetException ite) {
136 results[my_index] = ite.getCause();
137 } catch (Throwable t) {
138 results[my_index] = t;
139 }
140 }
141 };
142 t.start();
143 threads[i] = t;
144 }
145 for (Thread t : threads) {
146 t.join();
147 }
148 dumpResultStats(results);
149 System.out.println("testRacyLoader done");
150 }
151
152 private static void testRacyLoader2() throws Exception {
153 final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
154
155 final Thread[] threads = new Thread[4];
156 final Object[] results = new Object[threads.length];
157
158 final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length);
159 final Class<?> helper1 = racy_loader.loadClass("Helper1");
160 final Class<?> helper3 = racy_loader.loadClass("Helper3");
161
162 for (int i = 0; i != threads.length; ++i) {
163 final int my_index = i;
164 Thread t = new Thread() {
165 public void run() {
166 try {
167 Class<?> helper = (my_index < threads.length / 2) ? helper1 : helper3;
168 Method get = helper.getDeclaredMethod("get");
169 results[my_index] = get.invoke(null);
170 } catch (InvocationTargetException ite) {
171 results[my_index] = ite.getCause();
172 } catch (Throwable t) {
173 results[my_index] = t;
174 }
175 }
176 };
177 t.start();
178 threads[i] = t;
179 }
180 for (Thread t : threads) {
181 t.join();
182 }
183 dumpResultStats(results);
184 System.out.println("testRacyLoader2 done");
185 }
186
187 private static void dumpResultStats(Object[] results) throws Exception {
188 int throwables = 0;
189 int class_weaks = 0;
190 int unique_class_weaks = 0;
191 for (int i = 0; i != results.length; ++i) {
192 Object r = results[i];
193 if (r instanceof Throwable) {
194 ++throwables;
195 System.out.println(((Throwable) r).getMessage());
196 } else if (isClassPair(r)) {
197 printPair(r);
198 Object ref = getSecond(r);
199 ++class_weaks;
200 ++unique_class_weaks;
201 for (int j = 0; j != i; ++j) {
202 Object rj = results[j];
203 if (isClassPair(results[j]) && getSecond(results[j]) == ref) {
204 --unique_class_weaks;
205 break;
206 }
207 }
208 }
209 }
210 System.out.println("total: " + results.length);
211 System.out.println(" throwables: " + throwables);
212 System.out.println(" class_weaks: " + class_weaks
213 + " (" + unique_class_weaks + " unique)");
214 }
215
216 private static void testRacyMisbehavingLoader() throws Exception {
217 final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
218
219 final Thread[] threads = new Thread[4];
220 final Object[] results = new Object[threads.length];
221
222 final RacyMisbehavingLoader racy_loader =
223 new RacyMisbehavingLoader(system_loader, threads.length, false);
224 final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper");
225
226 for (int i = 0; i != threads.length; ++i) {
227 final int my_index = i;
228 Thread t = new Thread() {
229 public void run() {
230 try {
231 Method get = helper1.getDeclaredMethod("get");
232 results[my_index] = get.invoke(null);
233 } catch (InvocationTargetException ite) {
234 results[my_index] = ite.getCause();
235 } catch (Throwable t) {
236 results[my_index] = t;
237 }
238 }
239 };
240 t.start();
241 threads[i] = t;
242 }
243 for (Thread t : threads) {
244 t.join();
245 }
246 dumpResultStats(results);
247 System.out.println("testRacyMisbehavingLoader done");
248 }
249
250 private static void testRacyMisbehavingLoader2() throws Exception {
251 final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
252
253 final Thread[] threads = new Thread[4];
254 final Object[] results = new Object[threads.length];
255
256 final RacyMisbehavingLoader racy_loader =
257 new RacyMisbehavingLoader(system_loader, threads.length, true);
258 final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper");
259
260 for (int i = 0; i != threads.length; ++i) {
261 final int my_index = i;
262 Thread t = new Thread() {
263 public void run() {
264 try {
265 Method get = helper1.getDeclaredMethod("get");
266 results[my_index] = get.invoke(null);
267 } catch (InvocationTargetException ite) {
268 results[my_index] = ite.getCause();
269 } catch (Throwable t) {
270 results[my_index] = t;
271 }
272 }
273 };
274 t.start();
275 threads[i] = t;
276 }
277 for (Thread t : threads) {
278 t.join();
279 }
280 dumpResultStats(results);
281 System.out.println("testRacyMisbehavingLoader2 done");
282 }
283
284 private static DelegatingLoader createDelegatingLoader() {
285 ClassLoader system_loader = ClassLoader.getSystemClassLoader();
286 DefiningLoader defining_loader = new DefiningLoader(system_loader);
287 return new DelegatingLoader(system_loader, defining_loader);
288 }
289
290 private static void changeInner(DelegatingLoader delegating_loader) {
291 ClassLoader system_loader = ClassLoader.getSystemClassLoader();
292 DefiningLoader defining_loader = new DefiningLoader(system_loader);
293 delegating_loader.resetDefiningLoader(defining_loader);
294 }
295
296 private static WeakReference<Class<?>> wrapHelperGet(Class<?> helper) throws Exception {
297 Method get = helper.getDeclaredMethod("get");
298 Object pair = get.invoke(null);
299 printPair(pair);
300 return new WeakReference<Class<?>>(getSecond(pair));
301 }
302
303 private static void printPair(Object pair) throws Exception {
304 Method print = pair.getClass().getDeclaredMethod("print");
305 print.invoke(pair);
306 }
307
308 private static Class<?> getSecond(Object pair) throws Exception {
309 Field second = pair.getClass().getDeclaredField("second");
310 return (Class<?>) second.get(pair);
311 }
312
313 private static boolean isClassPair(Object r) {
314 return r != null && r.getClass().getName().equals("ClassPair");
315 }
316
317 public static native void clearResolvedTypes(Class<?> c);
318
319 static boolean usingRI = false;
320}