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