Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2010 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 import java.lang.reflect.InvocationTargetException;
     20 
     21 /**
     22  * Class loader test.
     23  */
     24 public class Main {
     25     /**
     26      * Thrown when an unexpected Exception is caught internally.
     27      */
     28     static class TestFailed extends Exception {
     29         public TestFailed(Throwable cause) {
     30             super(cause);
     31         }
     32     }
     33 
     34     /**
     35      * A class loader which loads classes from the dex file
     36      * "test.jar". However, it will return null when asked to load the
     37      * class InaccessibleSuper.
     38      *
     39      * When testing code calls BrokenDexLoader's findBrokenClass(),
     40      * a BrokenDexLoader will be the defining loader for the class
     41      * Inaccessible.  The VM will call the defining loader for
     42      * "InaccessibleSuper", which will return null, which the VM
     43      * should be able to deal with gracefully.
     44      *
     45      * Note that this depends heavily on the Dalvik test harness.
     46      */
     47     static class BrokenDexLoader extends ClassLoader {
     48 
     49         /** We return null when asked to load InaccessibleSuper. */
     50         private static class InaccessibleSuper {}
     51         private static class Inaccessible extends InaccessibleSuper {}
     52 
     53         private static final String SUPERCLASS_NAME =
     54                 "Main$BrokenDexLoader$InaccessibleSuper";
     55         private static final String CLASS_NAME =
     56                 "Main$BrokenDexLoader$Inaccessible";
     57 
     58         private static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/087-gc-after-link.jar";
     59 
     60         public BrokenDexLoader(ClassLoader parent) {
     61             super(parent);
     62         }
     63 
     64         /**
     65          * Finds the class with the specified binary name, from DEX_FILE.
     66          *
     67          * If we don't find a match, we throw an exception.
     68          */
     69         private Class<?> findDexClass(String name)
     70                 throws TestFailed, InvocationTargetException
     71         {
     72             Object dexFile = null;
     73             Class dexClass = null;
     74 
     75             try {
     76                 try {
     77                     /*
     78                      * Find the DexFile class, and construct a DexFile object
     79                      * through reflection, then call loadClass on it.
     80                      */
     81                     dexClass = ClassLoader.getSystemClassLoader().
     82                             loadClass("dalvik.system.DexFile");
     83                     Constructor ctor = dexClass.
     84                             getConstructor(new Class[] {String.class});
     85                     dexFile = ctor.newInstance(DEX_FILE);
     86                     Method meth = dexClass.getMethod("loadClass",
     87                             new Class[] { String.class, ClassLoader.class });
     88                     /*
     89                      * Invoking loadClass on CLASS_NAME is expected to
     90                      * throw an InvocationTargetException. Anything else
     91                      * is an error we can't recover from.
     92                      */
     93                     meth.invoke(dexFile, name, this);
     94                     System.out.println("Unreachable");
     95                 } finally {
     96                     if (dexFile != null) {
     97                         /* close the DexFile to make CloseGuard happy */
     98                         Method meth = dexClass.getMethod("close", (Class[]) null);
     99                         meth.invoke(dexFile);
    100                     }
    101                 }
    102             } catch (NoSuchMethodException nsme) {
    103                 throw new TestFailed(nsme);
    104             } catch (InstantiationException ie) {
    105                 throw new TestFailed(ie);
    106             } catch (IllegalAccessException iae) {
    107                 throw new TestFailed(iae);
    108             } catch (ClassNotFoundException cnfe) {
    109                 throw new TestFailed(cnfe);
    110             }
    111 
    112             return null;
    113         }
    114 
    115         /**
    116          * Load a class.
    117          *
    118          * Return null if the class's name is SUPERCLASS_NAME;
    119          * otherwise invoke the super's loadClass method.
    120          */
    121         public Class<?> loadClass(String name, boolean resolve)
    122                 throws ClassNotFoundException
    123         {
    124             if (SUPERCLASS_NAME.equals(name)) {
    125                 return null;
    126             }
    127 
    128             return super.loadClass(name, resolve);
    129         }
    130 
    131         /**
    132          * Attempt to find the class with the superclass we refuse to
    133          * load.  This is expected to throw an
    134          * InvocationTargetException, with a NullPointerException as
    135          * its cause.
    136          */
    137         public void findBrokenClass()
    138                 throws TestFailed, InvocationTargetException
    139         {
    140             findDexClass(CLASS_NAME);
    141         }
    142     }
    143 
    144     /**
    145      * Main entry point.
    146      */
    147     public static void main(String[] args)
    148             throws TestFailed, ClassNotFoundException {
    149         /*
    150          * Run test.
    151          */
    152         testFailLoadAndGc();
    153     }
    154 
    155     /**
    156      * See if we can GC after a failed load.
    157      */
    158     static void testFailLoadAndGc() throws TestFailed {
    159         processFailLoadAndGc();
    160         Runtime.getRuntime().gc();
    161         System.out.println("GC complete.");
    162     }
    163 
    164     private static void processFailLoadAndGc() throws TestFailed {
    165         try {
    166             BrokenDexLoader loader;
    167 
    168             loader = new BrokenDexLoader(ClassLoader.getSystemClassLoader());
    169             loader.findBrokenClass();
    170             System.err.println("ERROR: Inaccessible was accessible");
    171         } catch (InvocationTargetException ite) {
    172             Throwable cause = ite.getCause();
    173             if (cause instanceof NullPointerException) {
    174                 System.err.println("Got expected ITE/NPE");
    175             } else {
    176                 System.err.println("Got unexpected ITE");
    177                 ite.printStackTrace();
    178             }
    179         }
    180     }
    181 }
    182