1 /*
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 
17 import java.lang.reflect.Constructor;
18 import java.lang.reflect.Method;
19 
20 /**
21  * Class loader test.
22  */
23 public class Main {
24     /**
25      * Main entry point.
26      */
main(String[] args)27     public static void main(String[] args) throws Exception {
28         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);
64 
65         testSeparation();
66 
67         testClassForName();
68 
69         testNullClassLoader();
70     }
71 
testNullClassLoader()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 */
77             Class<?> mDexClass = Class.forName("dalvik.system.DexFile");
78             Constructor<?> ctor = mDexClass.getConstructor(String.class);
79             Object mDexFile = ctor.newInstance(DEX_FILE);
80             Method meth = mDexClass.getMethod("loadClass", String.class, ClassLoader.class);
81             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");
89     }
90 
testSeparation()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(System.out);
133         }
134     }
135 
runMutator(Class<?> c, int v)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 
getMutationTargetValue(Class<?> c)141     private static int getMutationTargetValue(Class<?> c) throws Exception {
142         java.lang.reflect.Field f = c.getDeclaredField("value");
143         return f.getInt(null);
144     }
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      */
testAccess1(ClassLoader loader)150     static void testAccess1(ClassLoader loader) {
151         Class<?> altClass;
152 
153         try {
154             altClass = loader.loadClass("Inaccessible1");
155         } catch (ClassNotFoundException cnfe) {
156             System.out.println("loadClass failed");
157             cnfe.printStackTrace(System.out);
158             return;
159         }
160 
161         /* instantiate */
162         Object obj;
163         try {
164             obj = altClass.newInstance();
165             System.out.println("ERROR: Inaccessible1 was accessible");
166         } catch (InstantiationException ie) {
167             System.out.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      */
testAccess2(ClassLoader loader)180     static void testAccess2(ClassLoader loader) {
181         Class<?> altClass;
182 
183         try {
184             altClass = loader.loadClass("Inaccessible2");
185             System.out.println("ERROR: Inaccessible2 was accessible: " + altClass);
186         } 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.out.println("Got unexpected CNFE/IAE #2");
192                 cnfe.printStackTrace(System.out);
193             }
194         }
195     }
196 
197     /**
198      * See if we can load a class with an inaccessible interface.
199      */
testAccess3(ClassLoader loader)200     static void testAccess3(ClassLoader loader) {
201         Class<?> altClass;
202 
203         try {
204             altClass = loader.loadClass("Inaccessible3");
205             System.out.println("ERROR: Inaccessible3 was accessible: " + altClass);
206         } 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.out.println("Got unexpected CNFE/IAE #3");
212                 cnfe.printStackTrace(System.out);
213             }
214         }
215     }
216 
217     /**
218      * Test a doubled class that extends the base class.
219      */
testExtend(ClassLoader loader)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.out.println("loadClass failed: " + cnfe);
231             return;
232         }
233 
234         /* instantiate */
235         try {
236             obj = doubledExtendClass.newInstance();
237         } catch (InstantiationException ie) {
238             System.out.println("newInstance failed: " + ie);
239             return;
240         } catch (IllegalAccessException iae) {
241             System.out.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.out.println("ERROR: did not get LinkageError on DE");
258             System.out.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      */
testExtendOkay(ClassLoader loader)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.out.println("loadClass failed: " + cnfe);
278             return;
279         }
280 
281         /* instantiate */
282         try {
283             obj = doubledExtendOkayClass.newInstance();
284         } catch (InstantiationException ie) {
285             System.out.println("newInstance failed: " + ie);
286             return;
287         } catch (IllegalAccessException iae) {
288             System.out.println("newInstance failed: " + iae);
289             return;
290         } catch (LinkageError le) {
291             System.out.println("Got unexpected LinkageError on DEO");
292             le.printStackTrace(System.out);
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.out.println("Got unexpected LinkageError on DEO");
308             le.printStackTrace(System.out);
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      */
testInterface(ClassLoader loader)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.out.println("loadClass failed: " + cnfe);
326             return;
327         }
328 
329         /* instantiate */
330         try {
331             obj = getDoubledClass.newInstance();
332         } catch (InstantiationException ie) {
333             System.out.println("newInstance failed: " + ie);
334             return;
335         } catch (IllegalAccessException iae) {
336             System.out.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.out.println("Should have failed by now on GetDoubled");
358     }
359 
360     /**
361      * Throw an abstract class into the middle and see what happens.
362      */
testAbstract(ClassLoader loader)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.out.println("loadClass ta failed: " + cnfe);
372             return;
373         }
374 
375         /* instantiate */
376         try {
377             obj = abstractGetClass.newInstance();
378         } catch (InstantiationException ie) {
379             System.out.println("newInstance failed: " + ie);
380             return;
381         } catch (IllegalAccessException iae) {
382             System.out.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.out.println("Should have failed by now in testAbstract");
403     }
404 
405     /**
406      * Test a doubled class that implements a common interface.
407      */
testImplement(ClassLoader loader)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.out.println("loadClass failed: " + cnfe);
419             return;
420         }
421 
422         /* instantiate */
423         try {
424             obj = doubledImplementClass.newInstance();
425         } catch (InstantiationException ie) {
426             System.out.println("newInstance failed: " + ie);
427             return;
428         } catch (IllegalAccessException iae) {
429             System.out.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      */
useImplement(DoubledImplement di, boolean isOne)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.out.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      */
testIfaceImplement(ClassLoader loader)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.out.println("loadClass failed: " + cnfe);
480             return;
481         }
482 
483         /* instantiate */
484         try {
485             obj = ifaceImplClass.newInstance();
486         } catch (InstantiationException ie) {
487             System.out.println("newInstance failed: " + ie);
488             return;
489         } catch (IllegalAccessException iae) {
490             System.out.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     }
508 
testClassForName()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     }
517 }
518