• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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