/*
* 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);
}