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 /**
     18  * Class loader test.
     19  */
     20 public class Main {
     21     /**
     22      * Main entry point.
     23      */
     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      */
     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      */
     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");
    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      */
    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");
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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