summaryrefslogtreecommitdiff
path: root/test/845-data-image
diff options
context:
space:
mode:
Diffstat (limited to 'test/845-data-image')
-rw-r--r--test/845-data-image/Android.bp40
-rw-r--r--test/845-data-image/expected-stderr.txt0
-rw-r--r--test/845-data-image/expected-stdout.txt4
-rw-r--r--test/845-data-image/info.txt1
-rw-r--r--test/845-data-image/run.py28
-rw-r--r--test/845-data-image/src-art/Main.java353
6 files changed, 426 insertions, 0 deletions
diff --git a/test/845-data-image/Android.bp b/test/845-data-image/Android.bp
new file mode 100644
index 0000000000..2daae4b31f
--- /dev/null
+++ b/test/845-data-image/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `845-data-image`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-845-data-image",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src-art/**/*.java"],
+ data: [
+ ":art-run-test-845-data-image-expected-stdout",
+ ":art-run-test-845-data-image-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-845-data-image-expected-stdout",
+ out: ["art-run-test-845-data-image-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-845-data-image-expected-stderr",
+ out: ["art-run-test-845-data-image-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/845-data-image/expected-stderr.txt b/test/845-data-image/expected-stderr.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/845-data-image/expected-stderr.txt
diff --git a/test/845-data-image/expected-stdout.txt b/test/845-data-image/expected-stdout.txt
new file mode 100644
index 0000000000..bebd8c09fd
--- /dev/null
+++ b/test/845-data-image/expected-stdout.txt
@@ -0,0 +1,4 @@
+JNI_OnLoad called
+JNI_OnLoad called
+JNI_OnLoad called
+JNI_OnLoad called
diff --git a/test/845-data-image/info.txt b/test/845-data-image/info.txt
new file mode 100644
index 0000000000..dfcd0dd126
--- /dev/null
+++ b/test/845-data-image/info.txt
@@ -0,0 +1 @@
+Test the generation of app image at runtime.
diff --git a/test/845-data-image/run.py b/test/845-data-image/run.py
new file mode 100644
index 0000000000..7e0dbf77c6
--- /dev/null
+++ b/test/845-data-image/run.py
@@ -0,0 +1,28 @@
+# 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 sys
+
+# We run the tests by disabling compilation with app image and forcing
+# relocation for better testing.
+# Run the test twice: one run for generating the image, a second run for using
+# the image.
+def run(ctx, args):
+ ctx.default_run(args, Xcompiler_option=["--compact-dex-level=fast"], app_image=False, relocate=True)
+ # Pass another argument to let the test know it should now expect an image.
+ ctx.default_run(args, Xcompiler_option=["--compact-dex-level=fast"], app_image=False, relocate=True, test_args=["--second-run"])
+ # Repeat the test with a different compact dex level, to make sure we don't
+ # pick up the existing image.
+ ctx.default_run(args, Xcompiler_option=["--compact-dex-level=none"], app_image=False, relocate=True)
+ ctx.default_run(args, Xcompiler_option=["--compact-dex-level=none"], app_image=False, relocate=True, test_args=["--second-run"])
diff --git a/test/845-data-image/src-art/Main.java b/test/845-data-image/src-art/Main.java
new file mode 100644
index 0000000000..e74a6d69a0
--- /dev/null
+++ b/test/845-data-image/src-art/Main.java
@@ -0,0 +1,353 @@
+/*
+ * 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);
+ }
+ }
+ }
+}