blob: e3bf82c8c33a4360b39400a89b2f8a161d28e79f [file] [log] [blame]
jeffhao5d1ac922011-09-29 17:41:15 -07001/*
2 * Copyright (C) 2008 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
Mathieu Chartier2d902b92015-11-24 16:22:54 -080017import java.lang.reflect.Constructor;
18import java.lang.reflect.Method;
jeffhao5d1ac922011-09-29 17:41:15 -070019/**
20 * Class loader test.
21 */
22public class Main {
23 /**
24 * Main entry point.
25 */
Brian Carlstrom59885472015-04-20 21:55:19 -070026 public static void main(String[] args) throws Exception {
jeffhao5d1ac922011-09-29 17:41:15 -070027 FancyLoader loader;
28
29 loader = new FancyLoader(ClassLoader.getSystemClassLoader());
30 //System.out.println("SYSTEM: " + ClassLoader.getSystemClassLoader());
31 //System.out.println("ALTERN: " + loader);
32
33 /*
34 * This statement has no effect on this program, but it can
35 * change the point where a LinkageException is thrown in
36 * testImplement(). When this is present the "reference
37 * implementation" throws an exception from Class.newInstance(),
38 * when it's absent the exception is deferred until the first time
39 * we call a method that isn't actually implemented.
40 *
41 * This isn't the class that fails -- it's a class with the same
42 * name in the "fancy" class loader -- but the VM thinks it has a
43 * reference to one of these; presumably the difference is that
44 * without this the VM finds itself holding a reference to an
45 * instance of an uninitialized class.
46 */
47 System.out.println("base: " + DoubledImplement.class);
48 System.out.println("base2: " + DoubledImplement2.class);
49
50 /*
51 * Run tests.
52 */
53 testAccess1(loader);
54 testAccess2(loader);
55 testAccess3(loader);
56
57 testExtend(loader);
58 testExtendOkay(loader);
59 testInterface(loader);
60 testAbstract(loader);
61 testImplement(loader);
62 testIfaceImplement(loader);
Andreas Gampe26684c02015-04-15 23:23:51 -070063
64 testSeparation();
Brian Carlstrom59885472015-04-20 21:55:19 -070065
66 testClassForName();
Mathieu Chartier2d902b92015-11-24 16:22:54 -080067
68 // Attempt to load without a class table, regression test for b/25866849.
69 testLoadNativeLibrary(args[0]);
70 }
71
72 static void testLoadNativeLibrary(String libName) throws Exception {
73 Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
74 if (pathClassLoader == null) {
75 throw new AssertionError("Couldn't find path class loader class");
76 }
77 Constructor constructor =
78 pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
79 ClassLoader loader = (ClassLoader) constructor.newInstance(
80 FancyLoader.DEX_FILE, ClassLoader.getSystemClassLoader());
81 Runtime runtime = Runtime.getRuntime();
82 Method method = runtime.getClass().getDeclaredMethod("loadLibrary", String.class,
83 ClassLoader.class);
84 if (method == null) {
85 throw new RuntimeException("loadLibrary not found");
86 }
87 method.setAccessible(true);
88 method.invoke(runtime, libName, loader);
Andreas Gampe26684c02015-04-15 23:23:51 -070089 }
90
91 static void testSeparation() {
92 FancyLoader loader1 = new FancyLoader(ClassLoader.getSystemClassLoader());
93 FancyLoader loader2 = new FancyLoader(ClassLoader.getSystemClassLoader());
94
95 try {
96 Class target1 = loader1.loadClass("MutationTarget");
97 Class target2 = loader2.loadClass("MutationTarget");
98
99 if (target1 == target2) {
100 throw new RuntimeException("target1 should not be equal to target2");
101 }
102
103 Class mutator1 = loader1.loadClass("Mutator");
104 Class mutator2 = loader2.loadClass("Mutator");
105
106 if (mutator1 == mutator2) {
107 throw new RuntimeException("mutator1 should not be equal to mutator2");
108 }
109
110 runMutator(mutator1, 1);
111
112 int value = getMutationTargetValue(target1);
113 if (value != 1) {
114 throw new RuntimeException("target 1 has unexpected value " + value);
115 }
116 value = getMutationTargetValue(target2);
117 if (value != 0) {
118 throw new RuntimeException("target 2 has unexpected value " + value);
119 }
120
121 runMutator(mutator2, 2);
122
123 value = getMutationTargetValue(target1);
124 if (value != 1) {
125 throw new RuntimeException("target 1 has unexpected value " + value);
126 }
127 value = getMutationTargetValue(target2);
128 if (value != 2) {
129 throw new RuntimeException("target 2 has unexpected value " + value);
130 }
131 } catch (Exception ex) {
132 ex.printStackTrace();
133 }
134 }
135
136 private static void runMutator(Class c, int v) throws Exception {
137 java.lang.reflect.Method m = c.getDeclaredMethod("mutate", int.class);
138 m.invoke(null, v);
139 }
140
141 private static int getMutationTargetValue(Class c) throws Exception {
142 java.lang.reflect.Field f = c.getDeclaredField("value");
143 return f.getInt(null);
jeffhao5d1ac922011-09-29 17:41:15 -0700144 }
145
146 /**
147 * See if we can load a class that isn't public to us. We should be
148 * able to load it but not instantiate it.
149 */
150 static void testAccess1(ClassLoader loader) {
151 Class altClass;
152
153 try {
154 altClass = loader.loadClass("Inaccessible1");
155 } catch (ClassNotFoundException cnfe) {
156 System.err.println("loadClass failed");
157 cnfe.printStackTrace();
158 return;
159 }
160
161 /* instantiate */
162 Object obj;
163 try {
164 obj = altClass.newInstance();
165 System.err.println("ERROR: Inaccessible1 was accessible");
166 } catch (InstantiationException ie) {
167 System.err.println("newInstance failed: " + ie);
168 return;
169 } catch (IllegalAccessException iae) {
170 System.out.println("Got expected access exception #1");
171 //System.out.println("+++ " + iae);
172 return;
173 }
174 }
175
176 /**
177 * See if we can load a class whose base class is not accessible to it
178 * (though the base *is* accessible to us).
179 */
180 static void testAccess2(ClassLoader loader) {
181 Class altClass;
182
183 try {
184 altClass = loader.loadClass("Inaccessible2");
Brian Carlstrom4d9716c2012-01-30 01:49:33 -0800185 System.err.println("ERROR: Inaccessible2 was accessible: " + altClass);
jeffhao5d1ac922011-09-29 17:41:15 -0700186 } catch (ClassNotFoundException cnfe) {
187 Throwable cause = cnfe.getCause();
188 if (cause instanceof IllegalAccessError) {
189 System.out.println("Got expected CNFE/IAE #2");
190 } else {
191 System.err.println("Got unexpected CNFE/IAE #2");
192 cnfe.printStackTrace();
193 }
194 }
195 }
196
197 /**
198 * See if we can load a class with an inaccessible interface.
199 */
200 static void testAccess3(ClassLoader loader) {
201 Class altClass;
202
203 try {
204 altClass = loader.loadClass("Inaccessible3");
Brian Carlstrom4d9716c2012-01-30 01:49:33 -0800205 System.err.println("ERROR: Inaccessible3 was accessible: " + altClass);
jeffhao5d1ac922011-09-29 17:41:15 -0700206 } catch (ClassNotFoundException cnfe) {
207 Throwable cause = cnfe.getCause();
208 if (cause instanceof IllegalAccessError) {
209 System.out.println("Got expected CNFE/IAE #3");
210 } else {
211 System.err.println("Got unexpected CNFE/IAE #3");
212 cnfe.printStackTrace();
213 }
214 }
215 }
216
217 /**
218 * Test a doubled class that extends the base class.
219 */
220 static void testExtend(ClassLoader loader) {
221 Class doubledExtendClass;
222 Object obj;
223
224 /* get the "alternate" version of DoubledExtend */
225 try {
226 doubledExtendClass = loader.loadClass("DoubledExtend");
227 //System.out.println("+++ DoubledExtend is " + doubledExtendClass
228 // + " in " + doubledExtendClass.getClassLoader());
229 } catch (ClassNotFoundException cnfe) {
230 System.err.println("loadClass failed: " + cnfe);
231 return;
232 }
233
234 /* instantiate */
235 try {
236 obj = doubledExtendClass.newInstance();
237 } catch (InstantiationException ie) {
238 System.err.println("newInstance failed: " + ie);
239 return;
240 } catch (IllegalAccessException iae) {
241 System.err.println("newInstance failed: " + iae);
242 return;
243 } catch (LinkageError le) {
244 System.out.println("Got expected LinkageError on DE");
245 return;
246 }
247
248 /* use the base class reference to get a CL-specific instance */
249 Base baseRef = (Base) obj;
250 DoubledExtend de = baseRef.getExtended();
251
252 /* try to call through it */
253 try {
254 String result;
255
256 result = Base.doStuff(de);
257 System.err.println("ERROR: did not get LinkageError on DE");
258 System.err.println("(result=" + result + ")");
259 } catch (LinkageError le) {
260 System.out.println("Got expected LinkageError on DE");
261 return;
262 }
263 }
264
265 /**
266 * Test a doubled class that extends the base class, but is okay since
267 * it doesn't override the base class method.
268 */
269 static void testExtendOkay(ClassLoader loader) {
270 Class doubledExtendOkayClass;
271 Object obj;
272
273 /* get the "alternate" version of DoubledExtendOkay */
274 try {
275 doubledExtendOkayClass = loader.loadClass("DoubledExtendOkay");
276 } catch (ClassNotFoundException cnfe) {
277 System.err.println("loadClass failed: " + cnfe);
278 return;
279 }
280
281 /* instantiate */
282 try {
283 obj = doubledExtendOkayClass.newInstance();
284 } catch (InstantiationException ie) {
285 System.err.println("newInstance failed: " + ie);
286 return;
287 } catch (IllegalAccessException iae) {
288 System.err.println("newInstance failed: " + iae);
289 return;
290 } catch (LinkageError le) {
291 System.err.println("Got unexpected LinkageError on DEO");
292 le.printStackTrace();
293 return;
294 }
295
296 /* use the base class reference to get a CL-specific instance */
297 BaseOkay baseRef = (BaseOkay) obj;
298 DoubledExtendOkay de = baseRef.getExtended();
299
300 /* try to call through it */
301 try {
302 String result;
303
304 result = BaseOkay.doStuff(de);
305 System.out.println("Got DEO result " + result);
306 } catch (LinkageError le) {
307 System.err.println("Got unexpected LinkageError on DEO");
308 le.printStackTrace();
309 return;
310 }
311 }
312
313 /**
314 * Try to access a doubled class through a class that implements
315 * an interface declared in a different class.
316 */
317 static void testInterface(ClassLoader loader) {
318 Class getDoubledClass;
319 Object obj;
320
321 /* get GetDoubled from the "alternate" class loader */
322 try {
323 getDoubledClass = loader.loadClass("GetDoubled");
324 } catch (ClassNotFoundException cnfe) {
325 System.err.println("loadClass failed: " + cnfe);
326 return;
327 }
328
329 /* instantiate */
330 try {
331 obj = getDoubledClass.newInstance();
332 } catch (InstantiationException ie) {
333 System.err.println("newInstance failed: " + ie);
334 return;
335 } catch (IllegalAccessException iae) {
336 System.err.println("newInstance failed: " + iae);
337 return;
338 } catch (LinkageError le) {
339 // Dalvik bails here
340 System.out.println("Got LinkageError on GD");
341 return;
342 }
343
344 /*
345 * Cast the object to the interface, and try to use it.
346 */
347 IGetDoubled iface = (IGetDoubled) obj;
348 try {
349 /* "de" will be the wrong variety of DoubledExtendOkay */
350 DoubledExtendOkay de = iface.getDoubled();
351 // reference impl bails here
352 String str = de.getStr();
353 } catch (LinkageError le) {
354 System.out.println("Got LinkageError on GD");
355 return;
356 }
357 System.err.println("Should have failed by now on GetDoubled");
358 }
359
360 /**
361 * Throw an abstract class into the middle and see what happens.
362 */
363 static void testAbstract(ClassLoader loader) {
364 Class abstractGetClass;
365 Object obj;
366
367 /* get AbstractGet from the "alternate" loader */
368 try {
369 abstractGetClass = loader.loadClass("AbstractGet");
370 } catch (ClassNotFoundException cnfe) {
371 System.err.println("loadClass ta failed: " + cnfe);
372 return;
373 }
374
375 /* instantiate */
376 try {
377 obj = abstractGetClass.newInstance();
378 } catch (InstantiationException ie) {
379 System.err.println("newInstance failed: " + ie);
380 return;
381 } catch (IllegalAccessException iae) {
382 System.err.println("newInstance failed: " + iae);
383 return;
384 } catch (LinkageError le) {
385 System.out.println("Got LinkageError on TA");
386 return;
387 }
388
389 /* use the base class reference to get a CL-specific instance */
390 BaseOkay baseRef = (BaseOkay) obj;
391 DoubledExtendOkay de = baseRef.getExtended();
392
393 /* try to call through it */
394 try {
395 String result;
396
397 result = BaseOkay.doStuff(de);
398 } catch (LinkageError le) {
399 System.out.println("Got LinkageError on TA");
400 return;
401 }
402 System.err.println("Should have failed by now in testAbstract");
403 }
404
405 /**
406 * Test a doubled class that implements a common interface.
407 */
408 static void testImplement(ClassLoader loader) {
409 Class doubledImplementClass;
410 Object obj;
411
412 useImplement(new DoubledImplement(), true);
413
414 /* get the "alternate" version of DoubledImplement */
415 try {
416 doubledImplementClass = loader.loadClass("DoubledImplement");
417 } catch (ClassNotFoundException cnfe) {
418 System.err.println("loadClass failed: " + cnfe);
419 return;
420 }
421
422 /* instantiate */
423 try {
424 obj = doubledImplementClass.newInstance();
425 } catch (InstantiationException ie) {
426 System.err.println("newInstance failed: " + ie);
427 return;
428 } catch (IllegalAccessException iae) {
429 System.err.println("newInstance failed: " + iae);
430 return;
431 } catch (LinkageError le) {
432 System.out.println("Got LinkageError on DI (early)");
433 return;
434 }
435
436 /* if we lived this long, try to do something with it */
437 ICommon icommon = (ICommon) obj;
438 useImplement(icommon.getDoubledInstance(), false);
439 }
440
441 /**
442 * Do something with a DoubledImplement instance.
443 */
444 static void useImplement(DoubledImplement di, boolean isOne) {
445 //System.out.println("useObject: " + di.toString() + " -- "
446 // + di.getClass().getClassLoader());
447 try {
448 di.one();
449 if (!isOne) {
450 System.err.println("ERROR: did not get LinkageError on DI");
451 }
452 } catch (LinkageError le) {
453 if (!isOne) {
454 System.out.println("Got LinkageError on DI (late)");
455 } else {
456 throw le;
457 }
458 }
459 }
460
461
462 /**
463 * Test a class that implements an interface with a super-interface
464 * that refers to a doubled class.
465 */
466 static void testIfaceImplement(ClassLoader loader) {
467 Class ifaceImplClass;
468 Object obj;
469
470 /*
471 * Create an instance of IfaceImpl. We also pull in
472 * DoubledImplement2 from the other class loader; without this
473 * we don't fail in some implementations.
474 */
475 try {
476 ifaceImplClass = loader.loadClass("IfaceImpl");
477 ifaceImplClass = loader.loadClass("DoubledImplement2");
478 } catch (ClassNotFoundException cnfe) {
479 System.err.println("loadClass failed: " + cnfe);
480 return;
481 }
482
483 /* instantiate */
484 try {
485 obj = ifaceImplClass.newInstance();
486 } catch (InstantiationException ie) {
487 System.err.println("newInstance failed: " + ie);
488 return;
489 } catch (IllegalAccessException iae) {
490 System.err.println("newInstance failed: " + iae);
491 return;
492 } catch (LinkageError le) {
493 System.out.println("Got LinkageError on IDI (early)");
494 //System.out.println(le);
495 return;
496 }
497
498 /*
499 * Without the pre-load of FancyLoader->DoubledImplement2, some
500 * implementations will happily execute through this part. "obj"
501 * comes from FancyLoader, but the di2 returned from ifaceSuper
502 * comes from the application class loader.
503 */
504 IfaceSuper ifaceSuper = (IfaceSuper) obj;
505 DoubledImplement2 di2 = ifaceSuper.getDoubledInstance2();
506 di2.one();
507 }
Brian Carlstrom59885472015-04-20 21:55:19 -0700508
509 static void testClassForName() throws Exception {
510 System.out.println(Class.forName("Main").toString());
511 try {
512 System.out.println(Class.forName("Main", false, null).toString());
513 } catch (ClassNotFoundException expected) {
514 System.out.println("Got expected ClassNotFoundException");
515 }
516 }
jeffhao5d1ac922011-09-29 17:41:15 -0700517}