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(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 
     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();
    133         }
    134     }
    135 
    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 
    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      */
    150     static void testAccess1(ClassLoader loader) {
    151         Class<?> altClass;
    152 
    153         try {
    154             altClass = loader.loadClass("Inaccessible1");
    155         } catch (ClassNotFoundException cnfe) {
    156             System.err.println("loadClass failed");
    157             cnfe.printStackTrace();
    158             return;
    159         }
    160 
    161         /* instantiate */
    162         Object obj;
    163         try {
    164             obj = altClass.newInstance();
    165             System.err.println("ERROR: Inaccessible1 was accessible");
    166         } catch (InstantiationException ie) {
    167             System.err.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      */
    180     static void testAccess2(ClassLoader loader) {
    181         Class<?> altClass;
    182 
    183         try {
    184             altClass = loader.loadClass("Inaccessible2");
    185             System.err.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.err.println("Got unexpected CNFE/IAE #2");
    192                 cnfe.printStackTrace();
    193             }
    194         }
    195     }
    196 
    197     /**
    198      * See if we can load a class with an inaccessible interface.
    199      */
    200     static void testAccess3(ClassLoader loader) {
    201         Class<?> altClass;
    202 
    203         try {
    204             altClass = loader.loadClass("Inaccessible3");
    205             System.err.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.err.println("Got unexpected CNFE/IAE #3");
    212                 cnfe.printStackTrace();
    213             }
    214         }
    215     }
    216 
    217     /**
    218      * Test a doubled class that extends the base class.
    219      */
    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.err.println("loadClass failed: " + cnfe);
    231             return;
    232         }
    233 
    234         /* instantiate */
    235         try {
    236             obj = doubledExtendClass.newInstance();
    237         } catch (InstantiationException ie) {
    238             System.err.println("newInstance failed: " + ie);
    239             return;
    240         } catch (IllegalAccessException iae) {
    241             System.err.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.err.println("ERROR: did not get LinkageError on DE");
    258             System.err.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      */
    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.err.println("loadClass failed: " + cnfe);
    278             return;
    279         }
    280 
    281         /* instantiate */
    282         try {
    283             obj = doubledExtendOkayClass.newInstance();
    284         } catch (InstantiationException ie) {
    285             System.err.println("newInstance failed: " + ie);
    286             return;
    287         } catch (IllegalAccessException iae) {
    288             System.err.println("newInstance failed: " + iae);
    289             return;
    290         } catch (LinkageError le) {
    291             System.err.println("Got unexpected LinkageError on DEO");
    292             le.printStackTrace();
    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.err.println("Got unexpected LinkageError on DEO");
    308             le.printStackTrace();
    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      */
    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.err.println("loadClass failed: " + cnfe);
    326             return;
    327         }
    328 
    329         /* instantiate */
    330         try {
    331             obj = getDoubledClass.newInstance();
    332         } catch (InstantiationException ie) {
    333             System.err.println("newInstance failed: " + ie);
    334             return;
    335         } catch (IllegalAccessException iae) {
    336             System.err.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.err.println("Should have failed by now on GetDoubled");
    358     }
    359 
    360     /**
    361      * Throw an abstract class into the middle and see what happens.
    362      */
    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.err.println("loadClass ta failed: " + cnfe);
    372             return;
    373         }
    374 
    375         /* instantiate */
    376         try {
    377             obj = abstractGetClass.newInstance();
    378         } catch (InstantiationException ie) {
    379             System.err.println("newInstance failed: " + ie);
    380             return;
    381         } catch (IllegalAccessException iae) {
    382             System.err.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.err.println("Should have failed by now in testAbstract");
    403     }
    404 
    405     /**
    406      * Test a doubled class that implements a common interface.
    407      */
    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.err.println("loadClass failed: " + cnfe);
    419             return;
    420         }
    421 
    422         /* instantiate */
    423         try {
    424             obj = doubledImplementClass.newInstance();
    425         } catch (InstantiationException ie) {
    426             System.err.println("newInstance failed: " + ie);
    427             return;
    428         } catch (IllegalAccessException iae) {
    429             System.err.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      */
    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.err.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      */
    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.err.println("loadClass failed: " + cnfe);
    480             return;
    481         }
    482 
    483         /* instantiate */
    484         try {
    485             obj = ifaceImplClass.newInstance();
    486         } catch (InstantiationException ie) {
    487             System.err.println("newInstance failed: " + ie);
    488             return;
    489         } catch (IllegalAccessException iae) {
    490             System.err.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 
    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