122 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			122 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Java
		
	
	
	
| /*
 | |
|  * Copyright (C) 2018 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.ref.WeakReference;
 | |
| import java.lang.reflect.InvocationHandler;
 | |
| import java.lang.reflect.Constructor;
 | |
| import java.lang.reflect.Method;
 | |
| 
 | |
| public class Main {
 | |
|   static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/616-cha-unloading-ex.jar";
 | |
|   static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
 | |
|   static Constructor<? extends ClassLoader> sConstructor;
 | |
| 
 | |
|   private static class CHAUnloaderRetType {
 | |
|     private CHAUnloaderRetType(WeakReference<ClassLoader> cl,
 | |
|                               AbstractCHATester obj,
 | |
|                               long methodPtr) {
 | |
|       this.cl = cl;
 | |
|       this.obj = obj;
 | |
|       this.methodPtr = methodPtr;
 | |
|     }
 | |
|     public WeakReference<ClassLoader> cl;
 | |
|     public AbstractCHATester obj;
 | |
|     public long methodPtr;
 | |
|   }
 | |
| 
 | |
|   public static void main(String[] args) throws Exception {
 | |
|     System.loadLibrary(args[0]);
 | |
| 
 | |
|     Class<ClassLoader> pathClassLoader = (Class<ClassLoader>) Class.forName("dalvik.system.PathClassLoader");
 | |
|     sConstructor =
 | |
|         pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
 | |
| 
 | |
|     testUnload();
 | |
|   }
 | |
| 
 | |
|   private static void testUnload() throws Exception {
 | |
|     // Load a concrete class, then unload it. Get a deleted ArtMethod to test if it'll be inlined.
 | |
|     CHAUnloaderRetType result = doUnloadLoader();
 | |
|     WeakReference<ClassLoader> loader = result.cl;
 | |
|     long methodPtr = result.methodPtr;
 | |
|     // Check that the classloader is indeed unloaded.
 | |
|     if (loader.get() != null) {
 | |
|       throw new Error("Expected class loader to be unloaded");
 | |
|     }
 | |
| 
 | |
|     // Reuse the linear alloc used by the unloaded class loader.
 | |
|     reuseArenaOfMethod(methodPtr);
 | |
| 
 | |
|     // Try to JIT-compile under dangerous conditions.
 | |
|     ensureJitCompiled(Main.class, "targetMethodForJit");
 | |
|     System.out.println("Done");
 | |
|   }
 | |
| 
 | |
|   private static void doUnloading() {
 | |
|     // Do multiple GCs to prevent rare flakiness if some other thread is keeping the
 | |
|     // classloader live.
 | |
|     for (int i = 0; i < 5; ++i) {
 | |
|        Runtime.getRuntime().gc();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   private static CHAUnloaderRetType setupLoader()
 | |
|       throws Exception {
 | |
|     ClassLoader loader = sConstructor.newInstance(
 | |
|         DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
 | |
|     Class<?> concreteCHATester = loader.loadClass("ConcreteCHATester");
 | |
| 
 | |
|     // Preemptively compile methods to prevent delayed JIT tasks from blocking the unloading.
 | |
|     ensureJitCompiled(concreteCHATester, "<init>");
 | |
|     ensureJitCompiled(concreteCHATester, "lonelyMethod");
 | |
| 
 | |
|     Object obj = concreteCHATester.newInstance();
 | |
|     Method lonelyMethod = concreteCHATester.getDeclaredMethod("lonelyMethod");
 | |
| 
 | |
|     // Get a pointer to a region that shall be not used after the unloading.
 | |
|     long artMethod = getArtMethod(lonelyMethod);
 | |
| 
 | |
|     AbstractCHATester ret = null;
 | |
|     return new CHAUnloaderRetType(new WeakReference(loader), ret, artMethod);
 | |
|   }
 | |
| 
 | |
|   private static CHAUnloaderRetType targetMethodForJit(int mode)
 | |
|       throws Exception {
 | |
|     CHAUnloaderRetType ret = new CHAUnloaderRetType(null, null, 0);
 | |
|     if (mode == 0) {
 | |
|       ret = setupLoader();
 | |
|     } else if (mode == 1) {
 | |
|       // This branch is not supposed to be executed. It shall trigger "lonelyMethod" inlining
 | |
|       // during jit compilation of "targetMethodForJit".
 | |
|       ret = setupLoader();
 | |
|       AbstractCHATester obj = ret.obj;
 | |
|       obj.lonelyMethod();
 | |
|     }
 | |
|     return ret;
 | |
|   }
 | |
| 
 | |
|   private static CHAUnloaderRetType doUnloadLoader()
 | |
|       throws Exception {
 | |
|     CHAUnloaderRetType result = targetMethodForJit(0);
 | |
|     doUnloading();
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   private static native void ensureJitCompiled(Class<?> itf, String method_name);
 | |
|   private static native long getArtMethod(Object javaMethod);
 | |
|   private static native void reuseArenaOfMethod(long artMethod);
 | |
| }
 |