Home | History | Annotate | Download | only in src
      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      */
     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 
     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 
     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 
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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