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