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 } finally { 95 if (dexFile != null) { 96 /* close the DexFile to make CloseGuard happy */ 97 Method meth = dexClass.getMethod("close", (Class[]) null); 98 meth.invoke(dexFile); 99 } 100 } 101 } catch (NoSuchMethodException nsme) { 102 throw new TestFailed(nsme); 103 } catch (InstantiationException ie) { 104 throw new TestFailed(ie); 105 } catch (IllegalAccessException iae) { 106 throw new TestFailed(iae); 107 } catch (ClassNotFoundException cnfe) { 108 throw new TestFailed(cnfe); 109 } 110 111 return null; 112 } 113 114 /** 115 * Load a class. 116 * 117 * Return null if the class's name is SUPERCLASS_NAME; 118 * otherwise invoke the super's loadClass method. 119 */ 120 public Class<?> loadClass(String name, boolean resolve) 121 throws ClassNotFoundException 122 { 123 if (SUPERCLASS_NAME.equals(name)) { 124 return null; 125 } 126 127 return super.loadClass(name, resolve); 128 } 129 130 /** 131 * Attempt to find the class with the superclass we refuse to 132 * load. This is expected to throw an 133 * InvocationTargetException, with a NullPointerException as 134 * its cause. 135 */ 136 public void findBrokenClass() 137 throws TestFailed, InvocationTargetException 138 { 139 findDexClass(CLASS_NAME); 140 } 141 } 142 143 /** 144 * Main entry point. 145 */ 146 public static void main(String[] args) 147 throws TestFailed, ClassNotFoundException { 148 /* 149 * Run test. 150 */ 151 testFailLoadAndGc(); 152 } 153 154 /** 155 * See if we can GC after a failed load. 156 */ 157 static void testFailLoadAndGc() throws TestFailed { 158 try { 159 BrokenDexLoader loader; 160 161 loader = new BrokenDexLoader(ClassLoader.getSystemClassLoader()); 162 loader.findBrokenClass(); 163 System.err.println("ERROR: Inaccessible was accessible"); 164 } catch (InvocationTargetException ite) { 165 Throwable cause = ite.getCause(); 166 if (cause instanceof NullPointerException) { 167 System.err.println("Got expected ITE/NPE"); 168 } else { 169 System.err.println("Got unexpected ITE"); 170 ite.printStackTrace(); 171 } 172 } 173 Runtime.getRuntime().gc(); 174 System.out.println("GC complete."); 175 } 176 } 177