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