blob: 01539b7172bf0cbd3bba2cac3688f9d3a2b50d61 [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 Chartier6d25cf62016-04-12 16:54:48 -070017import java.lang.reflect.Constructor;
18import java.lang.reflect.Method;
19
jeffhao5d1ac922011-09-29 17:41:15 -070020/**
21 * Class loader test.
22 */
23public class Main {
24 /**
25 * Main entry point.
26 */
Brian Carlstrom59885472015-04-20 21:55:19 -070027 public static void main(String[] args) throws Exception {
jeffhao5d1ac922011-09-29 17:41:15 -070028 FancyLoader loader;
29
30 loader = new FancyLoader(ClassLoader.getSystemClassLoader());
31 //System.out.println("SYSTEM: " + ClassLoader.getSystemClassLoader());
32 //System.out.println("ALTERN: " + loader);
33
34 /*
35 * This statement has no effect on this program, but it can
36 * change the point where a LinkageException is thrown in
37 * testImplement(). When this is present the "reference
38 * implementation" throws an exception from Class.newInstance(),
39 * when it's absent the exception is deferred until the first time
40 * we call a method that isn't actually implemented.
41 *
42 * This isn't the class that fails -- it's a class with the same
43 * name in the "fancy" class loader -- but the VM thinks it has a
44 * reference to one of these; presumably the difference is that
45 * without this the VM finds itself holding a reference to an
46 * instance of an uninitialized class.
47 */
48 System.out.println("base: " + DoubledImplement.class);
49 System.out.println("base2: " + DoubledImplement2.class);
50
51 /*
52 * Run tests.
53 */
54 testAccess1(loader);
55 testAccess2(loader);
56 testAccess3(loader);
57
58 testExtend(loader);
59 testExtendOkay(loader);
60 testInterface(loader);
61 testAbstract(loader);
62 testImplement(loader);
63 testIfaceImplement(loader);
Andreas Gampe26684c02015-04-15 23:23:51 -070064
65 testSeparation();
Brian Carlstrom59885472015-04-20 21:55:19 -070066
67 testClassForName();
Mathieu Chartier6d25cf62016-04-12 16:54:48 -070068
69 testNullClassLoader();
70 }
71
72 static void testNullClassLoader() {
73 try {
74 /* this is the "alternate" DEX/Jar file */
75 String DEX_FILE = System.getenv("DEX_LOCATION") + "/068-classloader-ex.jar";
76 /* on Dalvik, this is a DexFile; otherwise, it's null */
Andreas Gampe166aaee2016-07-18 08:27:23 -070077 Class<?> mDexClass = Class.forName("dalvik.system.DexFile");
78 Constructor<?> ctor = mDexClass.getConstructor(String.class);
Mathieu Chartier6d25cf62016-04-12 16:54:48 -070079 Object mDexFile = ctor.newInstance(DEX_FILE);
Andreas Gampe166aaee2016-07-18 08:27:23 -070080 Method meth = mDexClass.getMethod("loadClass", String.class, ClassLoader.class);
Mathieu Chartier6d25cf62016-04-12 16:54:48 -070081 Object klass = meth.invoke(mDexFile, "Mutator", null);
82 if (klass == null) {
83 throw new AssertionError("loadClass with nullclass loader failed");
84 }
85 } catch (Exception e) {
86 System.out.println(e);
87 }
88 System.out.println("Loaded class into null class 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 {
Andreas Gampe166aaee2016-07-18 08:27:23 -070096 Class<?> target1 = loader1.loadClass("MutationTarget");
97 Class<?> target2 = loader2.loadClass("MutationTarget");
Andreas Gampe26684c02015-04-15 23:23:51 -070098
99 if (target1 == target2) {
100 throw new RuntimeException("target1 should not be equal to target2");
101 }
102
Andreas Gampe166aaee2016-07-18 08:27:23 -0700103 Class<?> mutator1 = loader1.loadClass("Mutator");
104 Class<?> mutator2 = loader2.loadClass("Mutator");
Andreas Gampe26684c02015-04-15 23:23:51 -0700105
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
Andreas Gampe166aaee2016-07-18 08:27:23 -0700136 private static void runMutator(Class<?> c, int v) throws Exception {
Andreas Gampe26684c02015-04-15 23:23:51 -0700137 java.lang.reflect.Method m = c.getDeclaredMethod("mutate", int.class);
138 m.invoke(null, v);
139 }
140
Andreas Gampe166aaee2016-07-18 08:27:23 -0700141 private static int getMutationTargetValue(Class<?> c) throws Exception {
Andreas Gampe26684c02015-04-15 23:23:51 -0700142 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) {
Andreas Gampe166aaee2016-07-18 08:27:23 -0700151 Class<?> altClass;
jeffhao5d1ac922011-09-29 17:41:15 -0700152
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) {
Andreas Gampe166aaee2016-07-18 08:27:23 -0700181 Class<?> altClass;
jeffhao5d1ac922011-09-29 17:41:15 -0700182
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) {
Andreas Gampe166aaee2016-07-18 08:27:23 -0700201 Class<?> altClass;
jeffhao5d1ac922011-09-29 17:41:15 -0700202
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) {
Andreas Gampe166aaee2016-07-18 08:27:23 -0700221 Class<?> doubledExtendClass;
jeffhao5d1ac922011-09-29 17:41:15 -0700222 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) {
Andreas Gampe166aaee2016-07-18 08:27:23 -0700270 Class<?> doubledExtendOkayClass;
jeffhao5d1ac922011-09-29 17:41:15 -0700271 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) {
Andreas Gampe166aaee2016-07-18 08:27:23 -0700318 Class<?> getDoubledClass;
jeffhao5d1ac922011-09-29 17:41:15 -0700319 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) {
Andreas Gampe166aaee2016-07-18 08:27:23 -0700364 Class<?> abstractGetClass;
jeffhao5d1ac922011-09-29 17:41:15 -0700365 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) {
Andreas Gampe166aaee2016-07-18 08:27:23 -0700409 Class<?> doubledImplementClass;
jeffhao5d1ac922011-09-29 17:41:15 -0700410 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) {
Andreas Gampe166aaee2016-07-18 08:27:23 -0700467 Class<?> ifaceImplClass;
jeffhao5d1ac922011-09-29 17:41:15 -0700468 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}