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