/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * Class loader test. */ public class Main { /** * Main entry point. */ public static void main(String[] args) throws Exception { FancyLoader loader; loader = new FancyLoader(ClassLoader.getSystemClassLoader()); //System.out.println("SYSTEM: " + ClassLoader.getSystemClassLoader()); //System.out.println("ALTERN: " + loader); /* * This statement has no effect on this program, but it can * change the point where a LinkageException is thrown in * testImplement(). When this is present the "reference * implementation" throws an exception from Class.newInstance(), * when it's absent the exception is deferred until the first time * we call a method that isn't actually implemented. * * This isn't the class that fails -- it's a class with the same * name in the "fancy" class loader -- but the VM thinks it has a * reference to one of these; presumably the difference is that * without this the VM finds itself holding a reference to an * instance of an uninitialized class. */ System.out.println("base: " + DoubledImplement.class); System.out.println("base2: " + DoubledImplement2.class); /* * Run tests. */ testAccess1(loader); testAccess2(loader); testAccess3(loader); testExtend(loader); testExtendOkay(loader); testInterface(loader); testAbstract(loader); testImplement(loader); testIfaceImplement(loader); testSeparation(); testClassForName(); testNullClassLoader(); } static void testNullClassLoader() { try { /* this is the "alternate" DEX/Jar file */ String DEX_FILE = System.getenv("DEX_LOCATION") + "/068-classloader-ex.jar"; /* on Dalvik, this is a DexFile; otherwise, it's null */ Class mDexClass = Class.forName("dalvik.system.DexFile"); Constructor ctor = mDexClass.getConstructor(new Class[] {String.class}); Object mDexFile = ctor.newInstance(DEX_FILE); Method meth = mDexClass.getMethod("loadClass", new Class[] { String.class, ClassLoader.class }); Object klass = meth.invoke(mDexFile, "Mutator", null); if (klass == null) { throw new AssertionError("loadClass with nullclass loader failed"); } } catch (Exception e) { System.out.println(e); } System.out.println("Loaded class into null class loader"); } static void testSeparation() { FancyLoader loader1 = new FancyLoader(ClassLoader.getSystemClassLoader()); FancyLoader loader2 = new FancyLoader(ClassLoader.getSystemClassLoader()); try { Class target1 = loader1.loadClass("MutationTarget"); Class target2 = loader2.loadClass("MutationTarget"); if (target1 == target2) { throw new RuntimeException("target1 should not be equal to target2"); } Class mutator1 = loader1.loadClass("Mutator"); Class mutator2 = loader2.loadClass("Mutator"); if (mutator1 == mutator2) { throw new RuntimeException("mutator1 should not be equal to mutator2"); } runMutator(mutator1, 1); int value = getMutationTargetValue(target1); if (value != 1) { throw new RuntimeException("target 1 has unexpected value " + value); } value = getMutationTargetValue(target2); if (value != 0) { throw new RuntimeException("target 2 has unexpected value " + value); } runMutator(mutator2, 2); value = getMutationTargetValue(target1); if (value != 1) { throw new RuntimeException("target 1 has unexpected value " + value); } value = getMutationTargetValue(target2); if (value != 2) { throw new RuntimeException("target 2 has unexpected value " + value); } } catch (Exception ex) { ex.printStackTrace(); } } private static void runMutator(Class c, int v) throws Exception { java.lang.reflect.Method m = c.getDeclaredMethod("mutate", int.class); m.invoke(null, v); } private static int getMutationTargetValue(Class c) throws Exception { java.lang.reflect.Field f = c.getDeclaredField("value"); return f.getInt(null); } /** * See if we can load a class that isn't public to us. We should be * able to load it but not instantiate it. */ static void testAccess1(ClassLoader loader) { Class altClass; try { altClass = loader.loadClass("Inaccessible1"); } catch (ClassNotFoundException cnfe) { System.err.println("loadClass failed"); cnfe.printStackTrace(); return; } /* instantiate */ Object obj; try { obj = altClass.newInstance(); System.err.println("ERROR: Inaccessible1 was accessible"); } catch (InstantiationException ie) { System.err.println("newInstance failed: " + ie); return; } catch (IllegalAccessException iae) { System.out.println("Got expected access exception #1"); //System.out.println("+++ " + iae); return; } } /** * See if we can load a class whose base class is not accessible to it * (though the base *is* accessible to us). */ static void testAccess2(ClassLoader loader) { Class altClass; try { altClass = loader.loadClass("Inaccessible2"); System.err.println("ERROR: Inaccessible2 was accessible: " + altClass); } catch (ClassNotFoundException cnfe) { Throwable cause = cnfe.getCause(); if (cause instanceof IllegalAccessError) { System.out.println("Got expected CNFE/IAE #2"); } else { System.err.println("Got unexpected CNFE/IAE #2"); cnfe.printStackTrace(); } } } /** * See if we can load a class with an inaccessible interface. */ static void testAccess3(ClassLoader loader) { Class altClass; try { altClass = loader.loadClass("Inaccessible3"); System.err.println("ERROR: Inaccessible3 was accessible: " + altClass); } catch (ClassNotFoundException cnfe) { Throwable cause = cnfe.getCause(); if (cause instanceof IllegalAccessError) { System.out.println("Got expected CNFE/IAE #3"); } else { System.err.println("Got unexpected CNFE/IAE #3"); cnfe.printStackTrace(); } } } /** * Test a doubled class that extends the base class. */ static void testExtend(ClassLoader loader) { Class doubledExtendClass; Object obj; /* get the "alternate" version of DoubledExtend */ try { doubledExtendClass = loader.loadClass("DoubledExtend"); //System.out.println("+++ DoubledExtend is " + doubledExtendClass // + " in " + doubledExtendClass.getClassLoader()); } catch (ClassNotFoundException cnfe) { System.err.println("loadClass failed: " + cnfe); return; } /* instantiate */ try { obj = doubledExtendClass.newInstance(); } catch (InstantiationException ie) { System.err.println("newInstance failed: " + ie); return; } catch (IllegalAccessException iae) { System.err.println("newInstance failed: " + iae); return; } catch (LinkageError le) { System.out.println("Got expected LinkageError on DE"); return; } /* use the base class reference to get a CL-specific instance */ Base baseRef = (Base) obj; DoubledExtend de = baseRef.getExtended(); /* try to call through it */ try { String result; result = Base.doStuff(de); System.err.println("ERROR: did not get LinkageError on DE"); System.err.println("(result=" + result + ")"); } catch (LinkageError le) { System.out.println("Got expected LinkageError on DE"); return; } } /** * Test a doubled class that extends the base class, but is okay since * it doesn't override the base class method. */ static void testExtendOkay(ClassLoader loader) { Class doubledExtendOkayClass; Object obj; /* get the "alternate" version of DoubledExtendOkay */ try { doubledExtendOkayClass = loader.loadClass("DoubledExtendOkay"); } catch (ClassNotFoundException cnfe) { System.err.println("loadClass failed: " + cnfe); return; } /* instantiate */ try { obj = doubledExtendOkayClass.newInstance(); } catch (InstantiationException ie) { System.err.println("newInstance failed: " + ie); return; } catch (IllegalAccessException iae) { System.err.println("newInstance failed: " + iae); return; } catch (LinkageError le) { System.err.println("Got unexpected LinkageError on DEO"); le.printStackTrace(); return; } /* use the base class reference to get a CL-specific instance */ BaseOkay baseRef = (BaseOkay) obj; DoubledExtendOkay de = baseRef.getExtended(); /* try to call through it */ try { String result; result = BaseOkay.doStuff(de); System.out.println("Got DEO result " + result); } catch (LinkageError le) { System.err.println("Got unexpected LinkageError on DEO"); le.printStackTrace(); return; } } /** * Try to access a doubled class through a class that implements * an interface declared in a different class. */ static void testInterface(ClassLoader loader) { Class getDoubledClass; Object obj; /* get GetDoubled from the "alternate" class loader */ try { getDoubledClass = loader.loadClass("GetDoubled"); } catch (ClassNotFoundException cnfe) { System.err.println("loadClass failed: " + cnfe); return; } /* instantiate */ try { obj = getDoubledClass.newInstance(); } catch (InstantiationException ie) { System.err.println("newInstance failed: " + ie); return; } catch (IllegalAccessException iae) { System.err.println("newInstance failed: " + iae); return; } catch (LinkageError le) { // Dalvik bails here System.out.println("Got LinkageError on GD"); return; } /* * Cast the object to the interface, and try to use it. */ IGetDoubled iface = (IGetDoubled) obj; try { /* "de" will be the wrong variety of DoubledExtendOkay */ DoubledExtendOkay de = iface.getDoubled(); // reference impl bails here String str = de.getStr(); } catch (LinkageError le) { System.out.println("Got LinkageError on GD"); return; } System.err.println("Should have failed by now on GetDoubled"); } /** * Throw an abstract class into the middle and see what happens. */ static void testAbstract(ClassLoader loader) { Class abstractGetClass; Object obj; /* get AbstractGet from the "alternate" loader */ try { abstractGetClass = loader.loadClass("AbstractGet"); } catch (ClassNotFoundException cnfe) { System.err.println("loadClass ta failed: " + cnfe); return; } /* instantiate */ try { obj = abstractGetClass.newInstance(); } catch (InstantiationException ie) { System.err.println("newInstance failed: " + ie); return; } catch (IllegalAccessException iae) { System.err.println("newInstance failed: " + iae); return; } catch (LinkageError le) { System.out.println("Got LinkageError on TA"); return; } /* use the base class reference to get a CL-specific instance */ BaseOkay baseRef = (BaseOkay) obj; DoubledExtendOkay de = baseRef.getExtended(); /* try to call through it */ try { String result; result = BaseOkay.doStuff(de); } catch (LinkageError le) { System.out.println("Got LinkageError on TA"); return; } System.err.println("Should have failed by now in testAbstract"); } /** * Test a doubled class that implements a common interface. */ static void testImplement(ClassLoader loader) { Class doubledImplementClass; Object obj; useImplement(new DoubledImplement(), true); /* get the "alternate" version of DoubledImplement */ try { doubledImplementClass = loader.loadClass("DoubledImplement"); } catch (ClassNotFoundException cnfe) { System.err.println("loadClass failed: " + cnfe); return; } /* instantiate */ try { obj = doubledImplementClass.newInstance(); } catch (InstantiationException ie) { System.err.println("newInstance failed: " + ie); return; } catch (IllegalAccessException iae) { System.err.println("newInstance failed: " + iae); return; } catch (LinkageError le) { System.out.println("Got LinkageError on DI (early)"); return; } /* if we lived this long, try to do something with it */ ICommon icommon = (ICommon) obj; useImplement(icommon.getDoubledInstance(), false); } /** * Do something with a DoubledImplement instance. */ static void useImplement(DoubledImplement di, boolean isOne) { //System.out.println("useObject: " + di.toString() + " -- " // + di.getClass().getClassLoader()); try { di.one(); if (!isOne) { System.err.println("ERROR: did not get LinkageError on DI"); } } catch (LinkageError le) { if (!isOne) { System.out.println("Got LinkageError on DI (late)"); } else { throw le; } } } /** * Test a class that implements an interface with a super-interface * that refers to a doubled class. */ static void testIfaceImplement(ClassLoader loader) { Class ifaceImplClass; Object obj; /* * Create an instance of IfaceImpl. We also pull in * DoubledImplement2 from the other class loader; without this * we don't fail in some implementations. */ try { ifaceImplClass = loader.loadClass("IfaceImpl"); ifaceImplClass = loader.loadClass("DoubledImplement2"); } catch (ClassNotFoundException cnfe) { System.err.println("loadClass failed: " + cnfe); return; } /* instantiate */ try { obj = ifaceImplClass.newInstance(); } catch (InstantiationException ie) { System.err.println("newInstance failed: " + ie); return; } catch (IllegalAccessException iae) { System.err.println("newInstance failed: " + iae); return; } catch (LinkageError le) { System.out.println("Got LinkageError on IDI (early)"); //System.out.println(le); return; } /* * Without the pre-load of FancyLoader->DoubledImplement2, some * implementations will happily execute through this part. "obj" * comes from FancyLoader, but the di2 returned from ifaceSuper * comes from the application class loader. */ IfaceSuper ifaceSuper = (IfaceSuper) obj; DoubledImplement2 di2 = ifaceSuper.getDoubledInstance2(); di2.one(); } static void testClassForName() throws Exception { System.out.println(Class.forName("Main").toString()); try { System.out.println(Class.forName("Main", false, null).toString()); } catch (ClassNotFoundException expected) { System.out.println("Got expected ClassNotFoundException"); } } }