1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import dalvik.system.DexFile;
18 import dalvik.system.VMRuntime;
19 import java.io.File;
20 import java.io.IOException;
21 import java.lang.reflect.Array;
22 import java.lang.reflect.InvocationHandler;
23 import java.lang.reflect.Method;
24 import java.math.BigInteger;
25 import java.util.concurrent.CyclicBarrier;
26 
27 // This class helps testing that we don't mark `InheritsBigInteger` as initialized,
28 // given we do not expect `BigInteger` to be initialized in the boot image.
29 class InheritsBigInteger extends BigInteger {
InheritsBigInteger(String value)30   InheritsBigInteger(String value) {
31     super(value);
32   }
33 }
34 
35 class SuperClass {}
36 
37 class ClassWithStatics extends SuperClass {
38   public static final String STATIC_STRING = "foo";
39   public static final int STATIC_INT = 42;
40 }
41 
42 class ClassWithStaticType {
43   public static final Class<?> STATIC_TYPE = Object.class;
44 }
45 
46 // Add an interface for testing generating classes and interfaces.
47 interface Itf {
someMethod()48   public int someMethod();
someDefaultMethod()49   public default int someDefaultMethod() { return 42; }
50 }
51 
52 // Add a second interface with many methods to force a conflict in the IMT. We want a second
53 // interface to make sure `Itf` gets entries with the imt_unimplemented_method runtime method.
54 interface Itf2 {
defaultMethod1()55   default int defaultMethod1() { return 1; }
defaultMethod2()56   default int defaultMethod2() { return 2; }
defaultMethod3()57   default int defaultMethod3() { return 3; }
defaultMethod4()58   default int defaultMethod4() { return 4; }
defaultMethod5()59   default int defaultMethod5() { return 5; }
defaultMethod6()60   default int defaultMethod6() { return 6; }
defaultMethod7()61   default int defaultMethod7() { return 7; }
defaultMethod8()62   default int defaultMethod8() { return 8; }
defaultMethod9()63   default int defaultMethod9() { return 9; }
defaultMethod10()64   default int defaultMethod10() { return 10; }
defaultMethod11()65   default int defaultMethod11() { return 11; }
defaultMethod12()66   default int defaultMethod12() { return 12; }
defaultMethod13()67   default int defaultMethod13() { return 13; }
defaultMethod14()68   default int defaultMethod14() { return 14; }
defaultMethod15()69   default int defaultMethod15() { return 15; }
defaultMethod16()70   default int defaultMethod16() { return 16; }
defaultMethod17()71   default int defaultMethod17() { return 17; }
defaultMethod18()72   default int defaultMethod18() { return 18; }
defaultMethod19()73   default int defaultMethod19() { return 19; }
defaultMethod20()74   default int defaultMethod20() { return 20; }
defaultMethod21()75   default int defaultMethod21() { return 21; }
defaultMethod22()76   default int defaultMethod22() { return 22; }
defaultMethod23()77   default int defaultMethod23() { return 23; }
defaultMethod24()78   default int defaultMethod24() { return 24; }
defaultMethod25()79   default int defaultMethod25() { return 25; }
defaultMethod26()80   default int defaultMethod26() { return 26; }
defaultMethod27()81   default int defaultMethod27() { return 27; }
defaultMethod28()82   default int defaultMethod28() { return 28; }
defaultMethod29()83   default int defaultMethod29() { return 29; }
defaultMethod30()84   default int defaultMethod30() { return 30; }
defaultMethod31()85   default int defaultMethod31() { return 31; }
defaultMethod32()86   default int defaultMethod32() { return 32; }
defaultMethod33()87   default int defaultMethod33() { return 33; }
defaultMethod34()88   default int defaultMethod34() { return 34; }
defaultMethod35()89   default int defaultMethod35() { return 35; }
defaultMethod36()90   default int defaultMethod36() { return 36; }
defaultMethod37()91   default int defaultMethod37() { return 37; }
defaultMethod38()92   default int defaultMethod38() { return 38; }
defaultMethod39()93   default int defaultMethod39() { return 39; }
defaultMethod40()94   default int defaultMethod40() { return 40; }
defaultMethod41()95   default int defaultMethod41() { return 41; }
defaultMethod42()96   default int defaultMethod42() { return 42; }
defaultMethod43()97   default int defaultMethod43() { return 43; }
defaultMethod44()98   default int defaultMethod44() { return 44; }
defaultMethod45()99   default int defaultMethod45() { return 45; }
defaultMethod46()100   default int defaultMethod46() { return 46; }
defaultMethod47()101   default int defaultMethod47() { return 47; }
defaultMethod48()102   default int defaultMethod48() { return 48; }
defaultMethod49()103   default int defaultMethod49() { return 49; }
defaultMethod50()104   default int defaultMethod50() { return 50; }
defaultMethod51()105   default int defaultMethod51() { return 51; }
106 }
107 
108 class Itf2Impl implements Itf2 {
109 }
110 
111 class ClassWithDefaultConflict implements IfaceWithSayHi, IfaceWithSayHiAtRuntime {
112 }
113 
114 public class Main implements Itf {
115   static String myString = "MyString";
116 
117   static class MyThread extends Thread {
118     CyclicBarrier barrier;
119 
MyThread(CyclicBarrier barrier)120     public MyThread(CyclicBarrier barrier) {
121       this.barrier = barrier;
122     }
run()123     public void run() {
124       try {
125         synchronized (Main.myString) {
126           barrier.await();
127           barrier.reset();
128           // Infinite wait.
129           barrier.await();
130         }
131       } catch (Exception e) {
132         throw new Error(e);
133       }
134     }
135   }
136 
main(String[] args)137   public static void main(String[] args) throws Exception {
138     System.loadLibrary(args[0]);
139 
140     // Register the dex file so that the runtime can pick up which
141     // dex file to compile for the image.
142     File file = null;
143     try {
144       file = createTempFile();
145       String codePath = DEX_LOCATION + "/845-data-image.jar";
146       VMRuntime.registerAppInfo(
147           "test.app",
148           file.getPath(),
149           file.getPath(),
150           new String[] {codePath},
151           VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
152     } finally {
153       if (file != null) {
154         file.delete();
155       }
156     }
157 
158     if (!hasOatFile() || !hasImage()) {
159       // We only generate an app image if there is at least a vdex file and a boot image.
160       return;
161     }
162 
163     if (args.length == 2 && "--second-run".equals(args[1])) {
164       DexFile.OptimizationInfo info = VMRuntime.getBaseApkOptimizationInfo();
165       if (!info.isOptimized() && !isInImageSpace(Main.class)) {
166         throw new Error("Expected image to be loaded");
167       }
168     }
169 
170     runClassTests();
171 
172     // Test that we emit an empty lock word. If we are not, then this synchronized call here would
173     // block on a run with the runtime image.
174     synchronized (myString) {
175     }
176 
177     // Create a thread that makes sure `myString` is locked while the main thread is generating
178     // the runtime image.
179     CyclicBarrier barrier = new CyclicBarrier(2);
180     Thread t = new MyThread(barrier);
181     t.setDaemon(true);
182     t.start();
183     barrier.await();
184 
185     VMRuntime runtime = VMRuntime.getRuntime();
186     runtime.notifyStartupCompleted();
187 
188     String filter = getCompilerFilter(Main.class);
189     if ("speed-profile".equals(filter) || "speed".equals(filter)) {
190       // We only generate an app image for filters that don't compile.
191       return;
192     }
193 
194     String instructionSet = VMRuntime.getCurrentInstructionSet();
195     // Wait for the file to be generated.
196     File image = new File(DEX_LOCATION + "/" + instructionSet + "/845-data-image.art");
197     while (!image.exists()) {
198       Thread.yield();
199     }
200   }
201 
202   static class MyProxy implements InvocationHandler {
203 
204     private Object obj;
205 
newInstance(Object obj)206     public static Object newInstance(Object obj) {
207         return java.lang.reflect.Proxy.newProxyInstance(
208             obj.getClass().getClassLoader(),
209             obj.getClass().getInterfaces(),
210             new MyProxy(obj));
211     }
212 
MyProxy(Object obj)213     private MyProxy(Object obj) {
214         this.obj = obj;
215     }
216 
invoke(Object proxy, Method m, Object[] args)217     public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
218       return m.invoke(obj, args);
219     }
220   }
221 
222   public static Itf itf = new Main();
223   public static Itf2 itf2 = new Itf2Impl();
224   public static ClassWithStatics statics = new ClassWithStatics();
225   public static ClassWithStaticType staticType = new ClassWithStaticType();
226   public static ClassWithDefaultConflict defaultConflict = new ClassWithDefaultConflict();
227 
runClassTests()228   public static void runClassTests() {
229     // Test Class.getName, app images expect all strings to have hash codes.
230     assertEquals("Main", Main.class.getName());
231 
232     // Basic tests for invokes with a copied method.
233     assertEquals(3, new Main().someMethod());
234     assertEquals(42, new Main().someDefaultMethod());
235 
236     assertEquals(3, itf.someMethod());
237     assertEquals(42, itf.someDefaultMethod());
238 
239     // Test with a proxy class.
240     Itf foo = (Itf) MyProxy.newInstance(new Main());
241     assertEquals(3, foo.someMethod());
242     assertEquals(42, foo.someDefaultMethod());
243 
244     // Test with array classes.
245     assertEquals("[LMain;", Main[].class.getName());
246     assertEquals("[[LMain;", Main[][].class.getName());
247 
248     assertEquals("[LMain;", new Main[4].getClass().getName());
249     assertEquals("[[LMain;", new Main[1][2].getClass().getName());
250 
251     Main array[] = new Main[] { new Main() };
252     assertEquals("[LMain;", array.getClass().getName());
253 
254     assertEquals(Object[][][][].class, Array.newInstance(Object.class, 0, 0, 0, 0).getClass());
255     assertEquals("int", int.class.getName());
256     assertEquals("[I", int[].class.getName());
257 
258     assertEquals("foo", statics.STATIC_STRING);
259     assertEquals(42, statics.STATIC_INT);
260 
261     assertEquals(Object.class, staticType.STATIC_TYPE);
262 
263     // Call all interface methods to trigger the creation of a imt conflict method.
264     itf2.defaultMethod1();
265     itf2.defaultMethod2();
266     itf2.defaultMethod3();
267     itf2.defaultMethod4();
268     itf2.defaultMethod5();
269     itf2.defaultMethod6();
270     itf2.defaultMethod7();
271     itf2.defaultMethod8();
272     itf2.defaultMethod9();
273     itf2.defaultMethod10();
274     itf2.defaultMethod11();
275     itf2.defaultMethod12();
276     itf2.defaultMethod13();
277     itf2.defaultMethod14();
278     itf2.defaultMethod15();
279     itf2.defaultMethod16();
280     itf2.defaultMethod17();
281     itf2.defaultMethod18();
282     itf2.defaultMethod19();
283     itf2.defaultMethod20();
284     itf2.defaultMethod21();
285     itf2.defaultMethod22();
286     itf2.defaultMethod23();
287     itf2.defaultMethod24();
288     itf2.defaultMethod25();
289     itf2.defaultMethod26();
290     itf2.defaultMethod27();
291     itf2.defaultMethod28();
292     itf2.defaultMethod29();
293     itf2.defaultMethod30();
294     itf2.defaultMethod31();
295     itf2.defaultMethod32();
296     itf2.defaultMethod33();
297     itf2.defaultMethod34();
298     itf2.defaultMethod35();
299     itf2.defaultMethod36();
300     itf2.defaultMethod37();
301     itf2.defaultMethod38();
302     itf2.defaultMethod39();
303     itf2.defaultMethod40();
304     itf2.defaultMethod41();
305     itf2.defaultMethod42();
306     itf2.defaultMethod43();
307     itf2.defaultMethod44();
308     itf2.defaultMethod45();
309     itf2.defaultMethod46();
310     itf2.defaultMethod47();
311     itf2.defaultMethod48();
312     itf2.defaultMethod49();
313     itf2.defaultMethod50();
314     itf2.defaultMethod51();
315 
316     InheritsBigInteger bigInteger = new InheritsBigInteger("42");
317     assertEquals("42", bigInteger.toString());
318   }
319 
assertEquals(int expected, int actual)320   private static void assertEquals(int expected, int actual) {
321     if (expected != actual) {
322       throw new Error("Expected " + expected + ", got " + actual);
323     }
324   }
325 
assertEquals(Object expected, Object actual)326   private static void assertEquals(Object expected, Object actual) {
327     if (!expected.equals(actual)) {
328       throw new Error("Expected \"" + expected + "\", got \"" + actual + "\"");
329     }
330   }
331 
someMethod()332   public int someMethod() {
333     return 3;
334   }
335 
hasOatFile()336   private static native boolean hasOatFile();
hasImage()337   private static native boolean hasImage();
getCompilerFilter(Class<?> cls)338   private static native String getCompilerFilter(Class<?> cls);
isInImageSpace(Class<?> cls)339   private static native boolean isInImageSpace(Class<?> cls);
340 
341   private static final String TEMP_FILE_NAME_PREFIX = "temp";
342   private static final String TEMP_FILE_NAME_SUFFIX = "-file";
343   private static final String DEX_LOCATION = System.getenv("DEX_LOCATION");
344 
createTempFile()345   private static File createTempFile() throws Exception {
346     try {
347       return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
348     } catch (IOException e) {
349       System.setProperty("java.io.tmpdir", "/data/local/tmp");
350       try {
351         return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
352       } catch (IOException e2) {
353         System.setProperty("java.io.tmpdir", "/sdcard");
354         return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
355       }
356     }
357   }
358 }
359