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