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 { TestFailed(Throwable cause)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") + "/086-null-super.jar"; 59 BrokenDexLoader(ClassLoader parent)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 */ findDexClass(String name)69 private Class<?> findDexClass(String name) 70 throws TestFailed, InvocationTargetException 71 { 72 73 try { 74 /* 75 * Find the DexFile class, and construct a DexFile object 76 * through reflection, then call loadCLass on it. 77 */ 78 Class<?> mDexClass = ClassLoader.getSystemClassLoader(). 79 loadClass("dalvik.system.DexFile"); 80 Constructor<?> ctor = mDexClass.getConstructor(String.class); 81 Object mDexFile = ctor.newInstance(DEX_FILE); 82 Method meth = mDexClass. 83 getMethod("loadClass", String.class, ClassLoader.class); 84 /* 85 * Invoking loadClass on CLASS_NAME is expected to 86 * throw an InvocationTargetException. Anything else 87 * is an error we can't recover from. 88 */ 89 meth.invoke(mDexFile, name, this); 90 } catch (NoSuchMethodException nsme) { 91 throw new TestFailed(nsme); 92 } catch (InstantiationException ie) { 93 throw new TestFailed(ie); 94 } catch (IllegalAccessException iae) { 95 throw new TestFailed(iae); 96 } catch (ClassNotFoundException cnfe) { 97 throw new TestFailed(cnfe); 98 } 99 100 return null; 101 } 102 103 /** 104 * Load a class. 105 * 106 * Return null if the class's name is SUPERCLASS_NAME; 107 * otherwise invoke the super's loadClass method. 108 */ loadClass(String name, boolean resolve)109 public Class<?> loadClass(String name, boolean resolve) 110 throws ClassNotFoundException 111 { 112 if (SUPERCLASS_NAME.equals(name)) { 113 return null; 114 } 115 116 return super.loadClass(name, resolve); 117 } 118 119 /** 120 * Attempt to find the class with the superclass we refuse to 121 * load. This is expected to throw an 122 * InvocationTargetException, with a NullPointerException as 123 * its cause. 124 */ findBrokenClass()125 public void findBrokenClass() 126 throws TestFailed, InvocationTargetException 127 { 128 findDexClass(CLASS_NAME); 129 } 130 } 131 132 /** 133 * Main entry point. 134 */ main(String[] args)135 public static void main(String[] args) 136 throws TestFailed, ClassNotFoundException { 137 /* 138 * Run test. 139 */ 140 testFailLoadAndGc(); 141 } 142 143 /** 144 * See if we can GC after a failed load. 145 */ testFailLoadAndGc()146 static void testFailLoadAndGc() throws TestFailed { 147 try { 148 BrokenDexLoader loader; 149 150 loader = new BrokenDexLoader(ClassLoader.getSystemClassLoader()); 151 loader.findBrokenClass(); 152 System.out.println("ERROR: Inaccessible was accessible"); 153 } catch (InvocationTargetException ite) { 154 Throwable cause = ite.getCause(); 155 if (cause instanceof NullPointerException) { 156 System.out.println("Got expected ITE/NPE"); 157 } else { 158 System.out.println("Got unexpected ITE"); 159 ite.printStackTrace(System.out); 160 } 161 } 162 } 163 } 164