354 lines
11 KiB
Java
354 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2022 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 dalvik.system.DexFile;
|
|
import dalvik.system.VMRuntime;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.lang.reflect.Array;
|
|
import java.lang.reflect.InvocationHandler;
|
|
import java.lang.reflect.Method;
|
|
import java.math.BigInteger;
|
|
import java.util.concurrent.CyclicBarrier;
|
|
|
|
// This class helps testing that we don't mark `InheritsBigInteger` as initialized,
|
|
// given we do not expect `BigInteger` to be initialized in the boot image.
|
|
class InheritsBigInteger extends BigInteger {
|
|
InheritsBigInteger(String value) {
|
|
super(value);
|
|
}
|
|
}
|
|
|
|
class SuperClass {}
|
|
|
|
class ClassWithStatics extends SuperClass {
|
|
public static final String STATIC_STRING = "foo";
|
|
public static final int STATIC_INT = 42;
|
|
}
|
|
|
|
class ClassWithStaticType {
|
|
public static final Class<?> STATIC_TYPE = Object.class;
|
|
}
|
|
|
|
// Add an interface for testing generating classes and interfaces.
|
|
interface Itf {
|
|
public int someMethod();
|
|
public default int someDefaultMethod() { return 42; }
|
|
}
|
|
|
|
// Add a second interface with many methods to force a conflict in the IMT. We want a second
|
|
// interface to make sure `Itf` gets entries with the imt_unimplemented_method runtime method.
|
|
interface Itf2 {
|
|
default int defaultMethod1() { return 1; }
|
|
default int defaultMethod2() { return 2; }
|
|
default int defaultMethod3() { return 3; }
|
|
default int defaultMethod4() { return 4; }
|
|
default int defaultMethod5() { return 5; }
|
|
default int defaultMethod6() { return 6; }
|
|
default int defaultMethod7() { return 7; }
|
|
default int defaultMethod8() { return 8; }
|
|
default int defaultMethod9() { return 9; }
|
|
default int defaultMethod10() { return 10; }
|
|
default int defaultMethod11() { return 11; }
|
|
default int defaultMethod12() { return 12; }
|
|
default int defaultMethod13() { return 13; }
|
|
default int defaultMethod14() { return 14; }
|
|
default int defaultMethod15() { return 15; }
|
|
default int defaultMethod16() { return 16; }
|
|
default int defaultMethod17() { return 17; }
|
|
default int defaultMethod18() { return 18; }
|
|
default int defaultMethod19() { return 19; }
|
|
default int defaultMethod20() { return 20; }
|
|
default int defaultMethod21() { return 21; }
|
|
default int defaultMethod22() { return 22; }
|
|
default int defaultMethod23() { return 23; }
|
|
default int defaultMethod24() { return 24; }
|
|
default int defaultMethod25() { return 25; }
|
|
default int defaultMethod26() { return 26; }
|
|
default int defaultMethod27() { return 27; }
|
|
default int defaultMethod28() { return 28; }
|
|
default int defaultMethod29() { return 29; }
|
|
default int defaultMethod30() { return 30; }
|
|
default int defaultMethod31() { return 31; }
|
|
default int defaultMethod32() { return 32; }
|
|
default int defaultMethod33() { return 33; }
|
|
default int defaultMethod34() { return 34; }
|
|
default int defaultMethod35() { return 35; }
|
|
default int defaultMethod36() { return 36; }
|
|
default int defaultMethod37() { return 37; }
|
|
default int defaultMethod38() { return 38; }
|
|
default int defaultMethod39() { return 39; }
|
|
default int defaultMethod40() { return 40; }
|
|
default int defaultMethod41() { return 41; }
|
|
default int defaultMethod42() { return 42; }
|
|
default int defaultMethod43() { return 43; }
|
|
default int defaultMethod44() { return 44; }
|
|
default int defaultMethod45() { return 45; }
|
|
default int defaultMethod46() { return 46; }
|
|
default int defaultMethod47() { return 47; }
|
|
default int defaultMethod48() { return 48; }
|
|
default int defaultMethod49() { return 49; }
|
|
default int defaultMethod50() { return 50; }
|
|
default int defaultMethod51() { return 51; }
|
|
}
|
|
|
|
class Itf2Impl implements Itf2 {
|
|
}
|
|
|
|
public class Main implements Itf {
|
|
static String myString = "MyString";
|
|
|
|
static class MyThread extends Thread {
|
|
CyclicBarrier barrier;
|
|
|
|
public MyThread(CyclicBarrier barrier) {
|
|
this.barrier = barrier;
|
|
}
|
|
public void run() {
|
|
try {
|
|
synchronized (Main.myString) {
|
|
barrier.await();
|
|
barrier.reset();
|
|
// Infinite wait.
|
|
barrier.await();
|
|
}
|
|
} catch (Exception e) {
|
|
throw new Error(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
System.loadLibrary(args[0]);
|
|
|
|
// Register the dex file so that the runtime can pick up which
|
|
// dex file to compile for the image.
|
|
File file = null;
|
|
try {
|
|
file = createTempFile();
|
|
String codePath = DEX_LOCATION + "/845-data-image.jar";
|
|
VMRuntime.registerAppInfo(
|
|
"test.app",
|
|
file.getPath(),
|
|
file.getPath(),
|
|
new String[] {codePath},
|
|
VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
|
|
} finally {
|
|
if (file != null) {
|
|
file.delete();
|
|
}
|
|
}
|
|
|
|
if (!hasOatFile() || !hasImage()) {
|
|
// We only generate an app image if there is at least a vdex file and a boot image.
|
|
return;
|
|
}
|
|
|
|
if (args.length == 2 && "--second-run".equals(args[1])) {
|
|
DexFile.OptimizationInfo info = VMRuntime.getBaseApkOptimizationInfo();
|
|
if (!info.isOptimized()) {
|
|
throw new Error("Expected image to be loaded");
|
|
}
|
|
}
|
|
|
|
runClassTests();
|
|
|
|
// Test that we emit an empty lock word. If we are not, then this synchronized call here would
|
|
// block on a run with the runtime image.
|
|
synchronized (myString) {
|
|
}
|
|
|
|
// Create a thread that makes sure `myString` is locked while the main thread is generating
|
|
// the runtime image.
|
|
CyclicBarrier barrier = new CyclicBarrier(2);
|
|
Thread t = new MyThread(barrier);
|
|
t.setDaemon(true);
|
|
t.start();
|
|
barrier.await();
|
|
|
|
VMRuntime runtime = VMRuntime.getRuntime();
|
|
runtime.notifyStartupCompleted();
|
|
|
|
String filter = getCompilerFilter(Main.class);
|
|
if ("speed-profile".equals(filter) || "speed".equals(filter)) {
|
|
// We only generate an app image for filters that don't compile.
|
|
return;
|
|
}
|
|
|
|
String instructionSet = VMRuntime.getCurrentInstructionSet();
|
|
// Wait for the file to be generated.
|
|
File image = new File(DEX_LOCATION + "/" + instructionSet + "/845-data-image.art");
|
|
while (!image.exists()) {
|
|
Thread.yield();
|
|
}
|
|
}
|
|
|
|
static class MyProxy implements InvocationHandler {
|
|
|
|
private Object obj;
|
|
|
|
public static Object newInstance(Object obj) {
|
|
return java.lang.reflect.Proxy.newProxyInstance(
|
|
obj.getClass().getClassLoader(),
|
|
obj.getClass().getInterfaces(),
|
|
new MyProxy(obj));
|
|
}
|
|
|
|
private MyProxy(Object obj) {
|
|
this.obj = obj;
|
|
}
|
|
|
|
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
|
|
return m.invoke(obj, args);
|
|
}
|
|
}
|
|
|
|
public static Itf itf = new Main();
|
|
public static Itf2 itf2 = new Itf2Impl();
|
|
public static ClassWithStatics statics = new ClassWithStatics();
|
|
public static ClassWithStaticType staticType = new ClassWithStaticType();
|
|
|
|
public static void runClassTests() {
|
|
// Test Class.getName, app images expect all strings to have hash codes.
|
|
assertEquals("Main", Main.class.getName());
|
|
|
|
// Basic tests for invokes with a copied method.
|
|
assertEquals(3, new Main().someMethod());
|
|
assertEquals(42, new Main().someDefaultMethod());
|
|
|
|
assertEquals(3, itf.someMethod());
|
|
assertEquals(42, itf.someDefaultMethod());
|
|
|
|
// Test with a proxy class.
|
|
Itf foo = (Itf) MyProxy.newInstance(new Main());
|
|
assertEquals(3, foo.someMethod());
|
|
assertEquals(42, foo.someDefaultMethod());
|
|
|
|
// Test with array classes.
|
|
assertEquals("[LMain;", Main[].class.getName());
|
|
assertEquals("[[LMain;", Main[][].class.getName());
|
|
|
|
assertEquals("[LMain;", new Main[4].getClass().getName());
|
|
assertEquals("[[LMain;", new Main[1][2].getClass().getName());
|
|
|
|
Main array[] = new Main[] { new Main() };
|
|
assertEquals("[LMain;", array.getClass().getName());
|
|
|
|
assertEquals(Object[][][][].class, Array.newInstance(Object.class, 0, 0, 0, 0).getClass());
|
|
assertEquals("int", int.class.getName());
|
|
assertEquals("[I", int[].class.getName());
|
|
|
|
assertEquals("foo", statics.STATIC_STRING);
|
|
assertEquals(42, statics.STATIC_INT);
|
|
|
|
assertEquals(Object.class, staticType.STATIC_TYPE);
|
|
|
|
// Call all interface methods to trigger the creation of a imt conflict method.
|
|
itf2.defaultMethod1();
|
|
itf2.defaultMethod2();
|
|
itf2.defaultMethod3();
|
|
itf2.defaultMethod4();
|
|
itf2.defaultMethod5();
|
|
itf2.defaultMethod6();
|
|
itf2.defaultMethod7();
|
|
itf2.defaultMethod8();
|
|
itf2.defaultMethod9();
|
|
itf2.defaultMethod10();
|
|
itf2.defaultMethod11();
|
|
itf2.defaultMethod12();
|
|
itf2.defaultMethod13();
|
|
itf2.defaultMethod14();
|
|
itf2.defaultMethod15();
|
|
itf2.defaultMethod16();
|
|
itf2.defaultMethod17();
|
|
itf2.defaultMethod18();
|
|
itf2.defaultMethod19();
|
|
itf2.defaultMethod20();
|
|
itf2.defaultMethod21();
|
|
itf2.defaultMethod22();
|
|
itf2.defaultMethod23();
|
|
itf2.defaultMethod24();
|
|
itf2.defaultMethod25();
|
|
itf2.defaultMethod26();
|
|
itf2.defaultMethod27();
|
|
itf2.defaultMethod28();
|
|
itf2.defaultMethod29();
|
|
itf2.defaultMethod30();
|
|
itf2.defaultMethod31();
|
|
itf2.defaultMethod32();
|
|
itf2.defaultMethod33();
|
|
itf2.defaultMethod34();
|
|
itf2.defaultMethod35();
|
|
itf2.defaultMethod36();
|
|
itf2.defaultMethod37();
|
|
itf2.defaultMethod38();
|
|
itf2.defaultMethod39();
|
|
itf2.defaultMethod40();
|
|
itf2.defaultMethod41();
|
|
itf2.defaultMethod42();
|
|
itf2.defaultMethod43();
|
|
itf2.defaultMethod44();
|
|
itf2.defaultMethod45();
|
|
itf2.defaultMethod46();
|
|
itf2.defaultMethod47();
|
|
itf2.defaultMethod48();
|
|
itf2.defaultMethod49();
|
|
itf2.defaultMethod50();
|
|
itf2.defaultMethod51();
|
|
|
|
InheritsBigInteger bigInteger = new InheritsBigInteger("42");
|
|
assertEquals("42", bigInteger.toString());
|
|
}
|
|
|
|
private static void assertEquals(int expected, int actual) {
|
|
if (expected != actual) {
|
|
throw new Error("Expected " + expected + ", got " + actual);
|
|
}
|
|
}
|
|
|
|
private static void assertEquals(Object expected, Object actual) {
|
|
if (!expected.equals(actual)) {
|
|
throw new Error("Expected \"" + expected + "\", got \"" + actual + "\"");
|
|
}
|
|
}
|
|
|
|
public int someMethod() {
|
|
return 3;
|
|
}
|
|
|
|
private static native boolean hasOatFile();
|
|
private static native boolean hasImage();
|
|
private static native String getCompilerFilter(Class<?> cls);
|
|
|
|
private static final String TEMP_FILE_NAME_PREFIX = "temp";
|
|
private static final String TEMP_FILE_NAME_SUFFIX = "-file";
|
|
private static final String DEX_LOCATION = System.getenv("DEX_LOCATION");
|
|
|
|
private static File createTempFile() throws Exception {
|
|
try {
|
|
return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
|
|
} catch (IOException e) {
|
|
System.setProperty("java.io.tmpdir", "/data/local/tmp");
|
|
try {
|
|
return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
|
|
} catch (IOException e2) {
|
|
System.setProperty("java.io.tmpdir", "/sdcard");
|
|
return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
|
|
}
|
|
}
|
|
}
|
|
}
|