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) {
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 
63     /**
64      * See if we can load a class that isn't public to us.  We should be
65      * able to load it but not instantiate it.
66      */
testAccess1(ClassLoader loader)67     static void testAccess1(ClassLoader loader) {
68         Class altClass;
69 
70         try {
71             altClass = loader.loadClass("Inaccessible1");
72         } catch (ClassNotFoundException cnfe) {
73             System.err.println("loadClass failed");
74             cnfe.printStackTrace();
75             return;
76         }
77 
78         /* instantiate */
79         Object obj;
80         try {
81             obj = altClass.newInstance();
82             System.err.println("ERROR: Inaccessible1 was accessible");
83         } catch (InstantiationException ie) {
84             System.err.println("newInstance failed: " + ie);
85             return;
86         } catch (IllegalAccessException iae) {
87             System.out.println("Got expected access exception #1");
88             //System.out.println("+++ " + iae);
89             return;
90         }
91     }
92 
93     /**
94      * See if we can load a class whose base class is not accessible to it
95      * (though the base *is* accessible to us).
96      */
testAccess2(ClassLoader loader)97     static void testAccess2(ClassLoader loader) {
98         Class altClass;
99 
100         try {
101             altClass = loader.loadClass("Inaccessible2");
102             System.err.println("ERROR: Inaccessible2 was accessible: " + altClass);
103         } catch (ClassNotFoundException cnfe) {
104             Throwable cause = cnfe.getCause();
105             if (cause instanceof IllegalAccessError) {
106                 System.out.println("Got expected CNFE/IAE #2");
107             } else {
108                 System.err.println("Got unexpected CNFE/IAE #2");
109                 cnfe.printStackTrace();
110             }
111         }
112     }
113 
114     /**
115      * See if we can load a class with an inaccessible interface.
116      */
testAccess3(ClassLoader loader)117     static void testAccess3(ClassLoader loader) {
118         Class altClass;
119 
120         try {
121             altClass = loader.loadClass("Inaccessible3");
122             System.err.println("ERROR: Inaccessible3 was accessible: " + altClass);
123         } catch (ClassNotFoundException cnfe) {
124             Throwable cause = cnfe.getCause();
125             if (cause instanceof IllegalAccessError) {
126                 System.out.println("Got expected CNFE/IAE #3");
127             } else {
128                 System.err.println("Got unexpected CNFE/IAE #3");
129                 cnfe.printStackTrace();
130             }
131         }
132     }
133 
134     /**
135      * Test a doubled class that extends the base class.
136      */
testExtend(ClassLoader loader)137     static void testExtend(ClassLoader loader) {
138         Class doubledExtendClass;
139         Object obj;
140 
141         /* get the "alternate" version of DoubledExtend */
142         try {
143             doubledExtendClass = loader.loadClass("DoubledExtend");
144             //System.out.println("+++ DoubledExtend is " + doubledExtendClass
145             //    + " in " + doubledExtendClass.getClassLoader());
146         } catch (ClassNotFoundException cnfe) {
147             System.err.println("loadClass failed: " + cnfe);
148             return;
149         }
150 
151         /* instantiate */
152         try {
153             obj = doubledExtendClass.newInstance();
154         } catch (InstantiationException ie) {
155             System.err.println("newInstance failed: " + ie);
156             return;
157         } catch (IllegalAccessException iae) {
158             System.err.println("newInstance failed: " + iae);
159             return;
160         } catch (LinkageError le) {
161             System.out.println("Got expected LinkageError on DE");
162             return;
163         }
164 
165         /* use the base class reference to get a CL-specific instance */
166         Base baseRef = (Base) obj;
167         DoubledExtend de = baseRef.getExtended();
168 
169         /* try to call through it */
170         try {
171             String result;
172 
173             result = Base.doStuff(de);
174             System.err.println("ERROR: did not get LinkageError on DE");
175             System.err.println("(result=" + result + ")");
176         } catch (LinkageError le) {
177             System.out.println("Got expected LinkageError on DE");
178             return;
179         }
180     }
181 
182     /**
183      * Test a doubled class that extends the base class, but is okay since
184      * it doesn't override the base class method.
185      */
testExtendOkay(ClassLoader loader)186     static void testExtendOkay(ClassLoader loader) {
187         Class doubledExtendOkayClass;
188         Object obj;
189 
190         /* get the "alternate" version of DoubledExtendOkay */
191         try {
192             doubledExtendOkayClass = loader.loadClass("DoubledExtendOkay");
193         } catch (ClassNotFoundException cnfe) {
194             System.err.println("loadClass failed: " + cnfe);
195             return;
196         }
197 
198         /* instantiate */
199         try {
200             obj = doubledExtendOkayClass.newInstance();
201         } catch (InstantiationException ie) {
202             System.err.println("newInstance failed: " + ie);
203             return;
204         } catch (IllegalAccessException iae) {
205             System.err.println("newInstance failed: " + iae);
206             return;
207         } catch (LinkageError le) {
208             System.err.println("Got unexpected LinkageError on DEO");
209             le.printStackTrace();
210             return;
211         }
212 
213         /* use the base class reference to get a CL-specific instance */
214         BaseOkay baseRef = (BaseOkay) obj;
215         DoubledExtendOkay de = baseRef.getExtended();
216 
217         /* try to call through it */
218         try {
219             String result;
220 
221             result = BaseOkay.doStuff(de);
222             System.out.println("Got DEO result " + result);
223         } catch (LinkageError le) {
224             System.err.println("Got unexpected LinkageError on DEO");
225             le.printStackTrace();
226             return;
227         }
228     }
229 
230     /**
231      * Try to access a doubled class through a class that implements
232      * an interface declared in a different class.
233      */
testInterface(ClassLoader loader)234     static void testInterface(ClassLoader loader) {
235         Class getDoubledClass;
236         Object obj;
237 
238         /* get GetDoubled from the "alternate" class loader */
239         try {
240             getDoubledClass = loader.loadClass("GetDoubled");
241         } catch (ClassNotFoundException cnfe) {
242             System.err.println("loadClass failed: " + cnfe);
243             return;
244         }
245 
246         /* instantiate */
247         try {
248             obj = getDoubledClass.newInstance();
249         } catch (InstantiationException ie) {
250             System.err.println("newInstance failed: " + ie);
251             return;
252         } catch (IllegalAccessException iae) {
253             System.err.println("newInstance failed: " + iae);
254             return;
255         } catch (LinkageError le) {
256             // Dalvik bails here
257             System.out.println("Got LinkageError on GD");
258             return;
259         }
260 
261         /*
262          * Cast the object to the interface, and try to use it.
263          */
264         IGetDoubled iface = (IGetDoubled) obj;
265         try {
266             /* "de" will be the wrong variety of DoubledExtendOkay */
267             DoubledExtendOkay de = iface.getDoubled();
268             // reference impl bails here
269             String str = de.getStr();
270         } catch (LinkageError le) {
271             System.out.println("Got LinkageError on GD");
272             return;
273         }
274         System.err.println("Should have failed by now on GetDoubled");
275     }
276 
277     /**
278      * Throw an abstract class into the middle and see what happens.
279      */
testAbstract(ClassLoader loader)280     static void testAbstract(ClassLoader loader) {
281         Class abstractGetClass;
282         Object obj;
283 
284         /* get AbstractGet from the "alternate" loader */
285         try {
286             abstractGetClass = loader.loadClass("AbstractGet");
287         } catch (ClassNotFoundException cnfe) {
288             System.err.println("loadClass ta failed: " + cnfe);
289             return;
290         }
291 
292         /* instantiate */
293         try {
294             obj = abstractGetClass.newInstance();
295         } catch (InstantiationException ie) {
296             System.err.println("newInstance failed: " + ie);
297             return;
298         } catch (IllegalAccessException iae) {
299             System.err.println("newInstance failed: " + iae);
300             return;
301         } catch (LinkageError le) {
302             System.out.println("Got LinkageError on TA");
303             return;
304         }
305 
306         /* use the base class reference to get a CL-specific instance */
307         BaseOkay baseRef = (BaseOkay) obj;
308         DoubledExtendOkay de = baseRef.getExtended();
309 
310         /* try to call through it */
311         try {
312             String result;
313 
314             result = BaseOkay.doStuff(de);
315         } catch (LinkageError le) {
316             System.out.println("Got LinkageError on TA");
317             return;
318         }
319         System.err.println("Should have failed by now in testAbstract");
320     }
321 
322     /**
323      * Test a doubled class that implements a common interface.
324      */
testImplement(ClassLoader loader)325     static void testImplement(ClassLoader loader) {
326         Class doubledImplementClass;
327         Object obj;
328 
329         useImplement(new DoubledImplement(), true);
330 
331         /* get the "alternate" version of DoubledImplement */
332         try {
333             doubledImplementClass = loader.loadClass("DoubledImplement");
334         } catch (ClassNotFoundException cnfe) {
335             System.err.println("loadClass failed: " + cnfe);
336             return;
337         }
338 
339         /* instantiate */
340         try {
341             obj = doubledImplementClass.newInstance();
342         } catch (InstantiationException ie) {
343             System.err.println("newInstance failed: " + ie);
344             return;
345         } catch (IllegalAccessException iae) {
346             System.err.println("newInstance failed: " + iae);
347             return;
348         } catch (LinkageError le) {
349             System.out.println("Got LinkageError on DI (early)");
350             return;
351         }
352 
353         /* if we lived this long, try to do something with it */
354         ICommon icommon = (ICommon) obj;
355         useImplement(icommon.getDoubledInstance(), false);
356     }
357 
358     /**
359      * Do something with a DoubledImplement instance.
360      */
useImplement(DoubledImplement di, boolean isOne)361     static void useImplement(DoubledImplement di, boolean isOne) {
362         //System.out.println("useObject: " + di.toString() + " -- "
363         //    + di.getClass().getClassLoader());
364         try {
365             di.one();
366             if (!isOne) {
367                 System.err.println("ERROR: did not get LinkageError on DI");
368             }
369         } catch (LinkageError le) {
370             if (!isOne) {
371                 System.out.println("Got LinkageError on DI (late)");
372             } else {
373                 throw le;
374             }
375         }
376     }
377 
378 
379     /**
380      * Test a class that implements an interface with a super-interface
381      * that refers to a doubled class.
382      */
testIfaceImplement(ClassLoader loader)383     static void testIfaceImplement(ClassLoader loader) {
384         Class ifaceImplClass;
385         Object obj;
386 
387         /*
388          * Create an instance of IfaceImpl.  We also pull in
389          * DoubledImplement2 from the other class loader; without this
390          * we don't fail in some implementations.
391          */
392         try {
393             ifaceImplClass = loader.loadClass("IfaceImpl");
394             ifaceImplClass = loader.loadClass("DoubledImplement2");
395         } catch (ClassNotFoundException cnfe) {
396             System.err.println("loadClass failed: " + cnfe);
397             return;
398         }
399 
400         /* instantiate */
401         try {
402             obj = ifaceImplClass.newInstance();
403         } catch (InstantiationException ie) {
404             System.err.println("newInstance failed: " + ie);
405             return;
406         } catch (IllegalAccessException iae) {
407             System.err.println("newInstance failed: " + iae);
408             return;
409         } catch (LinkageError le) {
410             System.out.println("Got LinkageError on IDI (early)");
411             //System.out.println(le);
412             return;
413         }
414 
415         /*
416          * Without the pre-load of FancyLoader->DoubledImplement2, some
417          * implementations will happily execute through this part.  "obj"
418          * comes from FancyLoader, but the di2 returned from ifaceSuper
419          * comes from the application class loader.
420          */
421         IfaceSuper ifaceSuper = (IfaceSuper) obj;
422         DoubledImplement2 di2 = ifaceSuper.getDoubledInstance2();
423         di2.one();
424     }
425 }
426