| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.InvocationTargetException; |
| |
| /** |
| * Class loader test. |
| */ |
| public class Main { |
| /** |
| * Thrown when an unexpected Exception is caught internally. |
| */ |
| static class TestFailed extends Exception { |
| public TestFailed(Throwable cause) { |
| super(cause); |
| } |
| } |
| |
| /** |
| * A class loader which loads classes from the dex file |
| * "test.jar". However, it will return null when asked to load the |
| * class InaccessibleSuper. |
| * |
| * When testing code calls BrokenDexLoader's findBrokenClass(), |
| * a BrokenDexLoader will be the defining loader for the class |
| * Inaccessible. The VM will call the defining loader for |
| * "InaccessibleSuper", which will return null, which the VM |
| * should be able to deal with gracefully. |
| * |
| * Note that this depends heavily on the Dalvik test harness. |
| */ |
| static class BrokenDexLoader extends ClassLoader { |
| |
| /** We return null when asked to load InaccessibleSuper. */ |
| private static class InaccessibleSuper {} |
| private static class Inaccessible extends InaccessibleSuper {} |
| |
| private static final String SUPERCLASS_NAME = |
| "Main$BrokenDexLoader$InaccessibleSuper"; |
| private static final String CLASS_NAME = |
| "Main$BrokenDexLoader$Inaccessible"; |
| |
| private static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/087-gc-after-link.jar"; |
| |
| public BrokenDexLoader(ClassLoader parent) { |
| super(parent); |
| } |
| |
| /** |
| * Finds the class with the specified binary name, from DEX_FILE. |
| * |
| * If we don't find a match, we throw an exception. |
| */ |
| private Class<?> findDexClass(String name) |
| throws TestFailed, InvocationTargetException |
| { |
| Object dexFile = null; |
| Class<?> dexClass = null; |
| |
| try { |
| try { |
| /* |
| * Find the DexFile class, and construct a DexFile object |
| * through reflection, then call loadClass on it. |
| */ |
| dexClass = ClassLoader.getSystemClassLoader(). |
| loadClass("dalvik.system.DexFile"); |
| Constructor<?> ctor = dexClass.getConstructor(String.class); |
| dexFile = ctor.newInstance(DEX_FILE); |
| Method meth = dexClass.getMethod("loadClass", String.class, ClassLoader.class); |
| /* |
| * Invoking loadClass on CLASS_NAME is expected to |
| * throw an InvocationTargetException. Anything else |
| * is an error we can't recover from. |
| */ |
| meth.invoke(dexFile, name, this); |
| System.out.println("Unreachable"); |
| } finally { |
| if (dexFile != null) { |
| /* close the DexFile to make CloseGuard happy */ |
| Method meth = dexClass.getMethod("close"); |
| meth.invoke(dexFile); |
| } |
| } |
| } catch (NoSuchMethodException nsme) { |
| throw new TestFailed(nsme); |
| } catch (InstantiationException ie) { |
| throw new TestFailed(ie); |
| } catch (IllegalAccessException iae) { |
| throw new TestFailed(iae); |
| } catch (ClassNotFoundException cnfe) { |
| throw new TestFailed(cnfe); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Load a class. |
| * |
| * Return null if the class's name is SUPERCLASS_NAME; |
| * otherwise invoke the super's loadClass method. |
| */ |
| public Class<?> loadClass(String name, boolean resolve) |
| throws ClassNotFoundException |
| { |
| if (SUPERCLASS_NAME.equals(name)) { |
| return null; |
| } |
| |
| return super.loadClass(name, resolve); |
| } |
| |
| /** |
| * Attempt to find the class with the superclass we refuse to |
| * load. This is expected to throw an |
| * InvocationTargetException, with a NullPointerException as |
| * its cause. |
| */ |
| public void findBrokenClass() |
| throws TestFailed, InvocationTargetException |
| { |
| findDexClass(CLASS_NAME); |
| } |
| } |
| |
| /** |
| * Main entry point. |
| */ |
| public static void main(String[] args) |
| throws TestFailed, ClassNotFoundException { |
| /* |
| * Run test. |
| */ |
| testFailLoadAndGc(); |
| } |
| |
| /** |
| * See if we can GC after a failed load. |
| */ |
| static void testFailLoadAndGc() throws TestFailed { |
| processFailLoadAndGc(); |
| Runtime.getRuntime().gc(); |
| System.out.println("GC complete."); |
| } |
| |
| private static void processFailLoadAndGc() throws TestFailed { |
| try { |
| BrokenDexLoader loader; |
| |
| loader = new BrokenDexLoader(ClassLoader.getSystemClassLoader()); |
| loader.findBrokenClass(); |
| System.out.println("ERROR: Inaccessible was accessible"); |
| } catch (InvocationTargetException ite) { |
| Throwable cause = ite.getCause(); |
| if (cause instanceof NullPointerException) { |
| System.out.println("Got expected ITE/NPE"); |
| } else { |
| System.out.println("Got unexpected ITE"); |
| ite.printStackTrace(System.out); |
| } |
| } |
| } |
| } |